diff options
Diffstat (limited to 'src')
450 files changed, 15740 insertions, 11704 deletions
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h index 03f8e2d71a..734e779c70 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h @@ -359,6 +359,12 @@ public: } } + void mul32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + mul32(dataTempRegister, dest); + } + void neg32(RegisterID srcDest) { m_assembler.subu(srcDest, MIPSRegisters::zero, srcDest); @@ -420,6 +426,12 @@ public: store32(dataTempRegister, dest.m_ptr); } + void or32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + or32(dataTempRegister, dest); + } + void rshift32(RegisterID shiftAmount, RegisterID dest) { m_assembler.srav(dest, dest, shiftAmount); @@ -615,6 +627,12 @@ public: m_assembler.xorInsn(dest, src, immTempRegister); } + void xor32(Address src, RegisterID dest) + { + load32(src, dataTempRegister); + xor32(dataTempRegister, dest); + } + void sqrtDouble(FPRegisterID src, FPRegisterID dst) { m_assembler.sqrtd(dst, src); @@ -2519,6 +2537,18 @@ public: m_assembler.cvtdw(dest, fpTempRegister); } + void convertUInt32ToDouble(RegisterID src, FPRegisterID dest, RegisterID scratch) + { + m_assembler.mtc1(src, fpTempRegister); + m_assembler.bltz(src, 2); + m_assembler.cvtdw(dest, fpTempRegister); + m_assembler.beq(MIPSRegisters::zero, MIPSRegisters::zero, 4); + m_assembler.lui(scratch, 0x4f80); + m_assembler.mtc1(scratch, fpTempRegister); + m_assembler.cvtds(fpTempRegister, fpTempRegister); + m_assembler.addd(dest, dest, fpTempRegister); + } + void convertFloatToDouble(FPRegisterID src, FPRegisterID dst) { m_assembler.cvtds(dst, src); @@ -2761,7 +2791,7 @@ public: return CodeLocationLabel(); } - static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue) + static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel, Address, void*) { UNREACHABLE_FOR_PLATFORM(); } diff --git a/src/3rdparty/masm/disassembler/Mips32Disassembler.cpp b/src/3rdparty/masm/disassembler/Mips32Disassembler.cpp new file mode 100644 index 0000000000..af0a73b2cb --- /dev/null +++ b/src/3rdparty/masm/disassembler/Mips32Disassembler.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 Cisco Systems, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CISCO SYSTEMS, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Disassembler.h" + +#if USE(MIPS32_DISASSEMBLER) + +#include "mips32/Mips32Opcode.h" +#include "MacroAssemblerCodeRef.h" + +namespace JSC { + +bool tryToDisassemble(const MacroAssemblerCodePtr& codePtr, size_t size, const char* prefix, PrintStream& out) +{ + Mips32Opcode mipsOpcode; + + uint32_t* currentPC = reinterpret_cast<uint32_t*>(reinterpret_cast<uintptr_t>(codePtr.executableAddress()) & ~3); + uint32_t* endPC = currentPC + (size / sizeof(uint32_t)); + + while (currentPC < endPC) { + char pcString[12]; + snprintf(pcString, sizeof(pcString), "0x%x", reinterpret_cast<unsigned>(currentPC)); + out.printf("%s%10s: %s\n", prefix, pcString, mipsOpcode.disassemble(currentPC)); + currentPC++; + } + + return true; +} + +} // namespace JSC + +#endif // USE(MIPS32_DISASSEMBLER) + diff --git a/src/3rdparty/masm/disassembler/mips32/Mips32Opcode.cpp b/src/3rdparty/masm/disassembler/mips32/Mips32Opcode.cpp new file mode 100644 index 0000000000..164217eb55 --- /dev/null +++ b/src/3rdparty/masm/disassembler/mips32/Mips32Opcode.cpp @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2015 Cisco Systems, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CISCO SYSTEMS, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(MIPS32_DISASSEMBLER) + +#include "Mips32Opcode.h" + +#include <stdio.h> + +#define OPCODE_FMT "%s\t" +#define COP1_OPCODE_FMT "%s.%s\t" +#define FORMAT_INSTR(_format, ...) \ + snprintf(m_formatBuffer, bufferSize - 1, _format, ##__VA_ARGS__) + +const char *Mips32Opcode::registerName(uint8_t r) +{ + static const char *gpRegisters[] = { + "zero", "AT", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "kt0", "kt1", "gp", "sp", "s8", "ra" + }; + + return (r < sizeof(gpRegisters)) ? gpRegisters[r] : "invalid"; +} + +const char *Mips32Opcode::fpRegisterName(uint8_t r) +{ + static const char *fpRegisters[] = { + "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", + "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", + "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", + "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31" + }; + + return (r < sizeof(fpRegisters)) ? fpRegisters[r] : "invalid"; +} + +void Mips32Opcode::formatSpecialEncodingOpcode(uint8_t op1, uint8_t op2, uint8_t dest, uint8_t shift, uint8_t function) +{ + const char *opcode; + OpcodePrintFormat format = Unknown; + switch (function) { + case 0x00: + format = RdRtSa; + opcode = "sll"; + break; + case 0x02: + format = RdRtSa; + opcode = "srl"; + break; + case 0x03: + format = RdRtSa; + opcode = "sra"; + break; + case 0x04: + format = RdRtRs; + opcode = "sllv"; + break; + case 0x06: + format = RdRtRs; + opcode = "srlv"; + break; + case 0x07: + format = RdRtRs; + opcode = "srav"; + break; + case 0x08: + format = Rs; + opcode = "jr"; + break; + case 0x09: + format = (dest != 0x1f) ? RdRs : Rs; + opcode = "jalr"; + break; + case 0x10: + format = Rd; + opcode = "mfhi"; + break; + case 0x11: + format = Rs; + opcode = "mthi"; + break; + case 0x12: + format = Rd; + opcode = "mflo"; + break; + case 0x13: + format = Rs; + opcode = "mtlo"; + break; + case 0x18: + format = RsRt; + opcode = "mult"; + break; + case 0x19: + format = RsRt; + opcode = "multu"; + break; + case 0x1a: + format = RsRt; + opcode = "div"; + break; + case 0x1b: + format = RsRt; + opcode = "divu"; + break; + case 0x20: + format = RdRsRt; + opcode = "add"; + break; + case 0x21: + if (op2) { + format = RdRsRt; + opcode = "addu"; + } else { + format = RdRs; + opcode = "move"; + } + break; + case 0x22: + format = RdRsRt; + opcode = "sub"; + break; + case 0x23: + format = RdRsRt; + opcode = "subu"; + break; + case 0x24: + format = RdRsRt; + opcode = "and"; + break; + case 0x25: + format = RdRsRt; + opcode = "or"; + break; + case 0x26: + format = RdRsRt; + opcode = "xor"; + break; + case 0x27: + format = RdRsRt; + opcode = "nor"; + break; + case 0x2a: + format = RdRsRt; + opcode = "slt"; + break; + case 0x2b: + format = RdRsRt; + opcode = "sltu"; + break; + } + + switch (format) { + case Rs: + FORMAT_INSTR(OPCODE_FMT "%s", opcode, registerName(op1)); + break; + case Rd: + FORMAT_INSTR(OPCODE_FMT "%s", opcode, registerName(dest)); + break; + case RdRs: + FORMAT_INSTR(OPCODE_FMT "%s, %s", opcode, registerName(dest), registerName(op1)); + break; + case RsRt: + FORMAT_INSTR(OPCODE_FMT "%s, %s", opcode, registerName(op1), registerName(op2)); + break; + case RdRtRs: + FORMAT_INSTR(OPCODE_FMT "%s, %s, %s", opcode, registerName(dest), registerName(op2), registerName(op1)); + break; + case RdRsRt: + FORMAT_INSTR(OPCODE_FMT "%s, %s, %s", opcode, registerName(dest), registerName(op1), registerName(op2)); + break; + case RdRtSa: + FORMAT_INSTR(OPCODE_FMT "%s, %s, %d", opcode, registerName(dest), registerName(op2), shift); + break; + default: + FORMAT_INSTR("unknown special encoding opcode 0x%x", function); + break; + } +} + +void Mips32Opcode::formatSpecial2EncodingOpcode(uint8_t op1, uint8_t op2, uint8_t dest, uint8_t function) +{ + if (function == 0x02) { + FORMAT_INSTR(OPCODE_FMT "%s, %s, %s", "mul", registerName(dest), registerName(op1), registerName(op2)); + return; + } + + FORMAT_INSTR("unknown special2 encoding opcode 0x%x", function); +} + +void Mips32Opcode::formatJumpEncodingOpcode(uint32_t iOp, uint32_t index, uint32_t* opcodePtr) +{ + if ((iOp != 0x02) && (iOp != 0x03)) { + FORMAT_INSTR("unknown jump encoding opcode 0x%x", iOp); + return; + } + + FORMAT_INSTR(OPCODE_FMT "0x%x", (iOp == 0x02) ? "j" : "jal", + (reinterpret_cast<unsigned>(opcodePtr+1) & 0xf0000000) | (index << 2)); +} + +void Mips32Opcode::formatREGIMMEncodingOpcode(uint8_t rs, uint8_t rt, int16_t imm, uint32_t* opcodePtr) +{ + const char *opcodes[] = { "bltz", "bgez", "bltzl", "bgezl" }; + if (rt < sizeof(opcodes)) + FORMAT_INSTR(OPCODE_FMT "%s, 0x%x", opcodes[rt], registerName(rs), reinterpret_cast<unsigned>(opcodePtr+1) + (imm << 2)); + else + FORMAT_INSTR("unknown REGIMM encoding opcode 0x%x", rt); +} + +void Mips32Opcode::formatImmediateEncodingOpcode(uint32_t iOp, uint8_t rs, uint8_t rt, int16_t imm, uint32_t* opcodePtr) +{ + const char *opcode; + OpcodePrintFormat format = Unknown; + switch (iOp) { + case 0x04: + if (!rs && !rt) { + format = Addr; + opcode = "b"; + } else { + format = RsRtAddr; + opcode = "beq"; + } + break; + case 0x05: + format = RsRtAddr; + opcode = "bne"; + break; + case 0x06: + format = RsRtAddr; + opcode = "blez"; + break; + case 0x07: + format = RsRtAddr; + opcode = "bgtz"; + break; + case 0x08: + format = RtRsImm; + opcode = "addi"; + break; + case 0x09: + if (rs) { + format = RtRsImm; + opcode = "addiu"; + } else { + format = RtUImm; + opcode = "li"; + } + break; + case 0x0a: + format = RtRsImm; + opcode = "slti"; + break; + case 0x0b: + format = RtRsImm; + opcode = "sltiu"; + break; + case 0x0c: + format = RtRsImm; + opcode = "andi"; + break; + case 0x0d: + format = RtRsImm; + opcode = "ori"; + break; + case 0x0e: + format = RtRsImm; + opcode = "xori"; + break; + case 0x0f: + format = RtUImm; + opcode = "lui"; + break; + case 0x20: + format = RtOffsetBase; + opcode = "lb"; + break; + case 0x21: + format = RtOffsetBase; + opcode = "lh"; + break; + case 0x22: + format = RtOffsetBase; + opcode = "lwl"; + break; + case 0x23: + format = RtOffsetBase; + opcode = "lw"; + break; + case 0x24: + format = RtOffsetBase; + opcode = "lbu"; + break; + case 0x25: + format = RtOffsetBase; + opcode = "lhu"; + break; + case 0x26: + format = RtOffsetBase; + opcode = "lwr"; + break; + case 0x28: + format = RtOffsetBase; + opcode = "sb"; + break; + case 0x29: + format = RtOffsetBase; + opcode = "sh"; + break; + case 0x2a: + format = RtOffsetBase; + opcode = "swl"; + break; + case 0x2b: + format = RtOffsetBase; + opcode = "sw"; + break; + case 0x2e: + format = RtOffsetBase; + opcode = "swr"; + break; + case 0x35: + format = FtOffsetBase; + opcode = "ldc1"; + break; + case 0x3d: + format = FtOffsetBase; + opcode = "sdc1"; + break; + } + + switch (format) { + case Addr: + FORMAT_INSTR(OPCODE_FMT "0x%x", opcode, reinterpret_cast<unsigned>(opcodePtr+1) + (imm << 2)); + break; + case RtUImm: + FORMAT_INSTR(OPCODE_FMT "%s, 0x%hx", opcode, registerName(rt), imm); + break; + case RtRsImm: + FORMAT_INSTR(OPCODE_FMT "%s, %s, %d", opcode, registerName(rt), registerName(rs), imm); + break; + case RsRtAddr: + FORMAT_INSTR(OPCODE_FMT "%s, %s, 0x%x", opcode, registerName(rs), registerName(rt), + reinterpret_cast<unsigned>(opcodePtr+1) + (imm << 2)); + break; + case RtOffsetBase: + FORMAT_INSTR(OPCODE_FMT "%s, %d(%s)", opcode, registerName(rt), imm, registerName(rs)); + break; + case FtOffsetBase: + FORMAT_INSTR(OPCODE_FMT "%s, %d(%s)", opcode, fpRegisterName(rt), imm, registerName(rs)); + break; + default: + FORMAT_INSTR("unknown immediate encoding opcode 0x%x", iOp); + break; + } +} + +void Mips32Opcode::formatCOP1Opcode(uint8_t fmt, uint8_t ft, uint8_t fs, uint8_t fd, uint8_t func) +{ + const char *opcode; + const char *suffix; + OpcodePrintFormat format = Unknown; + + if (fmt < 0x10) { + switch (fmt) { + case 0x00: + opcode = "mfc1"; + break; + case 0x04: + opcode = "mtc1"; + break; + default: + FORMAT_INSTR("unknown COP1 rs 0x%x", fmt); + return; + } + FORMAT_INSTR(OPCODE_FMT "%s, %s", opcode, registerName(ft), fpRegisterName(fs)); + return; + } + + switch (fmt) { + case 0x10: + suffix = "s"; + break; + case 0x11: + suffix = "d"; + break; + case 0x14: + suffix = "w"; + break; + case 0x15: + suffix = "l"; + break; + case 0x16: + suffix = "ps"; + break; + default: + FORMAT_INSTR("unknown COP1 fmt 0x%x", fmt); + return; + } + + switch (func) { + case 0x00: + format = FdFsFt; + opcode = "add"; + break; + case 0x01: + format = FdFsFt; + opcode = "sub"; + break; + case 0x02: + format = FdFsFt; + opcode = "mul"; + break; + case 0x03: + format = FdFsFt; + opcode = "div"; + break; + case 0x04: + format = FdFs; + opcode = "sqrt"; + break; + case 0x05: + format = FdFs; + opcode = "abs"; + break; + case 0x06: + format = FdFs; + opcode = "mov"; + break; + case 0x07: + format = FdFs; + opcode = "neg"; + break; + case 0x08: + format = FdFs; + opcode = "round.l"; + break; + case 0x09: + format = FdFs; + opcode = "trunc.l"; + break; + case 0x0a: + format = FdFs; + opcode = "ceil.l"; + break; + case 0x0b: + format = FdFs; + opcode = "floor.l"; + break; + case 0x0c: + format = FdFs; + opcode = "round.w"; + break; + case 0x0d: + format = FdFs; + opcode = "trunc.w"; + break; + case 0x0e: + format = FdFs; + opcode = "ceil.w"; + break; + case 0x0f: + format = FdFs; + opcode = "floor.w"; + break; + case 0x20: + format = FdFs; + opcode = "cvt.s"; + break; + case 0x21: + format = FdFs; + opcode = "cvt.d"; + break; + case 0x24: + format = FdFs; + opcode = "cvt.w"; + break; + case 0x25: + format = FdFs; + opcode = "cvt.l"; + break; + } + + switch (format) { + case FdFs: + FORMAT_INSTR(COP1_OPCODE_FMT "%s, %s", opcode, suffix, fpRegisterName(fd), fpRegisterName(fs)); + break; + case FdFsFt: + FORMAT_INSTR(COP1_OPCODE_FMT "%s, %s, %s", opcode, suffix, fpRegisterName(fd), fpRegisterName(fs), fpRegisterName(ft)); + break; + default: + FORMAT_INSTR("unknown COP1 opcode 0x%x", func); + break; + } +} + +void Mips32Opcode::formatCOP1FPCompareOpcode(uint8_t fmt, uint8_t ft, uint8_t fs, uint8_t cc, uint8_t cond) +{ + const char *suffix; + static const char *opcodes[] = { + "c.f", "c.un", "c.eq", "c.ueq", "c.olt", "c.ult", "c.ole", "c.ule", + "c.sf", "c.ngle", "c.seq", "c.ngl", "c.lt", "c.nge", "c.le", "c.ngt" + }; + ASSERT(cond < sizeof(opcdoes)); + + switch (fmt) { + case 0x10: + suffix = "s"; + break; + case 0x11: + suffix = "d"; + break; + case 0x16: + suffix = "ps"; + break; + default: + FORMAT_INSTR("unknown COP1 fmt 0x%x", fmt); + return; + } + + if (!cc) + FORMAT_INSTR(COP1_OPCODE_FMT "%s, %s", opcodes[cond], suffix, fpRegisterName(fs), fpRegisterName(ft)); + else + FORMAT_INSTR(COP1_OPCODE_FMT "%d, %s, %s", opcodes[cond], suffix, cc, fpRegisterName(fs), fpRegisterName(ft)); +} + +void Mips32Opcode::formatCOP1BCOpcode(uint8_t cc, uint8_t ndtf, int16_t offset, uint32_t* opcodePtr) +{ + static const char *opcodes[] = { "bc1f", "bc1t", "bc1fl", "bc1tl" }; + ASSERT(ndtf < sizeof(opcodes)); + + if (!cc) + FORMAT_INSTR(OPCODE_FMT "0x%x", opcodes[ndtf], reinterpret_cast<unsigned>(opcodePtr+1) + (offset << 2)); + else + FORMAT_INSTR(OPCODE_FMT "%d, 0x%x", opcodes[ndtf], cc, reinterpret_cast<unsigned>(opcodePtr+1) + (offset << 2)); +} + +const char* Mips32Opcode::disassemble(uint32_t* opcodePtr) +{ + uint32_t opcode = *opcodePtr; + uint32_t iOp = (opcode >> 26) & 0x3f; + + if (!opcode) + FORMAT_INSTR(OPCODE_FMT, "nop"); + else if (!iOp) { + uint8_t op1 = (opcode >> 21) & 0x1f; + uint8_t op2 = (opcode >> 16) & 0x1f; + uint8_t dst = (opcode >> 11) & 0x1f; + uint8_t shft = (opcode >> 6) & 0x1f; + uint8_t func = opcode & 0x3f; + formatSpecialEncodingOpcode(op1, op2, dst, shft, func); + } else if ((iOp == 0x02) || (iOp == 0x03)) { + uint32_t index = opcode & 0x3ffffff; + formatJumpEncodingOpcode(iOp, index, opcodePtr); + } else if (iOp == 0x11) { + uint8_t fmt = (opcode >> 21) & 0x1f; + if (fmt == 0x08) { + uint8_t cc = (opcode >> 18) & 0x07; + uint8_t ndtf = (opcode >> 16) & 0x03; + int16_t offset = opcode & 0xffff; + formatCOP1BCOpcode(cc, ndtf, offset, opcodePtr); + } else if ((opcode & 0xf0) == 0x30) { + uint8_t ft = (opcode >> 16) & 0x1f; + uint8_t fs = (opcode >> 11) & 0x1f; + uint8_t cc = (opcode >> 8) & 0x07; + uint8_t cond = opcode & 0x0f; + formatCOP1FPCompareOpcode(fmt, ft, fs, cc, cond); + } else { + uint8_t ft = (opcode >> 16) & 0x1f; + uint8_t fs = (opcode >> 11) & 0x1f; + uint8_t fd = (opcode >> 6) & 0x1f; + uint8_t func = opcode & 0x3f; + formatCOP1Opcode(fmt, ft, fs, fd, func); + } + } else if (iOp == 0x1c) { + uint8_t op1 = (opcode >> 21) & 0x1f; + uint8_t op2 = (opcode >> 16) & 0x1f; + uint8_t dst = (opcode >> 11) & 0x1f; + uint8_t func = opcode & 0x3f; + formatSpecial2EncodingOpcode(op1, op2, dst, func); + } else { + uint8_t rs = (opcode >> 21) & 0x1f; + uint8_t rt = (opcode >> 16) & 0x1f; + int16_t imm = opcode & 0xffff; + if (iOp == 0x01) + formatREGIMMEncodingOpcode(rs, rt, imm, opcodePtr); + else + formatImmediateEncodingOpcode(iOp, rs, rt, imm, opcodePtr); + } + + return m_formatBuffer; +} + +#endif // USE(MIPS32_DISASSEMBLER) diff --git a/src/3rdparty/masm/disassembler/mips32/Mips32Opcode.h b/src/3rdparty/masm/disassembler/mips32/Mips32Opcode.h new file mode 100644 index 0000000000..c63fb1109a --- /dev/null +++ b/src/3rdparty/masm/disassembler/mips32/Mips32Opcode.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2015 Cisco Systems, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CISCO SYSTEMS, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MIPS32Opcode_h_ +#define _MIPS32Opcode_h_ + +#if USE(MIPS32_DISASSEMBLER) + +#include <stdint.h> +#include <wtf/Assertions.h> + +class Mips32Opcode { +public: + Mips32Opcode() {} + + const char* disassemble(uint32_t*); + +private: + enum OpcodePrintFormat { + Unknown = 0, + Rs, + Rd, + Addr, + RdRs, + RsRt, + RtUImm, + RdRtRs, + RdRsRt, + RdRtSa, + RtRsImm, + RsRtAddr, + RtOffsetBase, + FdFs, + FdFsFt, + FtOffsetBase + }; + + const char *registerName(uint8_t r); + const char *fpRegisterName(uint8_t r); + void formatSpecialEncodingOpcode(uint8_t op1, uint8_t op2, uint8_t dest, uint8_t shift, uint8_t function); + void formatSpecial2EncodingOpcode(uint8_t op1, uint8_t op2, uint8_t dest, uint8_t function); + void formatJumpEncodingOpcode(uint32_t iOp, uint32_t index, uint32_t* opcodePtr); + void formatREGIMMEncodingOpcode(uint8_t rs, uint8_t rt, int16_t imm, uint32_t* opcodePtr); + void formatImmediateEncodingOpcode(uint32_t iOp, uint8_t rs, uint8_t rt, int16_t imm, uint32_t* opcodePtr); + void formatCOP1Opcode(uint8_t fmt, uint8_t ft, uint8_t fs, uint8_t fd, uint8_t func); + void formatCOP1FPCompareOpcode(uint8_t fmt, uint8_t ft, uint8_t fs, uint8_t cc, uint8_t cond); + void formatCOP1BCOpcode(uint8_t cc, uint8_t ndtf, int16_t offset, uint32_t* opcodePtr); + + static const int bufferSize = 81; + + char m_formatBuffer[bufferSize]; +}; + +#endif + +#endif // _MIPS32Opcode_h_ diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri index d0980c5312..f09a8329c9 100644 --- a/src/3rdparty/masm/masm-defs.pri +++ b/src/3rdparty/masm/masm-defs.pri @@ -26,6 +26,7 @@ INCLUDEPATH += $$PWD disassembler { if(isEqual(QT_ARCH, "i386")|isEqual(QT_ARCH, "x86_64")): DEFINES += WTF_USE_UDIS86=1 if(isEqual(QT_ARCH, "arm")): DEFINES += WTF_USE_ARMV7_DISASSEMBLER=1 + if(isEqual(QT_ARCH, "mips")): DEFINES += WTF_USE_MIPS32_DISASSEMBLER=1 } else { DEFINES += WTF_USE_UDIS86=0 } diff --git a/src/3rdparty/masm/masm.pri b/src/3rdparty/masm/masm.pri index 3497650f0c..04548fe8a3 100644 --- a/src/3rdparty/masm/masm.pri +++ b/src/3rdparty/masm/masm.pri @@ -59,6 +59,9 @@ contains(DEFINES, WTF_USE_UDIS86=1) { SOURCES += $$PWD/disassembler/ARMv7Disassembler.cpp SOURCES += $$PWD/disassembler/ARMv7/ARMv7DOpcode.cpp HEADERS += $$PWD/disassembler/ARMv7/ARMv7DOpcode.h +SOURCES += $$PWD/disassembler/Mips32Disassembler.cpp +SOURCES += $$PWD/disassembler/mips32/Mips32Opcode.cpp +HEADERS += $$PWD/disassembler/mips32/Mips32Opcode.h SOURCES += $$PWD/yarr/*.cpp HEADERS += $$PWD/yarr/*.h diff --git a/src/3rdparty/masm/wtf/FilePrintStream.cpp b/src/3rdparty/masm/wtf/FilePrintStream.cpp index b5ab25e0bf..45f1565f46 100644 --- a/src/3rdparty/masm/wtf/FilePrintStream.cpp +++ b/src/3rdparty/masm/wtf/FilePrintStream.cpp @@ -38,7 +38,8 @@ FilePrintStream::~FilePrintStream() { if (m_adoptionMode == Borrow) return; - fclose(m_file); + if (m_file) + fclose(m_file); } PassOwnPtr<FilePrintStream> FilePrintStream::open(const char* filename, const char* mode) diff --git a/src/3rdparty/masm/wtf/Platform.h b/src/3rdparty/masm/wtf/Platform.h index 3e2a51379c..f0612fe50e 100644 --- a/src/3rdparty/masm/wtf/Platform.h +++ b/src/3rdparty/masm/wtf/Platform.h @@ -735,7 +735,7 @@ #define WTF_USE_UDIS86 1 #endif -#if !defined(ENABLE_DISASSEMBLER) && (USE(UDIS86) || USE(ARMV7_DISASSEMBLER)) +#if !defined(ENABLE_DISASSEMBLER) && (USE(UDIS86) || USE(ARMV7_DISASSEMBLER) || USE(MIPS32_DISASSEMBLER)) #define ENABLE_DISASSEMBLER 1 #endif diff --git a/src/imports/folderlistmodel/fileinfothread.cpp b/src/imports/folderlistmodel/fileinfothread.cpp index e1db7834ec..ebdfba42a8 100644 --- a/src/imports/folderlistmodel/fileinfothread.cpp +++ b/src/imports/folderlistmodel/fileinfothread.cpp @@ -251,7 +251,8 @@ void FileInfoThread::getFileInfos(const QString &path) fileInfoList = currentDir.entryInfoList(nameFilters, filter, sortFlags); if (!fileInfoList.isEmpty()) { - foreach (QFileInfo info, fileInfoList) { + filePropertyList.reserve(fileInfoList.count()); + foreach (const QFileInfo &info, fileInfoList) { //qDebug() << "Adding file : " << info.fileName() << "to list "; filePropertyList << FileProperty(info); } diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp index 80b32e64ef..0f4a5bda54 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp @@ -788,6 +788,20 @@ QVariant QQuickFolderListModel::get(int idx, const QString &property) const return QVariant(); } +/*! + \qmlmethod int FolderListModel::indexOf(url file) + \since 5.6 + + Get the index of the given file URL if the model contains it, + or -1 if not. +*/ +int QQuickFolderListModel::indexOf(const QUrl &file) const +{ + Q_D(const QQuickFolderListModel); + FileProperty toFind(QFileInfo(file.toLocalFile())); + return d->data.indexOf(toFind); +} + #include "moc_qquickfolderlistmodel.cpp" //![code] diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.h b/src/imports/folderlistmodel/qquickfolderlistmodel.h index 63ed528556..fcfec56c87 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.h +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.h @@ -109,9 +109,9 @@ public: void setNameFilters(const QStringList &filters); enum SortField { Unsorted, Name, Time, Size, Type }; + Q_ENUM(SortField) SortField sortField() const; void setSortField(SortField field); - Q_ENUMS(SortField) bool sortReversed() const; void setSortReversed(bool rev); @@ -132,6 +132,7 @@ public: Q_INVOKABLE bool isFolder(int index) const; Q_INVOKABLE QVariant get(int idx, const QString &property) const; + Q_INVOKABLE int indexOf(const QUrl &file) const; //![parserstatus] virtual void classBegin(); diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index b0ba3f9228..22a3eed39d 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -126,7 +126,7 @@ public: ~QQmlSqlDatabaseWrapper() { } - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); }; } @@ -214,7 +214,7 @@ static QString qmlsqldatabase_databaseFile(const QString& connectionName, QV4::E return qmlsqldatabase_databasesPath(engine) + QDir::separator() + connectionName; } -static ReturnedValue qmlsqldatabase_rows_index(QQmlSqlDatabaseWrapper *r, ExecutionEngine *v4, quint32 index, bool *hasProperty = 0) +static ReturnedValue qmlsqldatabase_rows_index(const QQmlSqlDatabaseWrapper *r, ExecutionEngine *v4, quint32 index, bool *hasProperty = 0) { Scope scope(v4); @@ -238,15 +238,14 @@ static ReturnedValue qmlsqldatabase_rows_index(QQmlSqlDatabaseWrapper *r, Execut } } -ReturnedValue QQmlSqlDatabaseWrapper::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue QQmlSqlDatabaseWrapper::getIndexed(const Managed *m, uint index, bool *hasProperty) { - QV4::Scope scope(static_cast<QQmlSqlDatabaseWrapper *>(m)->engine()); Q_ASSERT(m->as<QQmlSqlDatabaseWrapper>()); - QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, static_cast<QQmlSqlDatabaseWrapper *>(m)); + const QQmlSqlDatabaseWrapper *r = static_cast<const QQmlSqlDatabaseWrapper *>(m); if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows) return Object::getIndexed(m, index, hasProperty); - return qmlsqldatabase_rows_index(r, scope.engine, index, hasProperty); + return qmlsqldatabase_rows_index(r, r->engine(), index, hasProperty); } static ReturnedValue qmlsqldatabase_rows_item(CallContext *ctx) @@ -285,13 +284,13 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) if (query.prepare(sql)) { if (ctx->argc() > 1) { ScopedValue values(scope, ctx->args()[1]); - if (values->asArrayObject()) { + if (values->as<ArrayObject>()) { ScopedArrayObject array(scope, values); quint32 size = array->getLength(); QV4::ScopedValue v(scope); for (quint32 ii = 0; ii < size; ++ii) query.bindValue(ii, scope.engine->toVariant((v = array->getIndexed(ii)), -1)); - } else if (values->asObject()) { + } else if (values->as<Object>()) { ScopedObject object(scope, values); ObjectIterator it(scope, object, ObjectIterator::WithProtoChain|ObjectIterator::EnumerableOnly); ScopedValue key(scope); @@ -304,7 +303,7 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx) if (key->isString()) { query.bindValue(key->stringValue()->toQString(), v); } else { - assert(key->isInteger()); + Q_ASSERT(key->isInteger()); query.bindValue(key->integerValue(), v); } } @@ -401,7 +400,7 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx) db.transaction(); ScopedCallData callData(scope, 1); - callData->thisObject = scope.engine->globalObject(); + callData->thisObject = scope.engine->globalObject; callData->args[0] = w; TransactionRollback rollbackOnException(&db, &w->d()->inTransaction); @@ -433,7 +432,7 @@ static ReturnedValue qmlsqldatabase_transaction_shared(CallContext *ctx, bool re if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database) V4THROW_REFERENCE("Not a SQLDatabase object"); - FunctionObject *callback = ctx->argc() ? ctx->args()[0].asFunctionObject() : 0; + const FunctionObject *callback = ctx->argc() ? ctx->args()[0].as<FunctionObject>() : 0; if (!callback) V4THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR, QQmlEngine::tr("transaction: missing callback")); @@ -450,7 +449,7 @@ static ReturnedValue qmlsqldatabase_transaction_shared(CallContext *ctx, bool re db.transaction(); if (callback) { ScopedCallData callData(scope, 1); - callData->thisObject = scope.engine->globalObject(); + callData->thisObject = scope.engine->globalObject; callData->args[0] = w; TransactionRollback rollbackOnException(&db, &w->d()->inTransaction); callback->call(callData); @@ -673,7 +672,7 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args) QString dbversion = (v = (*args)[1])->toQStringNoThrow(); QString dbdescription = (v = (*args)[2])->toQStringNoThrow(); int dbestimatedsize = (v = (*args)[3])->toInt32(); - FunctionObject *dbcreationCallback = (v = (*args)[4])->asFunctionObject(); + FunctionObject *dbcreationCallback = (v = (*args)[4])->as<FunctionObject>(); QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(dbname.toUtf8()); @@ -724,7 +723,7 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args) if (created && dbcreationCallback) { Scope scope(ctx); ScopedCallData callData(scope, 1); - callData->thisObject = scope.engine->globalObject(); + callData->thisObject = scope.engine->globalObject; callData->args[0] = db; dbcreationCallback->call(callData); } diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes index d98e9e6981..050d0f78cd 100644 --- a/src/imports/qtquick2/plugins.qmltypes +++ b/src/imports/qtquick2/plugins.qmltypes @@ -1728,6 +1728,7 @@ Module { } } Property { name: "family"; type: "string" } + Property { name: "styleName"; type: "string" } Property { name: "bold"; type: "bool" } Property { name: "weight"; type: "FontWeight" } Property { name: "italic"; type: "bool" } diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index 039ee63f07..8bedad40e9 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -613,12 +613,19 @@ Item { \li blue(x, y) Returns the blue channel value of the pixel at \a x, \a y position \li alpha(x, y) Returns the alpha channel value of the pixel at \a x, \a y position \li pixel(x, y) Returns the color value of the pixel at \a x, \a y position + \li equals(image) Returns \c true if this image is identical to \a image - + see \l QImage::operator== (since 5.6) + For example: \code var image = grabImage(rect); compare(image.red(10, 10), 255); compare(image.pixel(20, 20), Qt.rgba(255, 0, 0, 255)); + + rect.width += 10; + var newImage = grabImage(rect); + verify(!newImage.equals(image)); \endcode \endlist @@ -869,6 +876,9 @@ Item { focused item. If \a delay is larger than 0, the test will wait for \a delay milliseconds. + The event will be sent to the TestCase window or, in case of multiple windows, + to the current active window. See \l QGuiApplication::focusWindow() for more details. + \b{Note:} At some point you should release the key using keyRelease(). \sa keyRelease(), keyClick() @@ -894,6 +904,9 @@ Item { focused item. If \a delay is larger than 0, the test will wait for \a delay milliseconds. + The event will be sent to the TestCase window or, in case of multiple windows, + to the current active window. See \l QGuiApplication::focusWindow() for more details. + \sa keyPress(), keyClick() */ function keyRelease(key, modifiers, delay) { @@ -917,6 +930,9 @@ Item { focused item. If \a delay is larger than 0, the test will wait for \a delay milliseconds. + The event will be sent to the TestCase window or, in case of multiple windows, + to the current active window. See \l QGuiApplication::focusWindow() for more details. + \sa keyPress(), keyRelease() */ function keyClick(key, modifiers, delay) { diff --git a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp index b078edcbc6..832f484c13 100644 --- a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp +++ b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp @@ -36,7 +36,7 @@ #include <qqmlcontext.h> #include <private/qqmlengine_p.h> #include <private/qv8engine_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4engine_p.h> #include <private/qv4object_p.h> diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp index 32cde7c126..5e244ba02c 100644 --- a/src/particles/qquickcustomparticle.cpp +++ b/src/particles/qquickcustomparticle.cpp @@ -370,10 +370,10 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() } } - NodeHashConstIt it = m_nodes.begin(); + NodeHashConstIt it = m_nodes.cbegin(); rootNode = it.value(); rootNode->setFlag(QSGNode::OwnsMaterial, true); - const NodeHashConstIt cend = m_nodes.end(); + NodeHashConstIt cend = m_nodes.cend(); for (++it; it != cend; ++it) rootNode->appendChildNode(it.value()); diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp index 7df3299325..d78a350306 100644 --- a/src/particles/qquickimageparticle.cpp +++ b/src/particles/qquickimageparticle.cpp @@ -1156,7 +1156,9 @@ QQuickParticleData* QQuickImageParticle::getShadowDatum(QQuickParticleData* datu QQuickParticleGroupData* gd = m_system->groupData[datum->group]; if (!m_shadowData.contains(datum->group)) { QVector<QQuickParticleData*> data; - for (int i=0; i<gd->size(); i++){ + const int gdSize = gd->size(); + data.reserve(gdSize); + for (int i = 0; i < gdSize; i++) { QQuickParticleData* datum = new QQuickParticleData(m_system); *datum = *(gd->data[i]); data << datum; diff --git a/src/particles/qquickimageparticle_p.h b/src/particles/qquickimageparticle_p.h index 7d7d98dab6..16832d76dc 100644 --- a/src/particles/qquickimageparticle_p.h +++ b/src/particles/qquickimageparticle_p.h @@ -175,13 +175,12 @@ class QQuickImageParticle : public QQuickParticlePainter Q_PROPERTY(bool spritesInterpolate READ spritesInterpolate WRITE setSpritesInterpolate NOTIFY spritesInterpolateChanged) Q_PROPERTY(EntryEffect entryEffect READ entryEffect WRITE setEntryEffect NOTIFY entryEffectChanged) - Q_ENUMS(EntryEffect) - Q_ENUMS(Status) public: explicit QQuickImageParticle(QQuickItem *parent = 0); virtual ~QQuickImageParticle(); enum Status { Null, Ready, Loading, Error }; + Q_ENUM(Status) QQmlListProperty<QQuickSprite> sprites(); QQuickStochasticEngine* spriteEngine() {return m_spriteEngine;} @@ -191,6 +190,7 @@ public: Fade = 1, Scale = 2 }; + Q_ENUM(EntryEffect) enum PerformanceLevel{//TODO: Expose? Unknown = 0, diff --git a/src/particles/qquickparticleemitter_p.h b/src/particles/qquickparticleemitter_p.h index 65e2a34aa7..68f01e3f6a 100644 --- a/src/particles/qquickparticleemitter_p.h +++ b/src/particles/qquickparticleemitter_p.h @@ -67,7 +67,6 @@ class QQuickParticleEmitter : public QQuickItem Q_PROPERTY(QQuickDirection *acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged) Q_PROPERTY(qreal velocityFromMovement READ velocityFromMovement WRITE setVelocityFromMovement NOTIFY velocityFromMovementChanged) - Q_ENUMS(Lifetime) public: explicit QQuickParticleEmitter(QQuickItem *parent = 0); virtual ~QQuickParticleEmitter(); @@ -76,6 +75,7 @@ public: enum Lifetime { InfiniteLife = QQuickParticleSystem::maxLife }; + Q_ENUM(Lifetime) bool enabled() const { diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp index affd19d981..617c33d6e5 100644 --- a/src/particles/qquickparticlesystem.cpp +++ b/src/particles/qquickparticlesystem.cpp @@ -1025,6 +1025,7 @@ void QQuickParticleSystem::createEngine() } m_groups = newList; QList<QQuickStochasticState*> states; + states.reserve(m_groups.count()); foreach (QQuickParticleGroup* g, m_groups) states << (QQuickStochasticState*)g; diff --git a/src/particles/qquickpointattractor_p.h b/src/particles/qquickpointattractor_p.h index aa2c9f2083..31038aeba4 100644 --- a/src/particles/qquickpointattractor_p.h +++ b/src/particles/qquickpointattractor_p.h @@ -45,8 +45,6 @@ class QQuickAttractorAffector : public QQuickParticleAffector Q_PROPERTY(qreal pointY READ pointY WRITE setPointY NOTIFY pointYChanged) Q_PROPERTY(AffectableParameters affectedParameter READ affectedParameter WRITE setAffectedParameter NOTIFY affectedParameterChanged) Q_PROPERTY(Proportion proportionalToDistance READ proportionalToDistance WRITE setProportionalToDistance NOTIFY proportionalToDistanceChanged) - Q_ENUMS(AffectableParameters) - Q_ENUMS(Proportion) public: enum Proportion{ @@ -56,12 +54,14 @@ public: InverseLinear, InverseQuadratic }; + Q_ENUM(Proportion) enum AffectableParameters { Position, Velocity, Acceleration }; + Q_ENUM(AffectableParameters) explicit QQuickAttractorAffector(QQuickItem *parent = 0); diff --git a/src/particles/qquicktrailemitter_p.h b/src/particles/qquicktrailemitter_p.h index d1213158d8..fd11a07056 100644 --- a/src/particles/qquicktrailemitter_p.h +++ b/src/particles/qquicktrailemitter_p.h @@ -48,11 +48,11 @@ class QQuickTrailEmitter : public QQuickParticleEmitter Q_PROPERTY(qreal emitHeight READ emitterYVariation WRITE setEmitterYVariation NOTIFY emitterYVariationChanged) Q_PROPERTY(qreal emitWidth READ emitterXVariation WRITE setEmitterXVariation NOTIFY emitterXVariationChanged) - Q_ENUMS(EmitSize) public: enum EmitSize { ParticleSize = -2//Anything less than 0 will do }; + Q_ENUM(EmitSize) explicit QQuickTrailEmitter(QQuickItem *parent = 0); virtual void emitWindow(int timeStamp); virtual void reset(); diff --git a/src/particles/qquickv4particledata_p.h b/src/particles/qquickv4particledata_p.h index f211ec7e7d..c29c7e1134 100644 --- a/src/particles/qquickv4particledata_p.h +++ b/src/particles/qquickv4particledata_p.h @@ -36,7 +36,7 @@ #include <private/qv8engine_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> QT_BEGIN_NAMESPACE diff --git a/src/particles/qquickwander_p.h b/src/particles/qquickwander_p.h index 49c49c560e..93ad8516a7 100644 --- a/src/particles/qquickwander_p.h +++ b/src/particles/qquickwander_p.h @@ -54,7 +54,6 @@ class QQuickWanderAffector : public QQuickParticleAffector Q_PROPERTY(qreal xVariance READ xVariance WRITE setXVariance NOTIFY xVarianceChanged) Q_PROPERTY(qreal yVariance READ yVariance WRITE setYVariance NOTIFY yVarianceChanged) Q_PROPERTY(AffectableParameters affectedParameter READ affectedParameter WRITE setAffectedParameter NOTIFY affectedParameterChanged) - Q_ENUMS(AffectableParameters) public: enum AffectableParameters { @@ -62,6 +61,7 @@ public: Velocity, Acceleration }; + Q_ENUM(AffectableParameters) explicit QQuickWanderAffector(QQuickItem *parent = 0); ~QQuickWanderAffector(); diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 664a457608..273407c19d 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -SUBDIRS += qmltooling +!contains(QT_CONFIG, no-qml-debug):SUBDIRS += qmltooling diff --git a/src/qml/debugger/qdebugmessageservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp index deaa472ce0..6bccec08b1 100644 --- a/src/qml/debugger/qdebugmessageservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp @@ -31,62 +31,37 @@ ** ****************************************************************************/ -#include "qdebugmessageservice_p.h" -#include "qqmldebugservice_p_p.h" +#include "qdebugmessageservice.h" +#include <private/qqmldebugconnector_p.h> #include <QDataStream> -#include <QMutex> QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(QDebugMessageService, qmlDebugMessageService) +const QString QDebugMessageService::s_key = QStringLiteral("DebugMessages"); void DebugMessageHandler(QtMsgType type, const QMessageLogContext &ctxt, const QString &buf) { - QDebugMessageService::instance()->sendDebugMessage(type, ctxt, buf); + QQmlDebugConnector::service<QDebugMessageService>()->sendDebugMessage(type, ctxt, buf); } -class QDebugMessageServicePrivate : public QQmlDebugServicePrivate -{ -public: - QDebugMessageServicePrivate() - : oldMsgHandler(0) - , prevState(QQmlDebugService::NotConnected) - { - } - - QtMessageHandler oldMsgHandler; - QQmlDebugService::State prevState; - QMutex initMutex; -}; - QDebugMessageService::QDebugMessageService(QObject *parent) : - QQmlDebugService(*(new QDebugMessageServicePrivate()), - QStringLiteral("DebugMessages"), 2, parent) + QQmlDebugService(s_key, 2, parent), oldMsgHandler(0), + prevState(QQmlDebugService::NotConnected) { - Q_D(QDebugMessageService); - // don't execute stateChanged() in parallel - QMutexLocker lock(&d->initMutex); - registerService(); + QMutexLocker lock(&initMutex); if (state() == Enabled) { - d->oldMsgHandler = qInstallMessageHandler(DebugMessageHandler); - d->prevState = Enabled; + oldMsgHandler = qInstallMessageHandler(DebugMessageHandler); + prevState = Enabled; } } -QDebugMessageService *QDebugMessageService::instance() -{ - return qmlDebugMessageService(); -} - void QDebugMessageService::sendDebugMessage(QtMsgType type, const QMessageLogContext &ctxt, const QString &buf) { - Q_D(QDebugMessageService); - //We do not want to alter the message handling mechanism //We just eavesdrop and forward the messages to a port //only if a client is connected to it. @@ -96,27 +71,26 @@ void QDebugMessageService::sendDebugMessage(QtMsgType type, ws << QString::fromLatin1(ctxt.file).toUtf8(); ws << ctxt.line << QString::fromLatin1(ctxt.function).toUtf8(); - sendMessage(message); - if (d->oldMsgHandler) - (*d->oldMsgHandler)(type, ctxt, buf); + emit messageToClient(name(), message); + if (oldMsgHandler) + (*oldMsgHandler)(type, ctxt, buf); } void QDebugMessageService::stateChanged(State state) { - Q_D(QDebugMessageService); - QMutexLocker lock(&d->initMutex); + QMutexLocker lock(&initMutex); - if (state != Enabled && d->prevState == Enabled) { - QtMessageHandler handler = qInstallMessageHandler(d->oldMsgHandler); + if (state != Enabled && prevState == Enabled) { + QtMessageHandler handler = qInstallMessageHandler(oldMsgHandler); // has our handler been overwritten in between? if (handler != DebugMessageHandler) qInstallMessageHandler(handler); - } else if (state == Enabled && d->prevState != Enabled) { - d->oldMsgHandler = qInstallMessageHandler(DebugMessageHandler); + } else if (state == Enabled && prevState != Enabled) { + oldMsgHandler = qInstallMessageHandler(DebugMessageHandler); } - d->prevState = state; + prevState = state; } QT_END_NAMESPACE diff --git a/src/qml/debugger/qdebugmessageservice_p.h b/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.h index 694cd0e64c..c0dc41bcd0 100644 --- a/src/qml/debugger/qdebugmessageservice_p.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.h @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef QDEBUGMESSAGESERVICE_P_H -#define QDEBUGMESSAGESERVICE_P_H +#ifndef QDEBUGMESSAGESERVICE_H +#define QDEBUGMESSAGESERVICE_H // // W A R N I N G @@ -45,9 +45,10 @@ // We mean it. // -#include "qqmldebugservice_p.h" +#include <private/qqmldebugservice_p.h> #include <QtCore/qlogging.h> +#include <QtCore/qmutex.h> QT_BEGIN_NAMESPACE @@ -59,19 +60,23 @@ class QDebugMessageService : public QQmlDebugService public: QDebugMessageService(QObject *parent = 0); - static QDebugMessageService *instance(); - void sendDebugMessage(QtMsgType type, const QMessageLogContext &ctxt, const QString &buf); protected: + static const QString s_key; + void stateChanged(State); private: - Q_DISABLE_COPY(QDebugMessageService) - Q_DECLARE_PRIVATE(QDebugMessageService) + friend class QQmlDebugConnector; + friend class QQmlDebuggerServiceFactory; + + QtMessageHandler oldMsgHandler; + QQmlDebugService::State prevState; + QMutex initMutex; }; QT_END_NAMESPACE -#endif // QDEBUGMESSAGESERVICE_P_H +#endif // QDEBUGMESSAGESERVICE_H diff --git a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro new file mode 100644 index 0000000000..d860328dc8 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro @@ -0,0 +1,32 @@ +TARGET = qmldbg_debugger +QT = qml-private core-private + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QQmlDebuggerServiceFactory +load(qt_plugin) + +SOURCES += \ + $$PWD/qdebugmessageservice.cpp \ + $$PWD/qqmldebuggerservicefactory.cpp \ + $$PWD/qqmlenginedebugservice.cpp \ + $$PWD/qqmlwatcher.cpp \ + $$PWD/qv4debugservice.cpp \ + $$PWD/qv4debuggeragent.cpp \ + $$PWD/qv4datacollector.cpp + +HEADERS += \ + $$PWD/../shared/qqmlconfigurabledebugservice.h \ + $$PWD/qdebugmessageservice.h \ + $$PWD/qqmldebuggerservicefactory.h \ + $$PWD/qqmlenginedebugservice.h \ + $$PWD/qqmlwatcher.h \ + $$PWD/qv4debugservice.h \ + $$PWD/qv4debuggeragent.h \ + $$PWD/qv4datacollector.h + +INCLUDEPATH += $$PWD \ + $$PWD/../shared + +OTHER_FILES += \ + $$PWD/qqmldebuggerservice.json + diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservice.json b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservice.json new file mode 100644 index 0000000000..b1e90364d5 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservice.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "DebugMessages", "QmlDebugger", "V8Debugger" ] +} diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp new file mode 100644 index 0000000000..7ba2aeecb0 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldebuggerservicefactory.h" +#include "qqmlenginedebugservice.h" +#include "qdebugmessageservice.h" +#include "qv4debugservice.h" +#include <private/qqmldebugserviceinterfaces_p.h> + +QT_BEGIN_NAMESPACE + +QQmlDebugService *QQmlDebuggerServiceFactory::create(const QString &key) +{ + if (key == QDebugMessageService::s_key) + return new QDebugMessageService(this); + + if (key == QQmlEngineDebugServiceImpl::s_key) + return new QQmlEngineDebugServiceImpl(this); + + if (key == QV4DebugServiceImpl::s_key) + return new QV4DebugServiceImpl(this); + + return 0; +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4qmlextensions.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.h index c1c4e0ec78..2c7509ba12 100644 --- a/src/qml/jsruntime/qv4qmlextensions.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.h @@ -31,13 +31,21 @@ ** ****************************************************************************/ -#include "qv4qmlextensions_p.h" -#include "qv4object_p.h" +#ifndef QQMLDEBUGGERSERVICEFACTORY_H +#define QQMLDEBUGGERSERVICEFACTORY_H -using namespace QV4; +#include <private/qqmldebugservicefactory_p.h> -void QmlExtensions::markObjects(ExecutionEngine *e) +QT_BEGIN_NAMESPACE + +class QQmlDebuggerServiceFactory : public QQmlDebugServiceFactory { - if (valueTypeWrapperPrototype) - valueTypeWrapperPrototype->mark(e); -} + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlDebugServiceFactory_iid FILE "qqmldebuggerservice.json") +public: + QQmlDebugService *create(const QString &key); +}; + +QT_END_NAMESPACE + +#endif // QQMLDEBUGGERSERVICEFACTORY_H diff --git a/src/qml/debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index da01d00f17..8f53dc6d50 100644 --- a/src/qml/debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -31,9 +31,10 @@ ** ****************************************************************************/ -#include "qqmlenginedebugservice_p.h" +#include "qqmlenginedebugservice.h" +#include "qqmlwatcher.h" -#include "qqmldebugstatesdelegate_p.h" +#include <private/qqmldebugstatesdelegate_p.h> #include <private/qqmlboundsignal_p.h> #include <qqmlengine.h> #include <private/qqmlmetatype_p.h> @@ -41,42 +42,31 @@ #include <private/qqmlproperty_p.h> #include <private/qqmlbinding_p.h> #include <private/qqmlcontext_p.h> -#include <private/qqmlwatcher_p.h> #include <private/qqmlvaluetype_p.h> #include <private/qqmlvmemetaobject_p.h> #include <private/qqmlexpression_p.h> #include <QtCore/qdebug.h> #include <QtCore/qmetaobject.h> +#include <QtCore/qfileinfo.h> #include <private/qmetaobject_p.h> QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(QQmlEngineDebugService, qmlEngineDebugService) - -QQmlEngineDebugService *QQmlEngineDebugService::instance() -{ - return qmlEngineDebugService(); -} - -QQmlEngineDebugService::QQmlEngineDebugService(QObject *parent) - : QQmlDebugService(QStringLiteral("QmlDebugger"), 2, parent), - m_watch(new QQmlWatcher(this)), - m_statesDelegate(0) +QQmlEngineDebugServiceImpl::QQmlEngineDebugServiceImpl(QObject *parent) : + QQmlEngineDebugService(2, parent), m_watch(new QQmlWatcher(this)), m_statesDelegate(0) { QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)), this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant))); - - registerService(); } -QQmlEngineDebugService::~QQmlEngineDebugService() +QQmlEngineDebugServiceImpl::~QQmlEngineDebugServiceImpl() { delete m_statesDelegate; } QDataStream &operator<<(QDataStream &ds, - const QQmlEngineDebugService::QQmlObjectData &data) + const QQmlEngineDebugServiceImpl::QQmlObjectData &data) { ds << data.url << data.lineNumber << data.columnNumber << data.idString << data.objectName << data.objectType << data.objectId << data.contextId @@ -85,7 +75,7 @@ QDataStream &operator<<(QDataStream &ds, } QDataStream &operator>>(QDataStream &ds, - QQmlEngineDebugService::QQmlObjectData &data) + QQmlEngineDebugServiceImpl::QQmlObjectData &data) { ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString >> data.objectName >> data.objectType >> data.objectId >> data.contextId @@ -94,7 +84,7 @@ QDataStream &operator>>(QDataStream &ds, } QDataStream &operator<<(QDataStream &ds, - const QQmlEngineDebugService::QQmlObjectProperty &data) + const QQmlEngineDebugServiceImpl::QQmlObjectProperty &data) { ds << (int)data.type << data.name; // check first whether the data can be saved @@ -110,12 +100,12 @@ QDataStream &operator<<(QDataStream &ds, } QDataStream &operator>>(QDataStream &ds, - QQmlEngineDebugService::QQmlObjectProperty &data) + QQmlEngineDebugServiceImpl::QQmlObjectProperty &data) { int type; ds >> type >> data.name >> data.value >> data.valueTypeName >> data.binding >> data.hasNotifySignal; - data.type = (QQmlEngineDebugService::QQmlObjectProperty::Type)type; + data.type = (QQmlEngineDebugServiceImpl::QQmlObjectProperty::Type)type; return ds; } @@ -142,8 +132,8 @@ static bool hasValidSignal(QObject *object, const QString &propertyName) return true; } -QQmlEngineDebugService::QQmlObjectProperty -QQmlEngineDebugService::propertyData(QObject *obj, int propIdx) +QQmlEngineDebugServiceImpl::QQmlObjectProperty +QQmlEngineDebugServiceImpl::propertyData(QObject *obj, int propIdx) { QQmlObjectProperty rv; @@ -177,7 +167,7 @@ QQmlEngineDebugService::propertyData(QObject *obj, int propIdx) return rv; } -QVariant QQmlEngineDebugService::valueContents(QVariant value) const +QVariant QQmlEngineDebugServiceImpl::valueContents(QVariant value) const { // We can't send JS objects across the wire, so transform them to variant // maps for serialization. @@ -192,6 +182,7 @@ QVariant QQmlEngineDebugService::valueContents(QVariant value) const QVariantList contents; QVariantList list = value.toList(); int count = list.size(); + contents.reserve(count); for (int i = 0; i < count; i++) contents << valueContents(list.at(i)); return contents; @@ -237,7 +228,7 @@ QVariant QQmlEngineDebugService::valueContents(QVariant value) const return QString(QStringLiteral("<unknown value>")); } -void QQmlEngineDebugService::buildObjectDump(QDataStream &message, +void QQmlEngineDebugServiceImpl::buildObjectDump(QDataStream &message, QObject *object, bool recur, bool dumpProperties) { message << objectData(object); @@ -277,13 +268,9 @@ void QQmlEngineDebugService::buildObjectDump(QDataStream &message, QQmlData *ddata = QQmlData::get(object); if (ddata && ddata->signalHandlers) { - QQmlAbstractBoundSignal *signalHandler = ddata->signalHandlers; + QQmlBoundSignal *signalHandler = ddata->signalHandlers; while (signalHandler) { - if (!dumpProperties) { - signalHandler = signalHandler->m_nextSignal; - continue; - } QQmlObjectProperty prop; prop.type = QQmlObjectProperty::SignalProperty; prop.hasNotifySignal = false; @@ -292,7 +279,7 @@ void QQmlEngineDebugService::buildObjectDump(QDataStream &message, prop.value = expr->expression(); QObject *scope = expr->scopeObject(); if (scope) { - QString methodName = QString::fromLatin1(QMetaObjectPrivate::signal(scope->metaObject(), signalHandler->index()).name()); + QString methodName = QString::fromLatin1(QMetaObjectPrivate::signal(scope->metaObject(), signalHandler->signalIndex()).name()); if (!methodName.isEmpty()) { prop.name = QLatin1String("on") + methodName[0].toUpper() + methodName.mid(1); @@ -301,7 +288,7 @@ void QQmlEngineDebugService::buildObjectDump(QDataStream &message, } fakeProperties << prop; - signalHandler = signalHandler->m_nextSignal; + signalHandler = nextSignal(signalHandler); } } @@ -314,7 +301,7 @@ void QQmlEngineDebugService::buildObjectDump(QDataStream &message, message << fakeProperties[ii]; } -void QQmlEngineDebugService::prepareDeferredObjects(QObject *obj) +void QQmlEngineDebugServiceImpl::prepareDeferredObjects(QObject *obj) { qmlExecuteDeferred(obj); @@ -326,7 +313,7 @@ void QQmlEngineDebugService::prepareDeferredObjects(QObject *obj) } -void QQmlEngineDebugService::storeObjectIds(QObject *co) +void QQmlEngineDebugServiceImpl::storeObjectIds(QObject *co) { QQmlDebugService::idForObject(co); QObjectList children = co->children(); @@ -334,7 +321,7 @@ void QQmlEngineDebugService::storeObjectIds(QObject *co) storeObjectIds(children.at(ii)); } -void QQmlEngineDebugService::buildObjectList(QDataStream &message, +void QQmlEngineDebugServiceImpl::buildObjectList(QDataStream &message, QQmlContext *ctxt, const QList<QPointer<QObject> > &instances) { @@ -378,15 +365,15 @@ void QQmlEngineDebugService::buildObjectList(QDataStream &message, } } -void QQmlEngineDebugService::buildStatesList(bool cleanList, +void QQmlEngineDebugServiceImpl::buildStatesList(bool cleanList, const QList<QPointer<QObject> > &instances) { if (m_statesDelegate) m_statesDelegate->buildStatesList(cleanList, instances); } -QQmlEngineDebugService::QQmlObjectData -QQmlEngineDebugService::objectData(QObject *object) +QQmlEngineDebugServiceImpl::QQmlObjectData +QQmlEngineDebugServiceImpl::objectData(QObject *object) { QQmlData *ddata = QQmlData::get(object); QQmlObjectData rv; @@ -425,12 +412,33 @@ QQmlEngineDebugService::objectData(QObject *object) return rv; } -void QQmlEngineDebugService::messageReceived(const QByteArray &message) +void QQmlEngineDebugServiceImpl::messageReceived(const QByteArray &message) { QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message)); } -void QQmlEngineDebugService::processMessage(const QByteArray &message) +/*! + Returns a list of objects matching the given filename, line and column. +*/ +QList<QObject*> QQmlEngineDebugServiceImpl::objectForLocationInfo(const QString &filename, + int lineNumber, int columnNumber) +{ + QList<QObject *> objects; + const QHash<int, QObject *> &hash = objectsForIds(); + for (QHash<int, QObject *>::ConstIterator i = hash.constBegin(); i != hash.constEnd(); ++i) { + QQmlData *ddata = QQmlData::get(i.value()); + if (ddata && ddata->outerContext) { + if (QFileInfo(ddata->outerContext->urlString()).fileName() == filename && + ddata->lineNumber == lineNumber && + ddata->columnNumber >= columnNumber) { + objects << i.value(); + } + } + } + return objects; +} + +void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message) { QQmlDebugStream ds(message); @@ -503,8 +511,7 @@ void QQmlEngineDebugService::processMessage(const QByteArray &message) ds >> file >> lineNumber >> columnNumber >> recurse >> dumpProperties; - QList<QObject*> objects = QQmlDebugService::objectForLocationInfo( - file, lineNumber, columnNumber); + QList<QObject*> objects = objectForLocationInfo(file, lineNumber, columnNumber); rs << QByteArray("FETCH_OBJECTS_FOR_LOCATION_R") << queryId << objects.count(); @@ -610,10 +617,10 @@ void QQmlEngineDebugService::processMessage(const QByteArray &message) rs << QByteArray("SET_METHOD_BODY_R") << queryId << ok; } - sendMessage(reply); + emit messageToClient(name(), reply); } -bool QQmlEngineDebugService::setBinding(int objectId, +bool QQmlEngineDebugServiceImpl::setBinding(int objectId, const QString &propertyName, const QVariant &expression, bool isLiteralValue, @@ -644,11 +651,9 @@ bool QQmlEngineDebugService::setBinding(int objectId, filename, line, column); QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression); } else if (property.isProperty()) { - QQmlBinding *binding = new QQmlBinding(expression.toString(), object, QQmlContextData::get(context), filename, line, column);; + QQmlBinding *binding = new QQmlBinding(expression.toString(), object, QQmlContextData::get(context), filename, line, column); binding->setTarget(property); - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::setBinding(property, binding); - if (oldBinding) - oldBinding->destroy(); + QQmlPropertyPrivate::setBinding(binding); binding->update(); } else { ok = false; @@ -667,7 +672,7 @@ bool QQmlEngineDebugService::setBinding(int objectId, return ok; } -bool QQmlEngineDebugService::resetBinding(int objectId, const QString &propertyName) +bool QQmlEngineDebugServiceImpl::resetBinding(int objectId, const QString &propertyName) { QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); @@ -679,12 +684,7 @@ bool QQmlEngineDebugService::resetBinding(int objectId, const QString &propertyN if (object->property(parentProperty.toLatin1()).isValid()) { QQmlProperty property(object, propertyName); - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(property); - if (oldBinding) { - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::setBinding(property, 0); - if (oldBinding) - oldBinding->destroy(); - } + QQmlPropertyPrivate::removeBinding(property); if (property.isResettable()) { // Note: this will reset the property in any case, without regard to states // Right now almost no QQuickItem has reset methods for its properties (with the @@ -725,7 +725,7 @@ bool QQmlEngineDebugService::resetBinding(int objectId, const QString &propertyN return false; } -bool QQmlEngineDebugService::setMethodBody(int objectId, const QString &method, const QString &body) +bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &method, const QString &body) { QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); @@ -762,22 +762,22 @@ bool QQmlEngineDebugService::setMethodBody(int objectId, const QString &method, int lineNumber = vmeMetaObject->vmeMethodLineNumber(prop->coreIndex); QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine(object)->handle()); QV4::Scope scope(v4); - QV4::ScopedValue v(scope, QQmlExpressionPrivate::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber)); + QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber)); vmeMetaObject->setVmeMethod(prop->coreIndex, v); return true; } -void QQmlEngineDebugService::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value) +void QQmlEngineDebugServiceImpl::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value) { QByteArray reply; QQmlDebugStream rs(&reply, QIODevice::WriteOnly); rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value); - sendMessage(reply); + emit messageToClient(name(), reply); } -void QQmlEngineDebugService::engineAboutToBeAdded(QQmlEngine *engine) +void QQmlEngineDebugServiceImpl::engineAboutToBeAdded(QQmlEngine *engine) { Q_ASSERT(engine); Q_ASSERT(!m_engines.contains(engine)); @@ -786,7 +786,7 @@ void QQmlEngineDebugService::engineAboutToBeAdded(QQmlEngine *engine) emit attachedToEngine(engine); } -void QQmlEngineDebugService::engineAboutToBeRemoved(QQmlEngine *engine) +void QQmlEngineDebugServiceImpl::engineAboutToBeRemoved(QQmlEngine *engine) { Q_ASSERT(engine); Q_ASSERT(m_engines.contains(engine)); @@ -795,7 +795,7 @@ void QQmlEngineDebugService::engineAboutToBeRemoved(QQmlEngine *engine) emit detachedFromEngine(engine); } -void QQmlEngineDebugService::objectCreated(QQmlEngine *engine, QObject *object) +void QQmlEngineDebugServiceImpl::objectCreated(QQmlEngine *engine, QObject *object) { Q_ASSERT(engine); Q_ASSERT(m_engines.contains(engine)); @@ -809,10 +809,10 @@ void QQmlEngineDebugService::objectCreated(QQmlEngine *engine, QObject *object) //unique queryId -1 rs << QByteArray("OBJECT_CREATED") << -1 << engineId << objectId << parentId; - sendMessage(reply); + emit messageToClient(name(), reply); } -void QQmlEngineDebugService::setStatesDelegate(QQmlDebugStatesDelegate *delegate) +void QQmlEngineDebugServiceImpl::setStatesDelegate(QQmlDebugStatesDelegate *delegate) { m_statesDelegate = delegate; } diff --git a/src/qml/debugger/qqmlenginedebugservice_p.h b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h index 0a824b132f..19a4827f89 100644 --- a/src/qml/debugger/qqmlenginedebugservice_p.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef QQMLENGINEDEBUGSERVICE_P_H -#define QQMLENGINEDEBUGSERVICE_P_H +#ifndef QQMLENGINEDEBUGSERVICE_H +#define QQMLENGINEDEBUGSERVICE_H // // W A R N I N G @@ -46,6 +46,7 @@ // #include <private/qqmldebugservice_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include <QtCore/qurl.h> #include <QtCore/qvariant.h> @@ -59,12 +60,12 @@ class QQmlWatcher; class QDataStream; class QQmlDebugStatesDelegate; -class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : public QQmlDebugService +class QQmlEngineDebugServiceImpl : public QQmlEngineDebugService { Q_OBJECT public: - QQmlEngineDebugService(QObject * = 0); - ~QQmlEngineDebugService(); + QQmlEngineDebugServiceImpl(QObject * = 0); + ~QQmlEngineDebugServiceImpl(); struct QQmlObjectData { QUrl url; @@ -94,8 +95,6 @@ public: void setStatesDelegate(QQmlDebugStatesDelegate *); - static QQmlEngineDebugService *instance(); - protected: virtual void messageReceived(const QByteArray &); @@ -104,6 +103,8 @@ private Q_SLOTS: void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value); private: + friend class QQmlDebuggerServiceFactory; + void prepareDeferredObjects(QObject *); void buildObjectList(QDataStream &, QQmlContext *, const QList<QPointer<QObject> > &instances); @@ -116,17 +117,19 @@ private: bool resetBinding(int objectId, const QString &propertyName); bool setMethodBody(int objectId, const QString &method, const QString &body); void storeObjectIds(QObject *co); + QList<QObject *> objectForLocationInfo(const QString &filename, int lineNumber, + int columnNumber); QList<QQmlEngine *> m_engines; QQmlWatcher *m_watch; QQmlDebugStatesDelegate *m_statesDelegate; }; -Q_QML_PRIVATE_EXPORT QDataStream &operator<<(QDataStream &, const QQmlEngineDebugService::QQmlObjectData &); -Q_QML_PRIVATE_EXPORT QDataStream &operator>>(QDataStream &, QQmlEngineDebugService::QQmlObjectData &); -Q_QML_PRIVATE_EXPORT QDataStream &operator<<(QDataStream &, const QQmlEngineDebugService::QQmlObjectProperty &); -Q_QML_PRIVATE_EXPORT QDataStream &operator>>(QDataStream &, QQmlEngineDebugService::QQmlObjectProperty &); +QDataStream &operator<<(QDataStream &, const QQmlEngineDebugServiceImpl::QQmlObjectData &); +QDataStream &operator>>(QDataStream &, QQmlEngineDebugServiceImpl::QQmlObjectData &); +QDataStream &operator<<(QDataStream &, const QQmlEngineDebugServiceImpl::QQmlObjectProperty &); +QDataStream &operator>>(QDataStream &, QQmlEngineDebugServiceImpl::QQmlObjectProperty &); QT_END_NAMESPACE -#endif // QQMLENGINEDEBUGSERVICE_P_H +#endif // QQMLENGINEDEBUGSERVICE_H diff --git a/src/qml/qml/qqmlwatcher.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp index 9726b6f3b9..9f9a6eb33b 100644 --- a/src/qml/qml/qqmlwatcher.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp @@ -31,15 +31,15 @@ ** ****************************************************************************/ -#include "qqmlwatcher_p.h" +#include "qqmlwatcher.h" #include "qqmlexpression.h" #include "qqmlcontext.h" #include "qqml.h" #include <private/qqmldebugservice_p.h> -#include "qqmlproperty_p.h" -#include "qqmlvaluetype_p.h" +#include <private/qqmlproperty_p.h> +#include <private/qqmlvaluetype_p.h> #include <QtCore/qmetaobject.h> #include <QtCore/qdebug.h> diff --git a/src/qml/qml/qqmlwatcher_p.h b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.h index a7bb3c3418..329aee77d2 100644 --- a/src/qml/qml/qqmlwatcher_p.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.h @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef QQMLWATCHER_P_H -#define QQMLWATCHER_P_H +#ifndef QQMLWATCHER_H +#define QQMLWATCHER_H // // W A R N I N G @@ -83,4 +83,4 @@ private: QT_END_NAMESPACE -#endif // QQMLWATCHER_P_H +#endif // QQMLWATCHER_H diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp new file mode 100644 index 0000000000..64ee5c3b96 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -0,0 +1,452 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4datacollector.h" + +#include <private/qv4script_p.h> +#include <private/qv4string_p.h> +#include <private/qv4objectiterator_p.h> +#include <private/qv4identifier_p.h> + +#include <QtCore/qjsonarray.h> + +QT_BEGIN_NAMESPACE + +QV4::Heap::CallContext *QV4DataCollector::findContext(QV4::Heap::ExecutionContext *ctxt, int frame) +{ + if (!ctxt) + return 0; + + QV4::Scope scope(ctxt->engine); + QV4::ScopedContext ctx(scope, ctxt); + while (ctx) { + QV4::CallContext *cCtxt = ctx->asCallContext(); + if (cCtxt && cCtxt->d()->function) { + if (frame < 1) + return cCtxt->d(); + --frame; + } + ctx = ctx->d()->parent; + } + + return 0; +} + +QV4::Heap::CallContext *QV4DataCollector::findScope(QV4::Heap::ExecutionContext *ctxt, int scope) +{ + if (!ctxt) + return 0; + + QV4::Scope s(ctxt->engine); + QV4::ScopedContext ctx(s, ctxt); + for (; scope > 0 && ctx; --scope) + ctx = ctx->d()->outer; + + return (ctx && ctx->d()) ? ctx->asCallContext()->d() : 0; +} + +QVector<QV4::Heap::ExecutionContext::ContextType> QV4DataCollector::getScopeTypes( + QV4::ExecutionEngine *engine, int frame) +{ + QVector<QV4::Heap::ExecutionContext::ContextType> types; + + QV4::Scope scope(engine); + QV4::Scoped<QV4::CallContext> sctxt(scope, findContext(engine->currentContext(), frame)); + if (!sctxt || sctxt->d()->type < QV4::Heap::ExecutionContext::Type_QmlContext) + return types; + + QV4::ScopedContext it(scope, sctxt->d()); + for (; it; it = it->d()->outer) + types.append(it->d()->type); + + return types; +} + + +QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine) + : m_engine(engine), m_collectedRefs(Q_NULLPTR) +{ + values.set(engine, engine->newArrayObject()); +} + +QV4DataCollector::~QV4DataCollector() +{ +} + +void QV4DataCollector::collect(const QV4::ScopedValue &value) +{ + if (m_collectedRefs) + m_collectedRefs->append(addRef(value)); +} + +QJsonObject QV4DataCollector::lookupRef(Ref ref) +{ + QJsonObject dict; + if (lookupSpecialRef(ref, &dict)) + return dict; + + dict.insert(QStringLiteral("handle"), qint64(ref)); + + QV4::Scope scope(engine()); + QV4::ScopedValue value(scope, getValue(ref)); + switch (value->type()) { + case QV4::Value::Empty_Type: + Q_ASSERT(!"empty Value encountered"); + break; + case QV4::Value::Undefined_Type: + dict.insert(QStringLiteral("type"), QStringLiteral("undefined")); + break; + case QV4::Value::Null_Type: + dict.insert(QStringLiteral("type"), QStringLiteral("null")); + break; + case QV4::Value::Boolean_Type: + dict.insert(QStringLiteral("type"), QStringLiteral("boolean")); + dict.insert(QStringLiteral("value"), value->booleanValue() ? QStringLiteral("true") + : QStringLiteral("false")); + break; + case QV4::Value::Managed_Type: + if (QV4::String *s = value->as<QV4::String>()) { + dict.insert(QStringLiteral("type"), QStringLiteral("string")); + dict.insert(QStringLiteral("value"), s->toQString()); + } else if (QV4::Object *o = value->as<QV4::Object>()) { + dict.insert(QStringLiteral("type"), QStringLiteral("object")); + dict.insert(QStringLiteral("properties"), collectProperties(o)); + } else { + Q_UNREACHABLE(); + } + break; + case QV4::Value::Integer_Type: + dict.insert(QStringLiteral("type"), QStringLiteral("number")); + dict.insert(QStringLiteral("value"), value->integerValue()); + break; + default: // double + dict.insert(QStringLiteral("type"), QStringLiteral("number")); + dict.insert(QStringLiteral("value"), value->doubleValue()); + break; + } + + return dict; +} + +QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionName) +{ + Ref ref = addRef(QV4::Primitive::emptyValue(), false); + + QJsonObject dict; + dict.insert(QStringLiteral("handle"), qint64(ref)); + dict.insert(QStringLiteral("type"), QStringLiteral("function")); + dict.insert(QStringLiteral("className"), QStringLiteral("Function")); + dict.insert(QStringLiteral("name"), functionName); + specialRefs.insert(ref, dict); + + return ref; +} + +QV4DataCollector::Ref QV4DataCollector::addScriptRef(const QString &scriptName) +{ + Ref ref = addRef(QV4::Primitive::emptyValue(), false); + + QJsonObject dict; + dict.insert(QStringLiteral("handle"), qint64(ref)); + dict.insert(QStringLiteral("type"), QStringLiteral("script")); + dict.insert(QStringLiteral("name"), scriptName); + specialRefs.insert(ref, dict); + + return ref; +} + +void QV4DataCollector::collectScope(QJsonObject *dict, QV4::Debugging::Debugger *debugger, + int frameNr, int scopeNr) +{ + QStringList names; + + Refs refs; + if (debugger->state() == QV4::Debugging::Debugger::Paused) { + RefHolder holder(this, &refs); + ArgumentCollectJob argumentsJob(m_engine, this, &names, frameNr, scopeNr); + debugger->runInEngine(&argumentsJob); + LocalCollectJob localsJob(m_engine, this, &names, frameNr, scopeNr); + debugger->runInEngine(&localsJob); + } + + QV4::Scope scope(engine()); + QV4::ScopedObject scopeObject(scope, engine()->newObject()); + + Q_ASSERT(names.size() == refs.size()); + for (int i = 0, ei = refs.size(); i != ei; ++i) + scopeObject->put(engine(), names.at(i), + QV4::Value::fromReturnedValue(getValue(refs.at(i)))); + + Ref scopeObjectRef = addRef(scopeObject); + dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef)); + if (m_collectedRefs) + m_collectedRefs->append(scopeObjectRef); +} + +QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicate) +{ + class ExceptionStateSaver + { + quint32 *hasExceptionLoc; + quint32 hadException; + + public: + ExceptionStateSaver(QV4::ExecutionEngine *engine) + : hasExceptionLoc(&engine->hasException) + , hadException(false) + { std::swap(*hasExceptionLoc, hadException); } + + ~ExceptionStateSaver() + { std::swap(*hasExceptionLoc, hadException); } + }; + + // if we wouldn't do this, the putIndexed won't work. + ExceptionStateSaver resetExceptionState(engine()); + QV4::Scope scope(engine()); + QV4::ScopedObject array(scope, values.value()); + if (deduplicate) { + for (Ref i = 0; i < array->getLength(); ++i) { + if (array->getIndexed(i) == value.rawValue()) + return i; + } + } + Ref ref = array->getLength(); + array->putIndexed(ref, value); + Q_ASSERT(array->getLength() - 1 == ref); + return ref; +} + +QV4::ReturnedValue QV4DataCollector::getValue(Ref ref) +{ + QV4::Scope scope(engine()); + QV4::ScopedObject array(scope, values.value()); + Q_ASSERT(ref < array->getLength()); + return array->getIndexed(ref, Q_NULLPTR); +} + +bool QV4DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict) +{ + SpecialRefs::const_iterator it = specialRefs.find(ref); + if (it == specialRefs.end()) + return false; + + *dict = it.value(); + return true; +} + +QJsonArray QV4DataCollector::collectProperties(QV4::Object *object) +{ + QJsonArray res; + + QV4::Scope scope(engine()); + QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly); + QV4::ScopedValue name(scope); + QV4::ScopedValue value(scope); + while (true) { + QV4::Value v; + name = it.nextPropertyNameAsString(&v); + if (name->isNull()) + break; + QString key = name->toQStringNoThrow(); + value = v; + res.append(collectAsJson(key, value)); + } + + return res; +} + +QJsonObject QV4DataCollector::collectAsJson(const QString &name, const QV4::ScopedValue &value) +{ + QJsonObject dict; + if (!name.isNull()) + dict.insert(QStringLiteral("name"), name); + Ref ref = addRef(value); + dict.insert(QStringLiteral("ref"), qint64(ref)); + if (m_collectedRefs) + m_collectedRefs->append(ref); + + // TODO: enable this when creator can handle it. + if (false) { + if (value->isManaged() && !value->isString()) { + QV4::Scope scope(engine()); + QV4::ScopedObject obj(scope, value->as<QV4::Object>()); + dict.insert(QStringLiteral("propertycount"), qint64(obj->getLength())); + } + } + + return dict; +} + +ExpressionEvalJob::ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, + const QString &expression, + QV4DataCollector *collector) + : JavaScriptJob(engine, frameNr, expression) + , collector(collector) +{ +} + +void ExpressionEvalJob::handleResult(QV4::ScopedValue &result) +{ + collector->collect(result); +} + +GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine, int seq) + : engine(engine) + , seq(seq) +{} + +void GatherSourcesJob::run() +{ + QStringList sources; + + foreach (QV4::CompiledData::CompilationUnit *unit, engine->compilationUnits) { + QString fileName = unit->fileName(); + if (!fileName.isEmpty()) + sources.append(fileName); + } + + QV4::Debugging::Debugger *debugger = engine->debugger; + emit debugger->sourcesCollected(debugger, sources, seq); +} + +ArgumentCollectJob::ArgumentCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, + QStringList *names, int frameNr, int scopeNr) + : engine(engine) + , collector(collector) + , names(names) + , frameNr(frameNr) + , scopeNr(scopeNr) +{} + +void ArgumentCollectJob::run() +{ + if (frameNr < 0) + return; + + QV4::Scope scope(engine); + QV4::Scoped<QV4::CallContext> ctxt( + scope, QV4DataCollector::findScope( + QV4DataCollector::findContext(engine->currentContext(), frameNr), scopeNr)); + if (!ctxt) + return; + + QV4::ScopedValue v(scope); + int nFormals = ctxt->formalCount(); + for (unsigned i = 0, ei = nFormals; i != ei; ++i) { + QString qName; + if (QV4::Identifier *name = ctxt->formals()[nFormals - i - 1]) + qName = name->string; + names->append(qName); + v = ctxt->argument(i); + collector->collect(v); + } +} + +LocalCollectJob::LocalCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, + QStringList *names, int frameNr, int scopeNr) + : engine(engine) + , collector(collector) + , names(names) + , frameNr(frameNr) + , scopeNr(scopeNr) +{} + +void LocalCollectJob::run() +{ + if (frameNr < 0) + return; + + QV4::Scope scope(engine); + QV4::Scoped<QV4::CallContext> ctxt( + scope, QV4DataCollector::findScope( + QV4DataCollector::findContext(engine->currentContext(), frameNr), scopeNr)); + if (!ctxt) + return; + + QV4::ScopedValue v(scope); + for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) { + QString qName; + if (QV4::Identifier *name = ctxt->variables()[i]) + qName = name->string; + names->append(qName); + v = ctxt->d()->locals[i]; + collector->collect(v); + } +} + +ThisCollectJob::ThisCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, + int frameNr, bool *foundThis) + : engine(engine) + , collector(collector) + , frameNr(frameNr) + , foundThis(foundThis) +{} + +void ThisCollectJob::run() +{ + *foundThis = myRun(); +} + +bool ThisCollectJob::myRun() +{ + QV4::Scope scope(engine); + QV4::ScopedContext ctxt(scope, QV4DataCollector::findContext(engine->currentContext(), + frameNr)); + while (ctxt) { + if (QV4::CallContext *cCtxt = ctxt->asCallContext()) + if (cCtxt->d()->activation) + break; + ctxt = ctxt->d()->outer; + } + + if (!ctxt) + return false; + + QV4::ScopedValue o(scope, ctxt->asCallContext()->d()->activation); + collector->collect(o); + return true; +} + +ExceptionCollectJob::ExceptionCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector) + : engine(engine) + , collector(collector) +{} + +void ExceptionCollectJob::run() +{ + QV4::Scope scope(engine); + QV4::ScopedValue v(scope, *engine->exceptionValue); + collector->collect(v); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h new file mode 100644 index 0000000000..c91b77cb93 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4DATACOLLECTOR_H +#define QV4DATACOLLECTOR_H + +#include <private/qv4engine_p.h> +#include <private/qv4debugging_p.h> + +QT_BEGIN_NAMESPACE + +class QV4DataCollector +{ +public: + typedef uint Ref; + typedef QVector<uint> Refs; + + static QV4::Heap::CallContext *findContext(QV4::Heap::ExecutionContext *ctxt, int frame); + static QV4::Heap::CallContext *findScope(QV4::Heap::ExecutionContext *ctxt, int scope); + static QVector<QV4::Heap::ExecutionContext::ContextType> getScopeTypes( + QV4::ExecutionEngine *engine, int frame); + + QV4DataCollector(QV4::ExecutionEngine *engine); + ~QV4DataCollector(); + + void collect(const QV4::ScopedValue &value); + + QJsonObject lookupRef(Ref ref); + + Ref addFunctionRef(const QString &functionName); + Ref addScriptRef(const QString &scriptName); + + void collectScope(QJsonObject *dict, QV4::Debugging::Debugger *debugger, int frameNr, + int scopeNr); + + QV4::ExecutionEngine *engine() const { return m_engine; } + +private: + friend class RefHolder; + + Ref addRef(QV4::Value value, bool deduplicate = true); + QV4::ReturnedValue getValue(Ref ref); + bool lookupSpecialRef(Ref ref, QJsonObject *dict); + + QJsonArray collectProperties(QV4::Object *object); + QJsonObject collectAsJson(const QString &name, const QV4::ScopedValue &value); + void collectArgumentsInContext(); + + QV4::ExecutionEngine *m_engine; + Refs *m_collectedRefs; + QV4::PersistentValue values; + typedef QHash<Ref, QJsonObject> SpecialRefs; + SpecialRefs specialRefs; +}; + +class RefHolder { +public: + RefHolder(QV4DataCollector *collector, QV4DataCollector::Refs *target) : + m_collector(collector), m_previousRefs(collector->m_collectedRefs) + { + m_collector->m_collectedRefs = target; + } + + ~RefHolder() + { + std::swap(m_collector->m_collectedRefs, m_previousRefs); + } + +private: + QV4DataCollector *m_collector; + QV4DataCollector::Refs *m_previousRefs; +}; + +class ExpressionEvalJob: public QV4::Debugging::Debugger::JavaScriptJob +{ + QV4DataCollector *collector; + +public: + ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, const QString &expression, + QV4DataCollector *collector); + virtual void handleResult(QV4::ScopedValue &result); +}; + +class GatherSourcesJob: public QV4::Debugging::Debugger::Job +{ + QV4::ExecutionEngine *engine; + const int seq; + +public: + GatherSourcesJob(QV4::ExecutionEngine *engine, int seq); + void run(); +}; + +class ArgumentCollectJob: public QV4::Debugging::Debugger::Job +{ + QV4::ExecutionEngine *engine; + QV4DataCollector *collector; + QStringList *names; + int frameNr; + int scopeNr; + +public: + ArgumentCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, + QStringList *names, int frameNr, int scopeNr); + void run(); +}; + +class LocalCollectJob: public QV4::Debugging::Debugger::Job +{ + QV4::ExecutionEngine *engine; + QV4DataCollector *collector; + QStringList *names; + int frameNr; + int scopeNr; + +public: + LocalCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, QStringList *names, + int frameNr, int scopeNr); + void run(); +}; + +class ThisCollectJob: public QV4::Debugging::Debugger::Job +{ + QV4::ExecutionEngine *engine; + QV4DataCollector *collector; + int frameNr; + bool *foundThis; + +public: + ThisCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, int frameNr, + bool *foundThis); + void run(); + bool myRun(); +}; + +class ExceptionCollectJob: public QV4::Debugging::Debugger::Job +{ + QV4::ExecutionEngine *engine; + QV4DataCollector *collector; + +public: + ExceptionCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector); + void run(); +}; + +QT_END_NAMESPACE + +#endif // QV4DATACOLLECTOR_H diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp new file mode 100644 index 0000000000..7f22b16e45 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4debuggeragent.h" +#include "qv4debugservice.h" +#include "qv4datacollector.h" + +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonarray.h> + +QT_BEGIN_NAMESPACE + +QV4DebuggerAgent::QV4DebuggerAgent(QV4DebugServiceImpl *debugService) + : m_breakOnThrow(false), m_debugService(debugService) +{} + +QV4::Debugging::Debugger *QV4DebuggerAgent::firstDebugger() const +{ + // Currently only 1 single engine is supported, so: + if (m_debuggers.isEmpty()) + return 0; + else + return m_debuggers.first(); +} + +bool QV4DebuggerAgent::isRunning() const +{ + // Currently only 1 single engine is supported, so: + if (QV4::Debugging::Debugger *debugger = firstDebugger()) + return debugger->state() == QV4::Debugging::Debugger::Running; + else + return false; +} + +void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::Debugger *debugger, + QV4::Debugging::PauseReason reason) +{ + Q_UNUSED(reason); + + m_debugService->clearHandles(debugger->engine()); + + QJsonObject event, body, script; + event.insert(QStringLiteral("type"), QStringLiteral("event")); + + switch (reason) { + case QV4::Debugging::Step: + case QV4::Debugging::PauseRequest: + case QV4::Debugging::BreakPoint: { + event.insert(QStringLiteral("event"), QStringLiteral("break")); + QVector<QV4::StackFrame> frames = debugger->stackTrace(1); + if (frames.isEmpty()) + break; + + const QV4::StackFrame &topFrame = frames.first(); + body.insert(QStringLiteral("invocationText"), topFrame.function); + body.insert(QStringLiteral("sourceLine"), topFrame.line - 1); + if (topFrame.column > 0) + body.insert(QStringLiteral("sourceColumn"), topFrame.column); + QJsonArray breakPoints; + foreach (int breakPointId, breakPointIds(topFrame.source, topFrame.line)) + breakPoints.push_back(breakPointId); + body.insert(QStringLiteral("breakpoints"), breakPoints); + script.insert(QStringLiteral("name"), topFrame.source); + } break; + case QV4::Debugging::Throwing: + // TODO: complete this! + event.insert(QStringLiteral("event"), QStringLiteral("exception")); + break; + } + + if (!script.isEmpty()) + body.insert(QStringLiteral("script"), script); + if (!body.isEmpty()) + event.insert(QStringLiteral("body"), body); + m_debugService->send(event); +} + +void QV4DebuggerAgent::sourcesCollected(QV4::Debugging::Debugger *debugger, + const QStringList &sources, int requestSequenceNr) +{ + QJsonArray body; + foreach (const QString &source, sources) { + QJsonObject src; + src[QLatin1String("name")] = source; + src[QLatin1String("scriptType")] = 4; + body.append(src); + } + + QJsonObject response; + response[QLatin1String("success")] = true; + response[QLatin1String("running")] = debugger->state() == QV4::Debugging::Debugger::Running; + response[QLatin1String("body")] = body; + response[QLatin1String("command")] = QStringLiteral("scripts"); + response[QLatin1String("request_seq")] = requestSequenceNr; + response[QLatin1String("type")] = QStringLiteral("response"); + m_debugService->send(response); +} + +void QV4DebuggerAgent::addDebugger(QV4::Debugging::Debugger *debugger) +{ + Q_ASSERT(!m_debuggers.contains(debugger)); + m_debuggers << debugger; + + debugger->setBreakOnThrow(m_breakOnThrow); + + foreach (const BreakPoint &breakPoint, m_breakPoints.values()) + if (breakPoint.enabled) + debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition); + + connect(debugger, SIGNAL(destroyed(QObject*)), + this, SLOT(handleDebuggerDeleted(QObject*))); + connect(debugger, SIGNAL(sourcesCollected(QV4::Debugging::Debugger*,QStringList,int)), + this, SLOT(sourcesCollected(QV4::Debugging::Debugger*,QStringList,int)), + Qt::QueuedConnection); + connect(debugger, SIGNAL(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)), + this, SLOT(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)), + Qt::QueuedConnection); +} + +void QV4DebuggerAgent::removeDebugger(QV4::Debugging::Debugger *debugger) +{ + m_debuggers.removeAll(debugger); + disconnect(debugger, SIGNAL(destroyed(QObject*)), + this, SLOT(handleDebuggerDeleted(QObject*))); + disconnect(debugger, SIGNAL(sourcesCollected(QV4::Debugging::Debugger*,QStringList,int)), + this, SLOT(sourcesCollected(QV4::Debugging::Debugger*,QStringList,int))); + disconnect(debugger, + SIGNAL(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)), + this, + SLOT(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason))); +} + +void QV4DebuggerAgent::handleDebuggerDeleted(QObject *debugger) +{ + m_debuggers.removeAll(static_cast<QV4::Debugging::Debugger *>(debugger)); +} + +void QV4DebuggerAgent::pause(QV4::Debugging::Debugger *debugger) const +{ + debugger->pause(); +} + +void QV4DebuggerAgent::pauseAll() const +{ + foreach (QV4::Debugging::Debugger *debugger, m_debuggers) + pause(debugger); +} + +void QV4DebuggerAgent::resumeAll() const +{ + foreach (QV4::Debugging::Debugger *debugger, m_debuggers) + if (debugger->state() == QV4::Debugging::Debugger::Paused) + debugger->resume(QV4::Debugging::Debugger::FullThrottle); +} + +int QV4DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, bool enabled, const QString &condition) +{ + if (enabled) + foreach (QV4::Debugging::Debugger *debugger, m_debuggers) + debugger->addBreakPoint(fileName, lineNumber, condition); + + int id = m_breakPoints.size(); + m_breakPoints.insert(id, BreakPoint(fileName, lineNumber, enabled, condition)); + return id; +} + +void QV4DebuggerAgent::removeBreakPoint(int id) +{ + BreakPoint breakPoint = m_breakPoints.value(id); + if (!breakPoint.isValid()) + return; + + m_breakPoints.remove(id); + + if (breakPoint.enabled) + foreach (QV4::Debugging::Debugger *debugger, m_debuggers) + debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr); +} + +void QV4DebuggerAgent::removeAllBreakPoints() +{ + QList<int> ids = m_breakPoints.keys(); + foreach (int id, ids) + removeBreakPoint(id); +} + +void QV4DebuggerAgent::enableBreakPoint(int id, bool onoff) +{ + BreakPoint &breakPoint = m_breakPoints[id]; + if (!breakPoint.isValid() || breakPoint.enabled == onoff) + return; + breakPoint.enabled = onoff; + + foreach (QV4::Debugging::Debugger *debugger, m_debuggers) { + if (onoff) + debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition); + else + debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr); + } +} + +QList<int> QV4DebuggerAgent::breakPointIds(const QString &fileName, int lineNumber) const +{ + QList<int> ids; + + for (QHash<int, BreakPoint>::const_iterator i = m_breakPoints.begin(), ei = m_breakPoints.end(); i != ei; ++i) + if (i->lineNr == lineNumber && fileName.endsWith(i->fileName)) + ids.push_back(i.key()); + + return ids; +} + +void QV4DebuggerAgent::setBreakOnThrow(bool onoff) +{ + if (onoff != m_breakOnThrow) { + m_breakOnThrow = onoff; + foreach (QV4::Debugging::Debugger *debugger, m_debuggers) + debugger->setBreakOnThrow(onoff); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h new file mode 100644 index 0000000000..6126eea772 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4DEBUGGERAGENT_H +#define QV4DEBUGGERAGENT_H + +#include <private/qv4debugging_p.h> + +QT_BEGIN_NAMESPACE + +class QV4DebugServiceImpl; + +class QV4DebuggerAgent : public QObject +{ + Q_OBJECT +public: + QV4DebuggerAgent(QV4DebugServiceImpl *m_debugService); + + QV4::Debugging::Debugger *firstDebugger() const; + bool isRunning() const; + + void addDebugger(QV4::Debugging::Debugger *debugger); + void removeDebugger(QV4::Debugging::Debugger *debugger); + + void pause(QV4::Debugging::Debugger *debugger) const; + void pauseAll() const; + void resumeAll() const; + int addBreakPoint(const QString &fileName, int lineNumber, bool enabled = true, const QString &condition = QString()); + void removeBreakPoint(int id); + void removeAllBreakPoints(); + void enableBreakPoint(int id, bool onoff); + QList<int> breakPointIds(const QString &fileName, int lineNumber) const; + + bool breakOnThrow() const { return m_breakOnThrow; } + void setBreakOnThrow(bool onoff); + +public slots: + void debuggerPaused(QV4::Debugging::Debugger *debugger, QV4::Debugging::PauseReason reason); + void sourcesCollected(QV4::Debugging::Debugger *debugger, const QStringList &sources, + int requestSequenceNr); + void handleDebuggerDeleted(QObject *debugger); + +private: + QList<QV4::Debugging::Debugger *> m_debuggers; + + struct BreakPoint { + QString fileName; + int lineNr; + bool enabled; + QString condition; + + BreakPoint(): lineNr(-1), enabled(false) {} + BreakPoint(const QString &fileName, int lineNr, bool enabled, const QString &condition) + : fileName(fileName), lineNr(lineNr), enabled(enabled), condition(condition) + {} + + bool isValid() const { return lineNr >= 0 && !fileName.isEmpty(); } + }; + + QHash<int, BreakPoint> m_breakPoints; + bool m_breakOnThrow; + QV4DebugServiceImpl *m_debugService; +}; + +QT_END_NAMESPACE + +#endif // QV4DEBUGGERAGENT_H diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp new file mode 100644 index 0000000000..6b68f9518e --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -0,0 +1,907 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4debugservice.h" +#include "qqmlengine.h" +#include <private/qv4engine_p.h> +#include <private/qv4function_p.h> +#include <private/qqmldebugconnector_p.h> + +#include <private/qv8engine_p.h> + +#include <QtCore/QJsonArray> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonObject> +#include <QtCore/QJsonValue> + +const char *const V4_CONNECT = "connect"; +const char *const V4_DISCONNECT = "disconnect"; +const char *const V4_BREAK_ON_SIGNAL = "breakonsignal"; +const char *const V4_PAUSE = "interrupt"; + +#define NO_PROTOCOL_TRACING +#ifdef NO_PROTOCOL_TRACING +# define TRACE_PROTOCOL(x) +#else +#include <QtCore/QDebug> +# define TRACE_PROTOCOL(x) x +#endif + +QT_BEGIN_NAMESPACE + +class V8CommandHandler; +class UnknownV8CommandHandler; + +int QV4DebugServiceImpl::debuggerIndex = 0; +int QV4DebugServiceImpl::sequence = 0; + +class V8CommandHandler +{ +public: + V8CommandHandler(const QString &command) + : cmd(command) + {} + + virtual ~V8CommandHandler() + {} + + QString command() const { return cmd; } + + void handle(const QJsonObject &request, QV4DebugServiceImpl *s) + { + TRACE_PROTOCOL(qDebug() << "handling command" << command() << "..."); + + req = request; + seq = req.value(QStringLiteral("seq")); + debugService = s; + + handleRequest(); + if (!response.isEmpty()) { + response[QLatin1String("type")] = QStringLiteral("response"); + debugService->send(response); + } + + debugService = 0; + seq = QJsonValue(); + req = QJsonObject(); + response = QJsonObject(); + } + + virtual void handleRequest() = 0; + +protected: + void addCommand() { response.insert(QStringLiteral("command"), cmd); } + void addRequestSequence() { response.insert(QStringLiteral("request_seq"), seq); } + void addSuccess(bool success) { response.insert(QStringLiteral("success"), success); } + void addBody(const QJsonObject &body) + { + response.insert(QStringLiteral("body"), body); + } + + void addRunning() + { + response.insert(QStringLiteral("running"), debugService->debuggerAgent.isRunning()); + } + + void addRefs() + { + response.insert(QStringLiteral("refs"), debugService->buildRefs()); + } + + void createErrorResponse(const QString &msg) + { + QJsonValue command = req.value(QStringLiteral("command")); + response.insert(QStringLiteral("command"), command); + addRequestSequence(); + addSuccess(false); + addRunning(); + response.insert(QStringLiteral("message"), msg); + } + + int requestSequenceNr() const + { return seq.toInt(-1); } + +protected: + QString cmd; + QJsonObject req; + QJsonValue seq; + QV4DebugServiceImpl *debugService; + QJsonObject response; +}; + +class UnknownV8CommandHandler: public V8CommandHandler +{ +public: + UnknownV8CommandHandler(): V8CommandHandler(QString()) {} + + virtual void handleRequest() + { + QString msg = QStringLiteral("unimplemented command \""); + msg += req.value(QStringLiteral("command")).toString(); + msg += QStringLiteral("\""); + createErrorResponse(msg); + } +}; + +namespace { +class V8VersionRequest: public V8CommandHandler +{ +public: + V8VersionRequest(): V8CommandHandler(QStringLiteral("version")) {} + + virtual void handleRequest() + { + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + QJsonObject body; + body.insert(QStringLiteral("V8Version"), + QLatin1String("this is not V8, this is V4 in Qt " QT_VERSION_STR)); + addBody(body); + } +}; + +class V8SetBreakPointRequest: public V8CommandHandler +{ +public: + V8SetBreakPointRequest(): V8CommandHandler(QStringLiteral("setbreakpoint")) {} + + virtual void handleRequest() + { + // decypher the payload: + QJsonObject args = req.value(QStringLiteral("arguments")).toObject(); + if (args.isEmpty()) + return; + + QString type = args.value(QStringLiteral("type")).toString(); + if (type != QStringLiteral("scriptRegExp")) { + createErrorResponse(QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type)); + return; + } + + QString fileName = args.value(QStringLiteral("target")).toString(); + if (fileName.isEmpty()) { + createErrorResponse(QStringLiteral("breakpoint has no file name")); + return; + } + + int line = args.value(QStringLiteral("line")).toInt(-1); + if (line < 0) { + createErrorResponse(QStringLiteral("breakpoint has an invalid line number")); + return; + } + + bool enabled = args.value(QStringLiteral("enabled")).toBool(true); + QString condition = args.value(QStringLiteral("condition")).toString(); + + // set the break point: + int id = debugService->debuggerAgent.addBreakPoint(fileName, line + 1, enabled, condition); + + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + QJsonObject body; + body.insert(QStringLiteral("type"), type); + body.insert(QStringLiteral("breakpoint"), id); + // It's undocumented, but V8 sends back an actual_locations array too. However, our + // Debugger currently doesn't tell us when it resolved a breakpoint, so we'll leave them + // pending until the breakpoint is hit for the first time. + addBody(body); + } +}; + +class V8ClearBreakPointRequest: public V8CommandHandler +{ +public: + V8ClearBreakPointRequest(): V8CommandHandler(QStringLiteral("clearbreakpoint")) {} + + virtual void handleRequest() + { + // decypher the payload: + QJsonObject args = req.value(QStringLiteral("arguments")).toObject(); + if (args.isEmpty()) + return; + + int id = args.value(QStringLiteral("breakpoint")).toInt(-1); + if (id < 0) { + createErrorResponse(QStringLiteral("breakpoint has an invalid number")); + return; + } + + // remove the break point: + debugService->debuggerAgent.removeBreakPoint(id); + + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + QJsonObject body; + body.insert(QStringLiteral("type"), QStringLiteral("scriptRegExp")); + body.insert(QStringLiteral("breakpoint"), id); + addBody(body); + } +}; + +class V8BacktraceRequest: public V8CommandHandler +{ +public: + V8BacktraceRequest(): V8CommandHandler(QStringLiteral("backtrace")) {} + + virtual void handleRequest() + { + // decypher the payload: + + QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + int fromFrame = arguments.value(QStringLiteral("fromFrame")).toInt(0); + int toFrame = arguments.value(QStringLiteral("toFrame")).toInt(fromFrame + 10); + // no idea what the bottom property is for, so we'll ignore it. + + QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + + QJsonArray frameArray; + QVector<QV4::StackFrame> frames = debugger->stackTrace(toFrame); + for (int i = fromFrame; i < toFrame && i < frames.size(); ++i) + frameArray.push_back(debugService->buildFrame(frames[i], i, debugger)); + + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + QJsonObject body; + if (frameArray.isEmpty()) { + body.insert(QStringLiteral("totalFrames"), 0); + } else { + body.insert(QStringLiteral("fromFrame"), fromFrame); + body.insert(QStringLiteral("toFrame"), fromFrame + frameArray.size()); + body.insert(QStringLiteral("frames"), frameArray); + } + addBody(body); + addRefs(); + } +}; + +class V8FrameRequest: public V8CommandHandler +{ +public: + V8FrameRequest(): V8CommandHandler(QStringLiteral("frame")) {} + + virtual void handleRequest() + { + // decypher the payload: + QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + const int frameNr = arguments.value(QStringLiteral("number")).toInt( + debugService->selectedFrame()); + + QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + QVector<QV4::StackFrame> frames = debugger->stackTrace(frameNr + 1); + if (frameNr < 0 || frameNr >= frames.size()) { + createErrorResponse(QStringLiteral("frame command has invalid frame number")); + return; + } + + debugService->selectFrame(frameNr); + QJsonObject frame = debugService->buildFrame(frames[frameNr], frameNr, debugger); + + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + addBody(frame); + addRefs(); + } +}; + +class V8ScopeRequest: public V8CommandHandler +{ +public: + V8ScopeRequest(): V8CommandHandler(QStringLiteral("scope")) {} + + virtual void handleRequest() + { + // decypher the payload: + QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + const int frameNr = arguments.value(QStringLiteral("frameNumber")).toInt( + debugService->selectedFrame()); + const int scopeNr = arguments.value(QStringLiteral("number")).toInt(0); + + QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + QVector<QV4::StackFrame> frames = debugger->stackTrace(frameNr + 1); + if (frameNr < 0 || frameNr >= frames.size()) { + createErrorResponse(QStringLiteral("scope command has invalid frame number")); + return; + } + if (scopeNr < 0) { + createErrorResponse(QStringLiteral("scope command has invalid scope number")); + return; + } + + QJsonObject scope = debugService->buildScope(frameNr, scopeNr, debugger); + + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + addBody(scope); + addRefs(); + } +}; + +class V8LookupRequest: public V8CommandHandler +{ +public: + V8LookupRequest(): V8CommandHandler(QStringLiteral("lookup")) {} + + virtual void handleRequest() + { + // decypher the payload: + QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + QJsonArray handles = arguments.value(QStringLiteral("handles")).toArray(); + + QJsonObject body; + foreach (const QJsonValue &handle, handles) + body[QString::number(handle.toInt())] = debugService->lookup(handle.toInt()); + + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + addBody(body); + addRefs(); + } +}; + +class V8ContinueRequest: public V8CommandHandler +{ +public: + V8ContinueRequest(): V8CommandHandler(QStringLiteral("continue")) {} + + virtual void handleRequest() + { + // decypher the payload: + QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + + QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + + if (arguments.empty()) { + debugger->resume(QV4::Debugging::Debugger::FullThrottle); + } else { + QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + QString stepAction = arguments.value(QStringLiteral("stepaction")).toString(); + const int stepcount = arguments.value(QStringLiteral("stepcount")).toInt(1); + if (stepcount != 1) + qWarning() << "Step count other than 1 is not supported."; + + if (stepAction == QStringLiteral("in")) { + debugger->resume(QV4::Debugging::Debugger::StepIn); + } else if (stepAction == QStringLiteral("out")) { + debugger->resume(QV4::Debugging::Debugger::StepOut); + } else if (stepAction == QStringLiteral("next")) { + debugger->resume(QV4::Debugging::Debugger::StepOver); + } else { + createErrorResponse(QStringLiteral("continue command has invalid stepaction")); + return; + } + } + + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + } +}; + +class V8DisconnectRequest: public V8CommandHandler +{ +public: + V8DisconnectRequest(): V8CommandHandler(QStringLiteral("disconnect")) {} + + virtual void handleRequest() + { + debugService->debuggerAgent.removeAllBreakPoints(); + debugService->debuggerAgent.resumeAll(); + + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + } +}; + +class V8SetExceptionBreakRequest: public V8CommandHandler +{ +public: + V8SetExceptionBreakRequest(): V8CommandHandler(QStringLiteral("setexceptionbreak")) {} + + virtual void handleRequest() + { + bool wasEnabled = debugService->debuggerAgent.breakOnThrow(); + + //decypher the payload: + QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + QString type = arguments.value(QStringLiteral("type")).toString(); + bool enabled = arguments.value(QStringLiteral("number")).toBool(!wasEnabled); + + if (type == QStringLiteral("all")) { + // that's fine + } else if (type == QStringLiteral("uncaught")) { + createErrorResponse(QStringLiteral("breaking only on uncaught exceptions is not supported yet")); + return; + } else { + createErrorResponse(QStringLiteral("invalid type for break on exception")); + return; + } + + // do it: + debugService->debuggerAgent.setBreakOnThrow(enabled); + + QJsonObject body; + body[QLatin1String("type")] = type; + body[QLatin1String("enabled")] = debugService->debuggerAgent.breakOnThrow(); + + // response: + addBody(body); + addRunning(); + addSuccess(true); + addRequestSequence(); + addCommand(); + } +}; + +class V8ScriptsRequest: public V8CommandHandler +{ +public: + V8ScriptsRequest(): V8CommandHandler(QStringLiteral("scripts")) {} + + virtual void handleRequest() + { + //decypher the payload: + QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + int types = arguments.value(QStringLiteral("types")).toInt(-1); + if (types < 0 || types > 7) { + createErrorResponse(QStringLiteral("invalid types value in scripts command")); + return; + } else if (types != 4) { + createErrorResponse(QStringLiteral("unsupported types value in scripts command")); + return; + } + + // do it: + QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + GatherSourcesJob job(debugger->engine(), requestSequenceNr()); + debugger->runInEngine(&job); + + // response will be send by + } +}; + +// Request: +// { +// "seq": 4, +// "type": "request", +// "command": "evaluate", +// "arguments": { +// "expression": "a", +// "frame": 0 +// } +// } +// +// Response: +// { +// "body": { +// "handle": 3, +// "type": "number", +// "value": 1 +// }, +// "command": "evaluate", +// "refs": [], +// "request_seq": 4, +// "running": false, +// "seq": 5, +// "success": true, +// "type": "response" +// } +// +// The "value" key in "body" is the result of evaluating the expression in the request. +class V8EvaluateRequest: public V8CommandHandler +{ +public: + V8EvaluateRequest(): V8CommandHandler(QStringLiteral("evaluate")) {} + + virtual void handleRequest() + { + //decypher the payload: + QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + QString expression = arguments.value(QStringLiteral("expression")).toString(); + const int frame = arguments.value(QStringLiteral("frame")).toInt(0); + + QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger(); + Q_ASSERT(debugger->state() == QV4::Debugging::Debugger::Paused); + + QV4DataCollector *collector = debugService->collector(); + QV4DataCollector::Refs refs; + RefHolder holder(collector, &refs); + Q_ASSERT(debugger->state() == QV4::Debugging::Debugger::Paused); + + ExpressionEvalJob job(debugger->engine(), frame, expression, collector); + debugger->runInEngine(&job); + + Q_ASSERT(refs.size() == 1); + + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + addBody(collector->lookupRef(refs.first())); + addRefs(); + } +}; +} // anonymous namespace + +void QV4DebugServiceImpl::addHandler(V8CommandHandler* handler) +{ + handlers[handler->command()] = handler; +} + +V8CommandHandler *QV4DebugServiceImpl::v8CommandHandler(const QString &command) const +{ + V8CommandHandler *handler = handlers.value(command, 0); + if (handler) + return handler; + else + return unknownV8CommandHandler.data(); +} + +QV4DebugServiceImpl::QV4DebugServiceImpl(QObject *parent) : + QQmlConfigurableDebugService<QV4DebugService>(1, parent), + debuggerAgent(this), version(1), theSelectedFrame(0), + unknownV8CommandHandler(new UnknownV8CommandHandler) +{ + addHandler(new V8VersionRequest); + addHandler(new V8SetBreakPointRequest); + addHandler(new V8ClearBreakPointRequest); + addHandler(new V8BacktraceRequest); + addHandler(new V8FrameRequest); + addHandler(new V8ScopeRequest); + addHandler(new V8LookupRequest); + addHandler(new V8ContinueRequest); + addHandler(new V8DisconnectRequest); + addHandler(new V8SetExceptionBreakRequest); + addHandler(new V8ScriptsRequest); + addHandler(new V8EvaluateRequest); +} + +QV4DebugServiceImpl::~QV4DebugServiceImpl() +{ + qDeleteAll(handlers); +} + +void QV4DebugServiceImpl::engineAboutToBeAdded(QQmlEngine *engine) +{ + QMutexLocker lock(&m_configMutex); + if (engine) { + QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); + if (QQmlDebugConnector *server = QQmlDebugConnector::instance()) { + if (ee) { + ee->enableDebugger(); + QV4::Debugging::Debugger *debugger = ee->debugger; + debuggerMap.insert(debuggerIndex++, debugger); + debuggerAgent.addDebugger(debugger); + debuggerAgent.moveToThread(server->thread()); + } + } + } + QQmlConfigurableDebugService<QV4DebugService>::engineAboutToBeAdded(engine); +} + +void QV4DebugServiceImpl::engineAboutToBeRemoved(QQmlEngine *engine) +{ + QMutexLocker lock(&m_configMutex); + if (engine){ + const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); + if (ee) { + QV4::Debugging::Debugger *debugger = ee->debugger; + typedef QMap<int, QV4::Debugging::Debugger *>::const_iterator DebuggerMapIterator; + const DebuggerMapIterator end = debuggerMap.constEnd(); + for (DebuggerMapIterator i = debuggerMap.constBegin(); i != end; ++i) { + if (i.value() == debugger) { + debuggerMap.remove(i.key()); + break; + } + } + debuggerAgent.removeDebugger(debugger); + } + } + QQmlConfigurableDebugService<QV4DebugService>::engineAboutToBeRemoved(engine); +} + +void QV4DebugServiceImpl::signalEmitted(const QString &signal) +{ + //This function is only called by QQmlBoundSignal + //only if there is a slot connected to the signal. Hence, there + //is no need for additional check. + + //Parse just the name and remove the class info + //Normalize to Lower case. + QString signalName = signal.left(signal.indexOf(QLatin1Char('('))).toLower(); + + foreach (const QString &signal, breakOnSignals) { + if (signal == signalName) { + // TODO: pause debugger + break; + } + } +} + +void QV4DebugServiceImpl::messageReceived(const QByteArray &message) +{ + QMutexLocker lock(&m_configMutex); + + QQmlDebugStream ms(message); + QByteArray header; + ms >> header; + + TRACE_PROTOCOL(qDebug() << "received message with header" << header); + + if (header == "V8DEBUG") { + QByteArray type; + QByteArray payload; + ms >> type >> payload; + TRACE_PROTOCOL(qDebug() << "... type:" << type); + + if (type == V4_CONNECT) { + emit messageToClient(name(), packMessage(type)); + stopWaiting(); + } else if (type == V4_PAUSE) { + debuggerAgent.pauseAll(); + sendSomethingToSomebody(type); + } else if (type == V4_BREAK_ON_SIGNAL) { + QByteArray signal; + bool enabled; + ms >> signal >> enabled; + //Normalize to lower case. + QString signalName(QString::fromUtf8(signal).toLower()); + if (enabled) + breakOnSignals.append(signalName); + else + breakOnSignals.removeOne(signalName); + } else if (type == "v8request") { + handleV8Request(payload); + } else if (type == V4_DISCONNECT) { + TRACE_PROTOCOL(qDebug() << "... payload:" << payload.constData()); + handleV8Request(payload); + } else { + sendSomethingToSomebody(type, 0); + } + } +} + +void QV4DebugServiceImpl::sendSomethingToSomebody(const char *type, int magicNumber) +{ + QByteArray response; + QQmlDebugStream rs(&response, QIODevice::WriteOnly); + rs << QByteArray(type) + << QByteArray::number(version) << QByteArray::number(magicNumber); + emit messageToClient(name(), packMessage(type, response)); +} + +void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload) +{ + TRACE_PROTOCOL(qDebug() << "v8request, payload:" << payload.constData()); + + QJsonDocument request = QJsonDocument::fromJson(payload); + QJsonObject o = request.object(); + QJsonValue type = o.value(QStringLiteral("type")); + if (type.toString() == QStringLiteral("request")) { + QJsonValue command = o.value(QStringLiteral("command")); + V8CommandHandler *h = v8CommandHandler(command.toString()); + if (h) + h->handle(o, this); + } +} + +QByteArray QV4DebugServiceImpl::packMessage(const QByteArray &command, const QByteArray &message) +{ + QByteArray reply; + QQmlDebugStream rs(&reply, QIODevice::WriteOnly); + static const QByteArray cmd("V8DEBUG"); + rs << cmd << command << message; + return reply; +} + +void QV4DebugServiceImpl::send(QJsonObject v8Payload) +{ + v8Payload[QLatin1String("seq")] = sequence++; + QJsonDocument doc; + doc.setObject(v8Payload); +#ifdef NO_PROTOCOL_TRACING + QByteArray responseData = doc.toJson(QJsonDocument::Compact); +#else + QByteArray responseData = doc.toJson(QJsonDocument::Indented); +#endif + + TRACE_PROTOCOL(qDebug() << "sending response for:" << responseData.constData() << endl); + + emit messageToClient(name(), packMessage("v8message", responseData)); +} + +void QV4DebugServiceImpl::clearHandles(QV4::ExecutionEngine *engine) +{ + theCollector.reset(new QV4DataCollector(engine)); +} + +QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, int frameNr, + QV4::Debugging::Debugger *debugger) +{ + QV4DataCollector::Ref ref; + + QJsonObject frame; + frame[QLatin1String("index")] = frameNr; + frame[QLatin1String("debuggerFrame")] = false; + ref = theCollector->addFunctionRef(stackFrame.function); + collectedRefs.append(ref); + frame[QLatin1String("func")] = toRef(ref); + ref = theCollector->addScriptRef(stackFrame.source); + collectedRefs.append(ref); + frame[QLatin1String("script")] = toRef(ref); + frame[QLatin1String("line")] = stackFrame.line - 1; + if (stackFrame.column >= 0) + frame[QLatin1String("column")] = stackFrame.column; + + QJsonArray scopes; + if (debugger->state() == QV4::Debugging::Debugger::Paused) { + RefHolder holder(theCollector.data(), &collectedRefs); + bool foundThis = false; + ThisCollectJob job(debugger->engine(), theCollector.data(), frameNr, &foundThis); + debugger->runInEngine(&job); + if (foundThis) + frame[QLatin1String("receiver")] = toRef(collectedRefs.last()); + + // Only type and index are used by Qt Creator, so we keep it easy: + QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes = + QV4DataCollector::getScopeTypes(debugger->engine(), frameNr); + for (int i = 0, ei = scopeTypes.count(); i != ei; ++i) { + int type = encodeScopeType(scopeTypes[i]); + if (type == -1) + continue; + + QJsonObject scope; + scope[QLatin1String("index")] = i; + scope[QLatin1String("type")] = type; + scopes.push_back(scope); + } + } + frame[QLatin1String("scopes")] = scopes; + + return frame; +} + +int QV4DebugServiceImpl::encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType) +{ + switch (scopeType) { + case QV4::Heap::ExecutionContext::Type_GlobalContext: + return 0; + break; + case QV4::Heap::ExecutionContext::Type_CatchContext: + return 4; + break; + case QV4::Heap::ExecutionContext::Type_WithContext: + return 2; + break; + case QV4::Heap::ExecutionContext::Type_SimpleCallContext: + case QV4::Heap::ExecutionContext::Type_CallContext: + return 1; + break; + case QV4::Heap::ExecutionContext::Type_QmlContext: + default: + return -1; + } +} + +QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr, + QV4::Debugging::Debugger *debugger) +{ + QJsonObject scope; + + QJsonObject object; + RefHolder holder(theCollector.data(), &collectedRefs); + theCollector->collectScope(&object, debugger, frameNr, scopeNr); + + if (debugger->state() == QV4::Debugging::Debugger::Paused) { + QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes = + QV4DataCollector::getScopeTypes(debugger->engine(), frameNr); + scope[QLatin1String("type")] = encodeScopeType(scopeTypes[scopeNr]); + } else { + scope[QLatin1String("type")] = -1; + } + scope[QLatin1String("index")] = scopeNr; + scope[QLatin1String("frameIndex")] = frameNr; + scope[QLatin1String("object")] = object; + + return scope; +} + +QJsonValue QV4DebugServiceImpl::lookup(QV4DataCollector::Ref refId) +{ + RefHolder holder(theCollector.data(), &collectedRefs); + return theCollector->lookupRef(refId); +} + +QJsonArray QV4DebugServiceImpl::buildRefs() +{ + QJsonArray refs; + std::sort(collectedRefs.begin(), collectedRefs.end()); + for (int i = 0, ei = collectedRefs.size(); i != ei; ++i) { + QV4DataCollector::Ref ref = collectedRefs.at(i); + if (i > 0 && ref == collectedRefs.at(i - 1)) + continue; + refs.append(lookup(ref)); + } + + collectedRefs.clear(); + return refs; +} + +QJsonValue QV4DebugServiceImpl::toRef(QV4DataCollector::Ref ref) +{ + QJsonObject dict; + dict.insert(QStringLiteral("ref"), qint64(ref)); + return dict; +} + +QV4DataCollector *QV4DebugServiceImpl::collector() const +{ + return theCollector.data(); +} + +void QV4DebugServiceImpl::selectFrame(int frameNr) +{ + theSelectedFrame = frameNr; +} + +int QV4DebugServiceImpl::selectedFrame() const +{ + return theSelectedFrame; +} + +QT_END_NAMESPACE diff --git a/src/qml/debugger/qv4debugservice_p.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h index f7b38b11b6..c80ad78cc8 100644 --- a/src/qml/debugger/qv4debugservice_p.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef QV4DEBUGSERVICE_P_H -#define QV4DEBUGSERVICE_P_H +#ifndef QV4DEBUGSERVICE_H +#define QV4DEBUGSERVICE_H // // W A R N I N G @@ -45,39 +45,81 @@ // We mean it. // -#include "qqmlconfigurabledebugservice_p.h" +#include "qqmlconfigurabledebugservice.h" +#include "qv4debuggeragent.h" +#include "qv4datacollector.h" +#include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qv4debugging_p.h> + +#include <QtCore/QJsonValue> QT_BEGIN_NAMESPACE namespace QV4 { struct ExecutionEngine; } + class QQmlEngine; -class QV4DebugServicePrivate; +class VariableCollector; +class V8CommandHandler; +class UnknownV8CommandHandler; +class QV4DebugServiceImpl; -class QV4DebugService : public QQmlConfigurableDebugService +class QV4DebugServiceImpl : public QQmlConfigurableDebugService<QV4DebugService> { Q_OBJECT public: - explicit QV4DebugService(QObject *parent = 0); - ~QV4DebugService(); + explicit QV4DebugServiceImpl(QObject *parent = 0); + ~QV4DebugServiceImpl(); - static QV4DebugService *instance(); void engineAboutToBeAdded(QQmlEngine *engine); void engineAboutToBeRemoved(QQmlEngine *engine); void signalEmitted(const QString &signal); + void send(QJsonObject v8Payload); + + QJsonObject buildScope(int frameNr, int scopeNr, QV4::Debugging::Debugger *debugger); + QJsonArray buildRefs(); + QJsonValue lookup(QV4DataCollector::Ref refId); + QJsonValue toRef(QV4DataCollector::Ref ref); + + QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr, + QV4::Debugging::Debugger *debugger); + int selectedFrame() const; + void selectFrame(int frameNr); + + void clearHandles(QV4::ExecutionEngine *engine); + + QV4DataCollector *collector() const; + QV4DebuggerAgent debuggerAgent; protected: void messageReceived(const QByteArray &); void sendSomethingToSomebody(const char *type, int magicNumber = 1); private: + friend class QQmlDebuggerServiceFactory; + void handleV8Request(const QByteArray &payload); + static QByteArray packMessage(const QByteArray &command, + const QByteArray &message = QByteArray()); + void processCommand(const QByteArray &command, const QByteArray &data); + V8CommandHandler *v8CommandHandler(const QString &command) const; + int encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType); -private: - Q_DISABLE_COPY(QV4DebugService) - Q_DECLARE_PRIVATE(QV4DebugService) + QStringList breakOnSignals; + QMap<int, QV4::Debugging::Debugger *> debuggerMap; + static int debuggerIndex; + static int sequence; + const int version; + QV4DataCollector::Refs collectedRefs; + + QScopedPointer<QV4DataCollector> theCollector; + int theSelectedFrame; + + void addHandler(V8CommandHandler* handler); + QHash<QString, V8CommandHandler*> handlers; + QScopedPointer<UnknownV8CommandHandler> unknownV8CommandHandler; }; QT_END_NAMESPACE -#endif // QV4DEBUGSERVICE_P_H +#endif // QV4DEBUGSERVICE_H diff --git a/src/plugins/qmltooling/shared/abstracttool.cpp b/src/plugins/qmltooling/qmldbg_inspector/abstracttool.cpp index 990bc704b9..3e059bed13 100644 --- a/src/plugins/qmltooling/shared/abstracttool.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/abstracttool.cpp @@ -35,6 +35,8 @@ #include "abstractviewinspector.h" +QT_BEGIN_NAMESPACE + namespace QmlJSDebugger { AbstractTool::AbstractTool(AbstractViewInspector *inspector) : @@ -44,3 +46,5 @@ AbstractTool::AbstractTool(AbstractViewInspector *inspector) : } } // namespace QmlJSDebugger + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/shared/abstracttool.h b/src/plugins/qmltooling/qmldbg_inspector/abstracttool.h index 85f2b5b9ad..c796925866 100644 --- a/src/plugins/qmltooling/shared/abstracttool.h +++ b/src/plugins/qmltooling/qmldbg_inspector/abstracttool.h @@ -41,7 +41,6 @@ class QMouseEvent; class QKeyEvent; class QWheelEvent; class QTouchEvent; -QT_END_NAMESPACE namespace QmlJSDebugger { @@ -81,4 +80,6 @@ private: } // namespace QmlJSDebugger +QT_END_NAMESPACE + #endif // ABSTRACTTOOL_H diff --git a/src/plugins/qmltooling/shared/abstractviewinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.cpp index e718cf022f..fa6dca7aca 100644 --- a/src/plugins/qmltooling/shared/abstractviewinspector.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.cpp @@ -32,14 +32,13 @@ ****************************************************************************/ #include "abstractviewinspector.h" - #include "abstracttool.h" #include <QtCore/QDebug> #include <QtQml/QQmlEngine> #include <QtQml/QQmlComponent> #include <QtCore/private/qabstractanimation_p.h> -#include <QtQml/private/qqmlinspectorservice_p.h> +#include <QtQml/private/qqmldebugconnector_p.h> #include <QtQml/private/qqmlcontext_p.h> #include <QtGui/QMouseEvent> @@ -61,6 +60,8 @@ // clearCache: void // Response for "destroyObject" carries the <debugId_int> of the destroyed object. +QT_BEGIN_NAMESPACE + const char REQUEST[] = "request"; const char RESPONSE[] = "response"; const char EVENT[] = "event"; @@ -78,10 +79,10 @@ const char CLEAR_CACHE[] = "clearCache"; namespace QmlJSDebugger { -AbstractViewInspector::AbstractViewInspector(QObject *parent) : +AbstractViewInspector::AbstractViewInspector(QQmlDebugService *service, QObject *parent) : QObject(parent), m_enabled(false), - m_debugService(QQmlInspectorService::instance()), + m_debugService(service), m_eventId(0), m_reloadEventId(-1) { @@ -260,14 +261,13 @@ void AbstractViewInspector::onQmlObjectDestroyed(QObject *object) return; QPair<int, int> ids = m_hashObjectsTobeDestroyed.take(object); - QQmlDebugService::removeInvalidObjectsFromHash(); QByteArray response; QQmlDebugStream rs(&response, QIODevice::WriteOnly); rs << QByteArray(RESPONSE) << ids.first << true << ids.second; - m_debugService->sendMessage(response); + emit m_debugService->messageToClient(m_debugService->name(), response); } void AbstractViewInspector::handleMessage(const QByteArray &message) @@ -361,7 +361,7 @@ void AbstractViewInspector::handleMessage(const QByteArray &message) QByteArray response; QQmlDebugStream rs(&response, QIODevice::WriteOnly); rs << QByteArray(RESPONSE) << requestId << success; - m_debugService->sendMessage(response); + emit m_debugService->messageToClient(m_debugService->name(), response); } void AbstractViewInspector::sendCurrentObjects(const QList<QObject*> &objects) @@ -372,11 +372,12 @@ void AbstractViewInspector::sendCurrentObjects(const QList<QObject*> &objects) ds << QByteArray(EVENT) << m_eventId++ << QByteArray(SELECT); QList<int> debugIds; + debugIds.reserve(objects.count()); foreach (QObject *object, objects) debugIds << QQmlDebugService::idForObject(object); ds << debugIds; - m_debugService->sendMessage(message); + emit m_debugService->messageToClient(m_debugService->name(), message); } void AbstractViewInspector::sendQmlFileReloaded(bool success) @@ -389,7 +390,7 @@ void AbstractViewInspector::sendQmlFileReloaded(bool success) QQmlDebugStream rs(&response, QIODevice::WriteOnly); rs << QByteArray(RESPONSE) << m_reloadEventId << success; - m_debugService->sendMessage(response); + emit m_debugService->messageToClient(m_debugService->name(), response); } QString AbstractViewInspector::idStringForObject(QObject *obj) const @@ -414,3 +415,5 @@ void AbstractViewInspector::removeTool(AbstractTool *tool) } } // namespace QmlJSDebugger + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/shared/abstractviewinspector.h b/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.h index 02b4247bec..8f7ad4ac5b 100644 --- a/src/plugins/qmltooling/shared/abstractviewinspector.h +++ b/src/plugins/qmltooling/qmldbg_inspector/abstractviewinspector.h @@ -38,18 +38,14 @@ #include <QtCore/QObject> #include <QtCore/QStringList> -#include "qmlinspectorconstants.h" - QT_BEGIN_NAMESPACE class QQmlEngine; -class QQmlInspectorService; +class QQmlDebugService; class QKeyEvent; class QMouseEvent; class QWheelEvent; class QTouchEvent; -QT_END_NAMESPACE - namespace QmlJSDebugger { class AbstractTool; @@ -62,7 +58,7 @@ class AbstractViewInspector : public QObject Q_OBJECT public: - explicit AbstractViewInspector(QObject *parent = 0); + explicit AbstractViewInspector(QQmlDebugService *service, QObject *parent = 0); void handleMessage(const QByteArray &message); @@ -115,7 +111,7 @@ private: bool m_enabled; - QQmlInspectorService *m_debugService; + QQmlDebugService *m_debugService; QList<AbstractTool *> m_tools; int m_eventId; int m_reloadEventId; @@ -125,4 +121,6 @@ private: } // namespace QmlJSDebugger +QT_END_NAMESPACE + #endif // ABSTRACTVIEWINSPECTOR_H diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/highlight.cpp b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp index 5af7a225fd..4d4e3aa720 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/highlight.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp @@ -38,8 +38,9 @@ #include <QtGui/QStaticText> #include <QtQuick/QQuickWindow> +QT_BEGIN_NAMESPACE + namespace QmlJSDebugger { -namespace QtQuick2 { Highlight::Highlight(QQuickItem *parent) : QQuickPaintedItem(parent) { @@ -193,5 +194,6 @@ void SelectionHighlight::disableNameDisplay() update(); } -} // namespace QtQuick2 } // namespace QmlJSDebugger + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/highlight.h b/src/plugins/qmltooling/qmldbg_inspector/highlight.h index 29d2f0d911..05f6382353 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/highlight.h +++ b/src/plugins/qmltooling/qmldbg_inspector/highlight.h @@ -39,9 +39,9 @@ #include <QtGui/QTransform> #include <QtQuick/QQuickPaintedItem> +QT_BEGIN_NAMESPACE namespace QmlJSDebugger { -namespace QtQuick2 { class Highlight : public QQuickPaintedItem { @@ -104,7 +104,8 @@ public: void paint(QPainter *painter); }; -} // namespace QtQuick2 } // namespace QmlJSDebugger +QT_END_NAMESPACE + #endif // HIGHLIGHT_H diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/inspecttool.cpp b/src/plugins/qmltooling/qmldbg_inspector/inspecttool.cpp index f86225fa0a..cc6b4ffb8c 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/inspecttool.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/inspecttool.cpp @@ -48,8 +48,12 @@ #include <QtQuick/QQuickView> #include <QtQuick/QQuickItem> +QT_BEGIN_NAMESPACE + namespace QmlJSDebugger { -namespace QtQuick2 { + +static const double ZoomSnapDelta = 0.04; +static const int PressAndHoldTimeout = 800; InspectTool::InspectTool(QQuickViewInspector *inspector, QQuickView *view) : AbstractTool(inspector), @@ -60,7 +64,7 @@ InspectTool::InspectTool(QQuickViewInspector *inspector, QQuickView *view) : m_tapEvent(false), m_contentItem(view->contentItem()), m_originalPosition(view->contentItem()->position()), - m_smoothScaleFactor(Constants::ZoomSnapDelta), + m_smoothScaleFactor(ZoomSnapDelta), m_minScale(0.125f), m_maxScale(48.0f), m_originalScale(view->contentItem()->scale()), @@ -71,7 +75,7 @@ InspectTool::InspectTool(QQuickViewInspector *inspector, QQuickView *view) : { //Press and Hold Timer m_pressAndHoldTimer.setSingleShot(true); - m_pressAndHoldTimer.setInterval(Constants::PressAndHoldTimeout); + m_pressAndHoldTimer.setInterval(PressAndHoldTimeout); connect(&m_pressAndHoldTimer, SIGNAL(timeout()), SLOT(zoomTo100())); //Timer to display selected item's name m_nameDisplayTimer.setSingleShot(true); @@ -418,5 +422,6 @@ void InspectTool::showSelectedItemName() inspector()->showSelectedItemName(m_lastItem, m_mousePosition); } -} // namespace QtQuick2 } // namespace QmlJSDebugger + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/inspecttool.h b/src/plugins/qmltooling/qmldbg_inspector/inspecttool.h index 0b1b49fd93..fdb763d4b3 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/inspecttool.h +++ b/src/plugins/qmltooling/qmldbg_inspector/inspecttool.h @@ -40,11 +40,12 @@ #include <QtCore/QPointer> #include <QtCore/QTimer> -QT_FORWARD_DECLARE_CLASS(QQuickView) -QT_FORWARD_DECLARE_CLASS(QQuickItem) +QT_BEGIN_NAMESPACE + +class QQuickView; +class QQuickItem; namespace QmlJSDebugger { -namespace QtQuick2 { class QQuickViewInspector; class HoverHighlight; @@ -119,7 +120,8 @@ private: QQuickItem *m_lastClickedItem; }; -} // namespace QtQuick2 } // namespace QmlJSDebugger +QT_END_NAMESPACE + #endif // INSPECTTOOL_H diff --git a/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro b/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro new file mode 100644 index 0000000000..1c3e5f387b --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro @@ -0,0 +1,27 @@ +TARGET = qmldbg_inspector +QT += qml-private quick-private core-private gui-private + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QQmlInspectorServiceFactory +load(qt_plugin) + +INCLUDEPATH *= $$PWD $$PWD/../shared + +SOURCES += \ + $$PWD/highlight.cpp \ + $$PWD/qquickviewinspector.cpp \ + $$PWD/abstracttool.cpp \ + $$PWD/abstractviewinspector.cpp \ + $$PWD/inspecttool.cpp \ + $$PWD/qqmlinspectorservice.cpp + +HEADERS += \ + $$PWD/highlight.h \ + $$PWD/qquickviewinspector.h \ + $$PWD/qqmlinspectorservicefactory.h \ + $$PWD/abstracttool.h \ + $$PWD/abstractviewinspector.h \ + $$PWD/inspecttool.h + +OTHER_FILES += \ + qqmlinspectorservice.json diff --git a/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp new file mode 100644 index 0000000000..1707091df3 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlinspectorservicefactory.h" +#include "qquickviewinspector.h" + +#include <private/qqmlglobal_p.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QPluginLoader> + +QT_BEGIN_NAMESPACE + +class QQmlInspectorServiceImpl : public QQmlInspectorService +{ + Q_OBJECT + +public: + QQmlInspectorServiceImpl(QObject *parent = 0); + + void addView(QObject *); + void removeView(QObject *); + +protected: + virtual void stateChanged(State state); + virtual void messageReceived(const QByteArray &); + +private Q_SLOTS: + void processMessage(const QByteArray &message); + void updateState(); + +private: + friend class QQmlInspectorServiceFactory; + + QList<QObject*> m_views; + QmlJSDebugger::AbstractViewInspector *m_currentInspector; +}; + +QQmlInspectorServiceImpl::QQmlInspectorServiceImpl(QObject *parent): + QQmlInspectorService(1, parent), m_currentInspector(0) +{ +} + +void QQmlInspectorServiceImpl::addView(QObject *view) +{ + m_views.append(view); + updateState(); +} + +void QQmlInspectorServiceImpl::removeView(QObject *view) +{ + m_views.removeAll(view); + updateState(); +} + +void QQmlInspectorServiceImpl::stateChanged(State /*state*/) +{ + QMetaObject::invokeMethod(this, "updateState", Qt::QueuedConnection); +} + +void QQmlInspectorServiceImpl::updateState() +{ + delete m_currentInspector; + m_currentInspector = 0; + + if (m_views.isEmpty() || state() != Enabled) + return; + + QQuickView *qtQuickView = qobject_cast<QQuickView*>(m_views.first()); + if (qtQuickView) + m_currentInspector = new QmlJSDebugger::QQuickViewInspector(this, qtQuickView, this); + else + qWarning() << "QQmlInspector: No inspector available for view '" + << m_views.first()->metaObject()->className() << "'."; +} + +void QQmlInspectorServiceImpl::messageReceived(const QByteArray &message) +{ + QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message)); +} + +void QQmlInspectorServiceImpl::processMessage(const QByteArray &message) +{ + if (m_currentInspector) + m_currentInspector->handleMessage(message); +} + +QQmlDebugService *QQmlInspectorServiceFactory::create(const QString &key) +{ + return key == QQmlInspectorServiceImpl::s_key ? new QQmlInspectorServiceImpl(this) : 0; +} + +QT_END_NAMESPACE + +#include "qqmlinspectorservice.moc" diff --git a/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.json b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.json new file mode 100644 index 0000000000..9ace8dad2f --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "QmlInspector" ] +} diff --git a/src/qml/debugger/qqmlinspectorinterface_p.h b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservicefactory.h index 9b29d383c7..52f84a362d 100644 --- a/src/qml/debugger/qqmlinspectorinterface_p.h +++ b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservicefactory.h @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef QQMLINSPECTORINTERFACE_H -#define QQMLINSPECTORINTERFACE_H +#ifndef QQMLINSPECTORSERVICE_H +#define QQMLINSPECTORSERVICE_H // // W A R N I N G @@ -45,30 +45,22 @@ // We mean it. // +#include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qqmldebugservicefactory_p.h> + #include <QtQml/qtqmlglobal.h> -#include <private/qqmlglobal_p.h> +#include <QtCore/QList> QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlInspectorInterface +class QQmlInspectorServiceFactory : public QQmlDebugServiceFactory { + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlDebugServiceFactory_iid FILE "qqmlinspectorservice.json") public: - QQmlInspectorInterface() {} - virtual ~QQmlInspectorInterface() {} - - virtual bool canHandleView(QObject *view) = 0; - - virtual void activate(QObject *view) = 0; - virtual void deactivate() = 0; - - virtual void clientMessage(const QByteArray &message) = 0; + QQmlDebugService *create(const QString &key); }; -#define QQmlInspectorInterface_iid "org.qt-project.Qt.QQmlInspectorInterface" - -Q_DECLARE_INTERFACE(QQmlInspectorInterface, QQmlInspectorInterface_iid) - QT_END_NAMESPACE -#endif // QQMLINSPECTORINTERFACE_H +#endif // QQMLINSPECTORSERVICE_H diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/qquickviewinspector.cpp index e61c421bfa..de9d5617b5 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/qquickviewinspector.cpp @@ -37,7 +37,6 @@ #include "inspecttool.h" #include <QtQml/private/qqmlengine_p.h> -#include <QtQml/private/qqmldebugservice_p.h> #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/QQuickView> @@ -45,8 +44,8 @@ #include <cfloat> +QT_BEGIN_NAMESPACE namespace QmlJSDebugger { -namespace QtQuick2 { /* * Collects all the items at the given position, from top to bottom. @@ -110,8 +109,9 @@ static QQuickItem *itemAt(QQuickItem *item, const QPointF &pos, } -QQuickViewInspector::QQuickViewInspector(QQuickView *view, QObject *parent) : - AbstractViewInspector(parent), +QQuickViewInspector::QQuickViewInspector(QQmlDebugService *service, QQuickView *view, + QObject *parent) : + AbstractViewInspector(service, parent), m_view(view), m_overlay(new QQuickItem), m_inspectTool(new InspectTool(this, view)), @@ -211,6 +211,7 @@ void QQuickViewInspector::setSelectedItems(const QList<QQuickItem *> &items) return; QList<QObject*> objectList; + objectList.reserve(items.count()); foreach (QQuickItem *item, items) objectList << item; @@ -314,8 +315,6 @@ void QQuickViewInspector::reloadQmlFile(const QHash<QString, QByteArray> &change // Reset the selection since we are reloading the main qml setSelectedItems(QList<QQuickItem *>()); - QQmlDebugService::clearObjectsFromHash(); - QHash<QUrl, QByteArray> debugCache; foreach (const QString &str, changesHash.keys()) @@ -373,5 +372,6 @@ void QQuickViewInspector::applyAppOnTop() setWindowFlags(flags); } -} // namespace QtQuick2 } // namespace QmlJSDebugger + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.h b/src/plugins/qmltooling/qmldbg_inspector/qquickviewinspector.h index be3ede4d07..e823e5a03d 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.h +++ b/src/plugins/qmltooling/qmldbg_inspector/qquickviewinspector.h @@ -43,10 +43,8 @@ QT_BEGIN_NAMESPACE class QQuickView; class QQuickItem; -QT_END_NAMESPACE namespace QmlJSDebugger { -namespace QtQuick2 { class InspectTool; class SelectionHighlight; @@ -55,7 +53,7 @@ class QQuickViewInspector : public AbstractViewInspector { Q_OBJECT public: - explicit QQuickViewInspector(QQuickView *view, QObject *parent = 0); + explicit QQuickViewInspector(QQmlDebugService *service, QQuickView *view, QObject *parent = 0); // AbstractViewInspector void changeCurrentObjects(const QList<QObject*> &objects); @@ -104,7 +102,8 @@ private: bool m_appOnTop; }; -} // namespace QtQuick2 } // namespace QmlJSDebugger +QT_END_NAMESPACE + #endif // QQUICKVIEWINSPECTOR_H diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp new file mode 100644 index 0000000000..057bf9523e --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlocalclientconnectionfactory.h" +#include "qpacketprotocol.h" +#include "qqmldebugserver.h" + +#include <QtCore/qplugin.h> +#include <QtNetwork/qlocalsocket.h> + +QT_BEGIN_NAMESPACE + + +class QLocalClientConnection : public QQmlDebugServerConnection +{ + Q_OBJECT + Q_DISABLE_COPY(QLocalClientConnection) + +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); + + bool isConnected() const; + void disconnect(); + + void waitForConnection(); + void flush(); + +private slots: + void connectionEstablished(); + +private: + bool connectToServer(); + + bool m_block; + QString m_filename; + QLocalSocket *m_socket; + QQmlDebugServer *m_debugServer; +}; + +QLocalClientConnection::QLocalClientConnection() : + m_block(false), + m_socket(0), + m_debugServer(0) +{ +} + +QLocalClientConnection::~QLocalClientConnection() +{ + if (isConnected()) + disconnect(); +} + +void QLocalClientConnection::setServer(QQmlDebugServer *server) +{ + m_debugServer = server; +} + +bool QLocalClientConnection::isConnected() const +{ + return m_socket && m_socket->state() == QLocalSocket::ConnectedState; +} + +void QLocalClientConnection::disconnect() +{ + while (m_socket && m_socket->bytesToWrite() > 0) + m_socket->waitForBytesWritten(); + + m_socket->deleteLater(); + m_socket = 0; +} + +bool QLocalClientConnection::setPortRange(int portFrom, int portTo, bool block, + const QString &hostaddress) +{ + Q_UNUSED(portFrom); + Q_UNUSED(portTo); + Q_UNUSED(block); + Q_UNUSED(hostaddress); + return false; +} + +bool QLocalClientConnection::setFileName(const QString &filename, bool block) +{ + m_filename = filename; + m_block = block; + return connectToServer(); +} + +void QLocalClientConnection::waitForConnection() +{ + m_socket->waitForConnected(-1); +} + +bool QLocalClientConnection::connectToServer() +{ + m_socket = new QLocalSocket; + m_socket->setParent(this); + QObject::connect(m_socket, SIGNAL(connected()), this, SLOT(connectionEstablished())); + m_socket->connectToServer(m_filename); + qDebug("QML Debugger: Connecting to socket %s...", m_filename.toLatin1().constData()); + return true; +} + +void QLocalClientConnection::flush() +{ + if (m_socket) + m_socket->flush(); +} + +void QLocalClientConnection::connectionEstablished() +{ + m_debugServer->setDevice(m_socket); +} + +QQmlDebugServerConnection *QLocalClientConnectionFactory::create(const QString &key) +{ + return (key == QLatin1String("QLocalClientConnection") ? new QLocalClientConnection : 0); +} + +QT_END_NAMESPACE + +#include "qlocalclientconnection.moc" diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.json b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.json new file mode 100644 index 0000000000..5f8fd49296 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "QLocalClientConnection" ] +} diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnectionfactory.h b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnectionfactory.h new file mode 100644 index 0000000000..110e0c2395 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnectionfactory.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLOCALCLIENTCONNECTIONFACTORY_H +#define QLOCALCLIENTCONNECTIONFACTORY_H + +#include "qqmldebugserverconnection.h" + +QT_BEGIN_NAMESPACE + +class QLocalClientConnectionFactory : public QQmlDebugServerConnectionFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlDebugServerConnectionFactory_iid FILE "qlocalclientconnection.json") + Q_INTERFACES(QQmlDebugServerConnectionFactory) +public: + QQmlDebugServerConnection *create(const QString &key); +}; + +QT_END_NAMESPACE + +#endif // QLOCALCLIENTCONNECTIONFACTORY_H diff --git a/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro b/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro new file mode 100644 index 0000000000..491be04b15 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro @@ -0,0 +1,20 @@ +TARGET = qmldbg_local +QT = qml-private + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QLocalClientConnectionFactory +load(qt_plugin) + +SOURCES += \ + $$PWD/qlocalclientconnection.cpp + +HEADERS += \ + $$PWD/qlocalclientconnectionfactory.h \ + $$PWD/../shared/qqmldebugserver.h \ + $$PWD/../shared/qqmldebugserverconnection.h + +INCLUDEPATH += $$PWD \ + $$PWD/../shared + +OTHER_FILES += \ + $$PWD/qlocalclientconnection.json diff --git a/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro b/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro new file mode 100644 index 0000000000..e1c4095d88 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro @@ -0,0 +1,28 @@ +TARGET = qmldbg_profiler +QT = qml-private core-private + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QQmlProfilerServiceFactory +load(qt_plugin) + +SOURCES += \ + $$PWD/qqmlenginecontrolservice.cpp \ + $$PWD/qqmlprofileradapter.cpp \ + $$PWD/qqmlprofilerservice.cpp \ + $$PWD/qqmlprofilerservicefactory.cpp \ + $$PWD/qv4profileradapter.cpp + +HEADERS += \ + $$PWD/../shared/qqmlconfigurabledebugservice.h \ + $$PWD/qqmlenginecontrolservice.h \ + $$PWD/qqmlprofileradapter.h \ + $$PWD/qqmlprofilerservice.h \ + $$PWD/qqmlprofilerservicefactory.h \ + $$PWD/qv4profileradapter.h + +INCLUDEPATH += $$PWD \ + $$PWD/../shared + +OTHER_FILES += \ + $$PWD/qqmlprofilerservice.json + diff --git a/src/qml/debugger/qqmlenginecontrolservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp index 07c6715524..4f131ac481 100644 --- a/src/qml/debugger/qqmlenginecontrolservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp @@ -31,24 +31,16 @@ ** ****************************************************************************/ +#include "qqmlenginecontrolservice.h" #include <QQmlEngine> -#include "qqmldebug.h" -#include "qqmlenginecontrolservice_p.h" QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(QQmlEngineControlService, qmlEngineControlService) +const QString QQmlEngineControlService::s_key = QStringLiteral("EngineControl"); -QQmlEngineControlService::QQmlEngineControlService() : - QQmlDebugService(QStringLiteral("EngineControl"), 1) +QQmlEngineControlService::QQmlEngineControlService(QObject *parent) : + QQmlDebugService(s_key, 1, parent) { - QMutexLocker lock(&dataMutex); - registerService(); -} - -QQmlEngineControlService *QQmlEngineControlService::instance() -{ - return qmlEngineControlService(); } void QQmlEngineControlService::messageReceived(const QByteArray &message) @@ -119,7 +111,7 @@ void QQmlEngineControlService::sendMessage(QQmlEngineControlService::MessageType QByteArray message; QQmlDebugStream d(&message, QIODevice::WriteOnly); d << type << idForObject(engine); - QQmlDebugService::sendMessage(message); + emit messageToClient(name(), message); } void QQmlEngineControlService::stateChanged(State) diff --git a/src/qml/debugger/qqmlenginecontrolservice_p.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.h index 2171937efe..e2a93e562a 100644 --- a/src/qml/debugger/qqmlenginecontrolservice_p.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.h @@ -35,7 +35,7 @@ #define QQMLENGINECONTROLSERVICE_H #include <QMutex> -#include "qqmldebugservice_p.h" +#include <private/qqmldebugservice_p.h> // // W A R N I N G @@ -53,6 +53,8 @@ QT_BEGIN_NAMESPACE class QQmlEngineControlService : public QQmlDebugService { public: + static const QString s_key; + enum MessageType { EngineAboutToBeAdded, EngineAdded, @@ -65,9 +67,7 @@ public: StopWaitingEngine }; - QQmlEngineControlService(); - - static QQmlEngineControlService *instance(); + QQmlEngineControlService(QObject *parent = 0); protected: QMutex dataMutex; diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp new file mode 100644 index 0000000000..349c181d13 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlprofileradapter.h" +#include <private/qqmldebugserviceinterfaces_p.h> + +QT_BEGIN_NAMESPACE + +QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine) : + QQmlAbstractProfilerAdapter(service), next(0) +{ + engine->enableProfiler(); + connect(this, SIGNAL(profilingEnabled(quint64)), engine->profiler, SLOT(startProfiling(quint64))); + connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)), + engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection); + connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling())); + connect(this, SIGNAL(profilingDisabledWhileWaiting()), + engine->profiler, SLOT(stopProfiling()), Qt::DirectConnection); + connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData())); + connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), + engine->profiler, SLOT(setTimer(QElapsedTimer))); + connect(engine->profiler, SIGNAL(dataReady(QVector<QQmlProfilerData>)), + this, SLOT(receiveData(QVector<QQmlProfilerData>))); +} + +// convert to QByteArrays that can be sent to the debug client +// use of QDataStream can skew results +// (see tst_qqmldebugtrace::trace() benchmark) +static void qQmlProfilerDataToByteArrays(const QQmlProfilerData *d, QList<QByteArray> &messages) +{ + QByteArray data; + Q_ASSERT_X(((d->messageType | d->detailType) & (1 << 31)) == 0, Q_FUNC_INFO, + "You can use at most 31 message types and 31 detail types."); + for (uint decodedMessageType = 0; (d->messageType >> decodedMessageType) != 0; + ++decodedMessageType) { + if ((d->messageType & (1 << decodedMessageType)) == 0) + continue; + + for (uint decodedDetailType = 0; (d->detailType >> decodedDetailType) != 0; + ++decodedDetailType) { + if ((d->detailType & (1 << decodedDetailType)) == 0) + continue; + + //### using QDataStream is relatively expensive + QQmlDebugStream ds(&data, QIODevice::WriteOnly); + ds << d->time << decodedMessageType << decodedDetailType; + + switch (decodedMessageType) { + case QQmlProfilerDefinitions::RangeStart: + if (decodedDetailType == (int)QQmlProfilerDefinitions::Binding) + ds << QQmlProfilerDefinitions::QmlBinding; + break; + case QQmlProfilerDefinitions::RangeData: + ds << d->detailString; + break; + case QQmlProfilerDefinitions::RangeLocation: + ds << (d->detailUrl.isEmpty() ? d->detailString : d->detailUrl.toString()) << d->x + << d->y; + break; + case QQmlProfilerDefinitions::RangeEnd: break; + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type."); + break; + } + messages << data; + data.clear(); + } + } +} + +qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) +{ + while (next != data.length()) { + if (data[next].time > until) + return data[next].time; + qQmlProfilerDataToByteArrays(&(data[next++]), messages); + } + + next = 0; + data.clear(); + return -1; +} + +void QQmlProfilerAdapter::receiveData(const QVector<QQmlProfilerData> &new_data) +{ + if (data.isEmpty()) + data = new_data; + else + data.append(new_data); + service->dataReady(this); +} + +QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlconfigurabledebugservice_p_p.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h index 6d693b6352..eceb58ce3a 100644 --- a/src/qml/debugger/qqmlconfigurabledebugservice_p_p.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h @@ -31,9 +31,6 @@ ** ****************************************************************************/ -#ifndef QQMLCONFIGURABLEDEBUGSERVICE_P_H -#define QQMLCONFIGURABLEDEBUGSERVICE_P_H - // // W A R N I N G // ------------- @@ -45,26 +42,28 @@ // We mean it. // -#include "qqmldebugservice_p.h" -#include "qqmldebugservice_p_p.h" +#ifndef QQMLPROFILERADAPTER_H +#define QQMLPROFILERADAPTER_H -#include <QMutex> +#include <private/qqmlabstractprofileradapter_p.h> +#include <private/qqmlprofiler_p.h> QT_BEGIN_NAMESPACE -class QQmlEngine; - -class QQmlConfigurableDebugServicePrivate : public QQmlDebugServicePrivate -{ - Q_DECLARE_PUBLIC(QQmlConfigurableDebugService) +class QQmlProfilerAdapter : public QQmlAbstractProfilerAdapter { + Q_OBJECT public: - QQmlConfigurableDebugServicePrivate() : configMutex(QMutex::Recursive) {} + QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine); + qint64 sendMessages(qint64 until, QList<QByteArray> &messages); + +public slots: + void receiveData(const QVector<QQmlProfilerData> &new_data); - QMutex configMutex; - QList<QQmlEngine *> waitingEngines; - bool waitingForConfiguration; +private: + QVector<QQmlProfilerData> data; + int next; }; QT_END_NAMESPACE -#endif // QQMLCONFIGURABLEDEBUGSERVICE_P_H +#endif // QQMLPROFILERADAPTER_H diff --git a/src/qml/debugger/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index 85556836e6..65b99ef7ca 100644 --- a/src/qml/debugger/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -31,10 +31,10 @@ ** ****************************************************************************/ -#include "qqmlprofilerservice_p.h" -#include "qqmldebugserver_p.h" -#include "qv4profileradapter_p.h" -#include "qqmlprofiler_p.h" +#include "qqmlprofilerservice.h" +#include "qv4profileradapter.h" +#include "qqmlprofileradapter.h" +#include "qqmlprofilerservicefactory.h" #include <private/qqmlengine_p.h> #include <QtCore/qdatastream.h> @@ -45,20 +45,14 @@ QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(QQmlProfilerService, profilerInstance) - -QQmlProfilerService::QQmlProfilerService() - : QQmlConfigurableDebugService(QStringLiteral("CanvasFrameRate"), 1) +QQmlProfilerServiceImpl::QQmlProfilerServiceImpl(QObject *parent) : + QQmlConfigurableDebugService<QQmlProfilerService>(1, parent), + m_waitingForStop(false) { m_timer.start(); - - QMutexLocker lock(configMutex()); - // If there is no debug server it doesn't matter as we'll never get enabled anyway. - if (QQmlDebugServer::instance() != 0) - moveToThread(QQmlDebugServer::instance()->thread()); } -QQmlProfilerService::~QQmlProfilerService() +QQmlProfilerServiceImpl::~QQmlProfilerServiceImpl() { // No need to lock here. If any engine or global profiler is still trying to register at this // point we have a nasty bug anyway. @@ -66,9 +60,9 @@ QQmlProfilerService::~QQmlProfilerService() qDeleteAll(m_globalProfilers); } -void QQmlProfilerService::dataReady(QQmlAbstractProfilerAdapter *profiler) +void QQmlProfilerServiceImpl::dataReady(QQmlAbstractProfilerAdapter *profiler) { - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); bool dataComplete = true; for (QMultiMap<qint64, QQmlAbstractProfilerAdapter *>::iterator i(m_startTimes.begin()); i != m_startTimes.end();) { if (i.value() == profiler) { @@ -98,38 +92,35 @@ void QQmlProfilerService::dataReady(QQmlAbstractProfilerAdapter *profiler) } } -QQmlProfilerService *QQmlProfilerService::instance() -{ - // just make sure that the service is properly registered - return profilerInstance(); -} - -void QQmlProfilerService::engineAboutToBeAdded(QQmlEngine *engine) +void QQmlProfilerServiceImpl::engineAboutToBeAdded(QQmlEngine *engine) { - Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be added from the engine thread"); + Q_ASSERT_X(QThread::currentThread() == engine->thread(), Q_FUNC_INFO, + "QML profilers have to be added from the engine thread"); - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); QQmlProfilerAdapter *qmlAdapter = new QQmlProfilerAdapter(this, QQmlEnginePrivate::get(engine)); QV4ProfilerAdapter *v4Adapter = new QV4ProfilerAdapter(this, QV8Engine::getV4(engine->handle())); addEngineProfiler(qmlAdapter, engine); addEngineProfiler(v4Adapter, engine); - QQmlConfigurableDebugService::engineAboutToBeAdded(engine); + QQmlConfigurableDebugService<QQmlProfilerService>::engineAboutToBeAdded(engine); } -void QQmlProfilerService::engineAdded(QQmlEngine *engine) +void QQmlProfilerServiceImpl::engineAdded(QQmlEngine *engine) { - Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be added from the engine thread"); + Q_ASSERT_X(QThread::currentThread() == engine->thread(), Q_FUNC_INFO, + "QML profilers have to be added from the engine thread"); - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) profiler->stopWaiting(); } -void QQmlProfilerService::engineAboutToBeRemoved(QQmlEngine *engine) +void QQmlProfilerServiceImpl::engineAboutToBeRemoved(QQmlEngine *engine) { - Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be removed from the engine thread"); + Q_ASSERT_X(QThread::currentThread() == engine->thread(), Q_FUNC_INFO, + "QML profilers have to be removed from the engine thread"); - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); bool isRunning = false; foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) { if (profiler->isRunning()) @@ -144,11 +135,12 @@ void QQmlProfilerService::engineAboutToBeRemoved(QQmlEngine *engine) } } -void QQmlProfilerService::engineRemoved(QQmlEngine *engine) +void QQmlProfilerServiceImpl::engineRemoved(QQmlEngine *engine) { - Q_ASSERT_X(QThread::currentThread() != thread(), Q_FUNC_INFO, "QML profilers have to be removed from the engine thread"); + Q_ASSERT_X(QThread::currentThread() == engine->thread(), Q_FUNC_INFO, + "QML profilers have to be removed from the engine thread"); - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers.values(engine)) { removeProfilerFromStartTimes(profiler); delete profiler; @@ -156,16 +148,16 @@ void QQmlProfilerService::engineRemoved(QQmlEngine *engine) m_engineProfilers.remove(engine); } -void QQmlProfilerService::addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QQmlEngine *engine) +void QQmlProfilerServiceImpl::addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QQmlEngine *engine) { profiler->moveToThread(thread()); profiler->synchronize(m_timer); m_engineProfilers.insert(engine, profiler); } -void QQmlProfilerService::addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) +void QQmlProfilerServiceImpl::addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) { - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); profiler->synchronize(m_timer); m_globalProfilers.append(profiler); // Global profiler, not connected to a specific engine. @@ -179,15 +171,15 @@ void QQmlProfilerService::addGlobalProfiler(QQmlAbstractProfilerAdapter *profile profiler->startProfiling(features); } -void QQmlProfilerService::removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) +void QQmlProfilerServiceImpl::removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) { - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); removeProfilerFromStartTimes(profiler); m_globalProfilers.removeOne(profiler); delete profiler; } -void QQmlProfilerService::removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler) +void QQmlProfilerServiceImpl::removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler) { for (QMultiMap<qint64, QQmlAbstractProfilerAdapter *>::iterator i(m_startTimes.begin()); i != m_startTimes.end();) { @@ -206,9 +198,9 @@ void QQmlProfilerService::removeProfilerFromStartTimes(const QQmlAbstractProfile * * If any engine profiler is started like that also start all global profilers. */ -void QQmlProfilerService::startProfiling(QQmlEngine *engine, quint64 features) +void QQmlProfilerServiceImpl::startProfiling(QQmlEngine *engine, quint64 features) { - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); QByteArray message; QQmlDebugStream d(&message, QIODevice::WriteOnly); @@ -243,9 +235,11 @@ void QQmlProfilerService::startProfiling(QQmlEngine *engine, quint64 features) if (!profiler->isRunning()) profiler->startProfiling(features); } + + emit startFlushTimer(); } - QQmlDebugService::sendMessage(message); + emit messageToClient(name(), message); } /*! @@ -255,9 +249,9 @@ void QQmlProfilerService::startProfiling(QQmlEngine *engine, quint64 features) * If afterwards no more engine profilers are running, also stop all global profilers. Otherwise * only make them report their data. */ -void QQmlProfilerService::stopProfiling(QQmlEngine *engine) +void QQmlProfilerServiceImpl::stopProfiling(QQmlEngine *engine) { - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); QList<QQmlAbstractProfilerAdapter *> stopping; QList<QQmlAbstractProfilerAdapter *> reporting; @@ -274,6 +268,9 @@ void QQmlProfilerService::stopProfiling(QQmlEngine *engine) } } + if (stopping.isEmpty()) + return; + foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) { if (!profiler->isRunning()) continue; @@ -285,6 +282,9 @@ void QQmlProfilerService::stopProfiling(QQmlEngine *engine) } } + emit stopFlushTimer(); + m_waitingForStop = true; + foreach (QQmlAbstractProfilerAdapter *profiler, reporting) profiler->reportData(); @@ -295,21 +295,24 @@ void QQmlProfilerService::stopProfiling(QQmlEngine *engine) /* Send the queued up messages. */ -void QQmlProfilerService::sendMessages() +void QQmlProfilerServiceImpl::sendMessages() { QList<QByteArray> messages; QByteArray data; - QQmlDebugStream traceEnd(&data, QIODevice::WriteOnly); - traceEnd << m_timer.nsecsElapsed() << (int)Event << (int)EndTrace; - QSet<QQmlEngine *> seen; - foreach (QQmlAbstractProfilerAdapter *profiler, m_startTimes) { - for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin()); - i != m_engineProfilers.end(); ++i) { - if (i.value() == profiler && !seen.contains(i.key())) { - seen << i.key(); - traceEnd << idForObject(i.key()); + if (m_waitingForStop) { + QQmlDebugStream traceEnd(&data, QIODevice::WriteOnly); + traceEnd << m_timer.nsecsElapsed() << (int)Event << (int)EndTrace; + + QSet<QQmlEngine *> seen; + foreach (QQmlAbstractProfilerAdapter *profiler, m_startTimes) { + for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin()); + i != m_engineProfilers.end(); ++i) { + if (i.value() == profiler && !seen.contains(i.key())) { + seen << i.key(); + traceEnd << idForObject(i.key()); + } } } } @@ -326,20 +329,31 @@ void QQmlProfilerService::sendMessages() } } - //indicate completion - messages << data; - data.clear(); + if (m_waitingForStop) { + //indicate completion + messages << data; + data.clear(); - QQmlDebugStream ds(&data, QIODevice::WriteOnly); - ds << (qint64)-1 << (int)Complete; - messages << data; + QQmlDebugStream ds(&data, QIODevice::WriteOnly); + ds << (qint64)-1 << (int)Complete; + messages << data; + m_waitingForStop = false; + } + + emit messagesToClient(name(), messages); - QQmlDebugService::sendMessages(messages); + // Restart flushing if any profilers are still running + foreach (const QQmlAbstractProfilerAdapter *profiler, m_engineProfilers) { + if (profiler->isRunning()) { + emit startFlushTimer(); + break; + } + } } -void QQmlProfilerService::stateAboutToBeChanged(QQmlDebugService::State newState) +void QQmlProfilerServiceImpl::stateAboutToBeChanged(QQmlDebugService::State newState) { - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); if (state() == newState) return; @@ -351,9 +365,9 @@ void QQmlProfilerService::stateAboutToBeChanged(QQmlDebugService::State newState } } -void QQmlProfilerService::messageReceived(const QByteArray &message) +void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message) { - QMutexLocker lock(configMutex()); + QMutexLocker lock(&m_configMutex); QByteArray rwData = message; QQmlDebugStream stream(&rwData, QIODevice::ReadOnly); @@ -361,11 +375,25 @@ void QQmlProfilerService::messageReceived(const QByteArray &message) int engineId = -1; quint64 features = std::numeric_limits<quint64>::max(); bool enabled; + uint flushInterval = 0; stream >> enabled; if (!stream.atEnd()) stream >> engineId; if (!stream.atEnd()) stream >> features; + if (!stream.atEnd()) { + stream >> flushInterval; + m_flushTimer.setInterval(flushInterval); + if (flushInterval > 0) { + connect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush())); + connect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start())); + connect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop())); + } else { + disconnect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush())); + disconnect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start())); + disconnect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop())); + } + } // If engineId == -1 objectForId() and then the cast will return 0. if (enabled) @@ -376,4 +404,23 @@ void QQmlProfilerService::messageReceived(const QByteArray &message) stopWaiting(); } +void QQmlProfilerServiceImpl::flush() +{ + QMutexLocker lock(&m_configMutex); + + foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers) { + if (profiler->isRunning()) { + m_startTimes.insert(-1, profiler); + profiler->reportData(); + } + } + + foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) { + if (profiler->isRunning()) { + m_startTimes.insert(-1, profiler); + profiler->reportData(); + } + } +} + QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofilerservice_p.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h index 978d8413a7..9b139ffabb 100644 --- a/src/qml/debugger/qqmlprofilerservice_p.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h @@ -45,10 +45,10 @@ // We mean it. // -#include "qqmlconfigurabledebugservice_p.h" -#include "qqmlprofilerdefinitions_p.h" -#include "qqmlabstractprofileradapter_p.h" - +#include "qqmlconfigurabledebugservice.h" +#include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qqmlprofilerdefinitions_p.h> +#include <private/qqmlabstractprofileradapter_p.h> #include <private/qqmlboundsignal_p.h> #include <QtCore/qelapsedtimer.h> @@ -57,6 +57,7 @@ #include <QtCore/qvector.h> #include <QtCore/qstringbuilder.h> #include <QtCore/qwaitcondition.h> +#include <QtCore/qtimer.h> #include <limits> @@ -66,12 +67,13 @@ class QUrl; class QQmlEngine; -class Q_QML_PRIVATE_EXPORT QQmlProfilerService : public QQmlConfigurableDebugService, public QQmlProfilerDefinitions +class QQmlProfilerServiceImpl : + public QQmlConfigurableDebugService<QQmlProfilerService>, + public QQmlProfilerDefinitions { Q_OBJECT public: - static QQmlProfilerService *instance(); void engineAboutToBeAdded(QQmlEngine *engine); void engineAboutToBeRemoved(QQmlEngine *engine); void engineAdded(QQmlEngine *engine); @@ -83,22 +85,32 @@ public: void startProfiling(QQmlEngine *engine, quint64 features = std::numeric_limits<quint64>::max()); void stopProfiling(QQmlEngine *engine); - QQmlProfilerService(); - ~QQmlProfilerService(); + QQmlProfilerServiceImpl(QObject *parent = 0); + ~QQmlProfilerServiceImpl(); void dataReady(QQmlAbstractProfilerAdapter *profiler); +signals: + void startFlushTimer(); + void stopFlushTimer(); + +private slots: + void flush(); + protected: virtual void stateAboutToBeChanged(State state); virtual void messageReceived(const QByteArray &); private: + friend class QQmlProfilerServiceFactory; void sendMessages(); void addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QQmlEngine *engine); void removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler); QElapsedTimer m_timer; + QTimer m_flushTimer; + bool m_waitingForStop; QList<QQmlAbstractProfilerAdapter *> m_globalProfilers; QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *> m_engineProfilers; diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.json b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.json new file mode 100644 index 0000000000..ec1ec364da --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "CanvasFrameRate", "EngineControl" ] +} diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp new file mode 100644 index 0000000000..83c2075246 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlprofilerservice.h" +#include "qqmlenginecontrolservice.h" +#include "qqmlprofilerservicefactory.h" + +QT_BEGIN_NAMESPACE + +QQmlDebugService *QQmlProfilerServiceFactory::create(const QString &key) +{ + if (key == QQmlProfilerServiceImpl::s_key) + return new QQmlProfilerServiceImpl(this); + + if (key == QQmlEngineControlService::s_key) + return new QQmlEngineControlService(this); + + return 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.h new file mode 100644 index 0000000000..b570136e5b --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILERSERVICE_H +#define QQMLPROFILERSERVICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qqmldebugservicefactory_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlProfilerServiceFactory : public QQmlDebugServiceFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlDebugServiceFactory_iid FILE "qqmlprofilerservice.json") +public: + QQmlDebugService *create(const QString &key); +}; + +QT_END_NAMESPACE + +#endif // QQMLPROFILERSERVICE_H diff --git a/src/qml/debugger/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp index 2b8183dc69..24e01f4c68 100644 --- a/src/qml/debugger/qv4profileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp @@ -31,14 +31,13 @@ ** ****************************************************************************/ -#include "qv4profileradapter_p.h" -#include "qqmlprofilerservice_p.h" -#include "qqmldebugservice_p.h" +#include "qv4profileradapter.h" +#include "qqmlprofilerservice.h" QT_BEGIN_NAMESPACE QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine) : - QQmlAbstractProfilerAdapter(service) + QQmlAbstractProfilerAdapter(service), dataPos(0), memoryPos(0) { engine->enableProfiler(); connect(this, SIGNAL(profilingEnabled(quint64)), @@ -51,45 +50,63 @@ QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::Execut connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData())); connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), engine->profiler, SLOT(setTimer(QElapsedTimer))); - connect(engine->profiler, SIGNAL(dataReady(QList<QV4::Profiling::FunctionCallProperties>, - QList<QV4::Profiling::MemoryAllocationProperties>)), - this, SLOT(receiveData(QList<QV4::Profiling::FunctionCallProperties>, - QList<QV4::Profiling::MemoryAllocationProperties>))); + connect(engine->profiler, SIGNAL(dataReady(QVector<QV4::Profiling::FunctionCallProperties>, + QVector<QV4::Profiling::MemoryAllocationProperties>)), + this, SLOT(receiveData(QVector<QV4::Profiling::FunctionCallProperties>, + QVector<QV4::Profiling::MemoryAllocationProperties>))); } qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &messages) { QByteArray message; - while (!memory_data.empty() && memory_data.front().timestamp <= until) { + while (memory_data.length() > memoryPos && memory_data[memoryPos].timestamp <= until) { QQmlDebugStream d(&message, QIODevice::WriteOnly); - QV4::Profiling::MemoryAllocationProperties &props = memory_data.front(); + QV4::Profiling::MemoryAllocationProperties &props = memory_data[memoryPos]; d << props.timestamp << MemoryAllocation << props.type << props.size; - memory_data.pop_front(); + ++memoryPos; messages.append(message); } - return memory_data.empty() ? -1 : memory_data.front().timestamp; + return memory_data.length() == memoryPos ? -1 : memory_data[memoryPos].timestamp; +} + +qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &messages, + qint64 callNext) +{ + if (callNext == -1) { + data.clear(); + dataPos = 0; + } + + qint64 memoryNext = appendMemoryEvents(until, messages); + + if (memoryNext == -1) { + memory_data.clear(); + memoryPos = 0; + return callNext; + } + + return callNext == -1 ? memoryNext : qMin(callNext, memoryNext); } qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) { QByteArray message; while (true) { - while (!stack.empty() && (data.empty() || stack.top() <= data.front().start)) { - if (stack.top() > until) { - qint64 memory_next = appendMemoryEvents(until, messages); - return memory_next == -1 ? stack.top() : qMin(stack.top(), memory_next); - } + while (!stack.isEmpty() && (dataPos == data.length() || + stack.top() <= data[dataPos].start)) { + if (stack.top() > until) + return finalizeMessages(until, messages, stack.top()); + appendMemoryEvents(stack.top(), messages); QQmlDebugStream d(&message, QIODevice::WriteOnly); d << stack.pop() << RangeEnd << Javascript; messages.append(message); } - while (!data.empty() && (stack.empty() || data.front().start < stack.top())) { - const QV4::Profiling::FunctionCallProperties &props = data.front(); - if (props.start > until) { - qint64 memory_next = appendMemoryEvents(until, messages); - return memory_next == -1 ? props.start : qMin(props.start, memory_next); - } + while (dataPos != data.length() && (stack.empty() || data[dataPos].start < stack.top())) { + const QV4::Profiling::FunctionCallProperties &props = data[dataPos]; + if (props.start > until) + return finalizeMessages(until, messages, props.start); + appendMemoryEvents(props.start, messages); QQmlDebugStream d_start(&message, QIODevice::WriteOnly); @@ -106,19 +123,30 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message messages.push_back(message); message.clear(); stack.push(props.end); - data.pop_front(); + ++dataPos; } - if (stack.empty() && data.empty()) - return appendMemoryEvents(until, messages); + if (stack.empty() && dataPos == data.length()) + return finalizeMessages(until, messages, -1); } } -void QV4ProfilerAdapter::receiveData(const QList<QV4::Profiling::FunctionCallProperties> &new_data, - const QList<QV4::Profiling::MemoryAllocationProperties> &new_memory_data) +void QV4ProfilerAdapter::receiveData( + const QVector<QV4::Profiling::FunctionCallProperties> &new_data, + const QVector<QV4::Profiling::MemoryAllocationProperties> &new_memory_data) { - data = new_data; - memory_data = new_memory_data; - stack.clear(); + // In rare cases it could be that another flush or stop event is processed while data from + // the previous one is still pending. In that case we just append the data. + + if (data.isEmpty()) + data = new_data; + else + data.append(new_data); + + if (memory_data.isEmpty()) + memory_data = new_memory_data; + else + memory_data.append(new_memory_data); + service->dataReady(this); } diff --git a/src/qml/debugger/qv4profileradapter_p.h b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h index 2f467f4beb..cea3da72e3 100644 --- a/src/qml/debugger/qv4profileradapter_p.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h @@ -45,8 +45,8 @@ // We mean it. // -#include "qv4profiling_p.h" -#include "qqmlabstractprofileradapter_p.h" +#include <private/qv4profiling_p.h> +#include <private/qqmlabstractprofileradapter_p.h> #include <QStack> #include <QList> @@ -63,14 +63,17 @@ public: virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages); public slots: - void receiveData(const QList<QV4::Profiling::FunctionCallProperties> &, - const QList<QV4::Profiling::MemoryAllocationProperties> &); + void receiveData(const QVector<QV4::Profiling::FunctionCallProperties> &, + const QVector<QV4::Profiling::MemoryAllocationProperties> &); private: - QList<QV4::Profiling::FunctionCallProperties> data; - QList<QV4::Profiling::MemoryAllocationProperties> memory_data; + QVector<QV4::Profiling::FunctionCallProperties> data; + QVector<QV4::Profiling::MemoryAllocationProperties> memory_data; + int dataPos; + int memoryPos; QStack<qint64> stack; qint64 appendMemoryEvents(qint64 until, QList<QByteArray> &messages); + qint64 finalizeMessages(qint64 until, QList<QByteArray> &messages, qint64 callNext); }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/qmldbg_qtquick2.pro b/src/plugins/qmltooling/qmldbg_qtquick2/qmldbg_qtquick2.pro deleted file mode 100644 index 2ee0d703b2..0000000000 --- a/src/plugins/qmltooling/qmldbg_qtquick2/qmldbg_qtquick2.pro +++ /dev/null @@ -1,27 +0,0 @@ -TARGET = qmldbg_qtquick2 -QT += qml-private quick-private core-private gui-private - -PLUGIN_TYPE = qmltooling -PLUGIN_CLASS_NAME = QtQuick2Plugin -load(qt_plugin) - -INCLUDEPATH *= $$PWD $$PWD/../shared - -SOURCES += \ - qtquick2plugin.cpp \ - highlight.cpp \ - qquickviewinspector.cpp \ - ../shared/abstracttool.cpp \ - ../shared/abstractviewinspector.cpp \ - inspecttool.cpp - -HEADERS += \ - qtquick2plugin.h \ - highlight.h \ - qquickviewinspector.h \ - ../shared/abstracttool.h \ - ../shared/abstractviewinspector.h \ - ../shared/qmlinspectorconstants.h \ - inspecttool.h - -OTHER_FILES += qtquick2plugin.json diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/qtquick2plugin.cpp b/src/plugins/qmltooling/qmldbg_qtquick2/qtquick2plugin.cpp deleted file mode 100644 index 88801ec9db..0000000000 --- a/src/plugins/qmltooling/qmldbg_qtquick2/qtquick2plugin.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qtquick2plugin.h" -#include "qquickviewinspector.h" - -#include <QtCore/qplugin.h> -#include <QtQml/private/qqmlinspectorservice_p.h> -#include <QtQuick/QQuickView> - -namespace QmlJSDebugger { -namespace QtQuick2 { - -QtQuick2Plugin::QtQuick2Plugin() : - m_inspector(0) -{ -} - -QtQuick2Plugin::~QtQuick2Plugin() -{ - delete m_inspector; -} - -bool QtQuick2Plugin::canHandleView(QObject *view) -{ - return qobject_cast<QQuickView*>(view); -} - -void QtQuick2Plugin::activate(QObject *view) -{ - QQuickView *qtQuickView = qobject_cast<QQuickView*>(view); - Q_ASSERT(qtQuickView); - m_inspector = new QQuickViewInspector(qtQuickView, qtQuickView); -} - -void QtQuick2Plugin::deactivate() -{ - delete m_inspector; -} - -void QtQuick2Plugin::clientMessage(const QByteArray &message) -{ - if (m_inspector) - m_inspector->handleMessage(message); -} - -} // namespace QtQuick2 -} // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/qtquick2plugin.h b/src/plugins/qmltooling/qmldbg_qtquick2/qtquick2plugin.h deleted file mode 100644 index 6866e74aa1..0000000000 --- a/src/plugins/qmltooling/qmldbg_qtquick2/qtquick2plugin.h +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QTQUICK2PLUGINPLUGIN_H -#define QTQUICK2PLUGINPLUGIN_H - -#include <QtCore/QPointer> -#include <QtQml/private/qqmlinspectorinterface_p.h> - -namespace QmlJSDebugger { - -class AbstractViewInspector; - -namespace QtQuick2 { - -class QtQuick2Plugin : public QObject, public QQmlInspectorInterface -{ - Q_OBJECT - Q_DISABLE_COPY(QtQuick2Plugin) - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlInspectorInterface") - Q_INTERFACES(QQmlInspectorInterface) - -public: - QtQuick2Plugin(); - ~QtQuick2Plugin(); - - // QQmlInspectorInterface - bool canHandleView(QObject *view); - void activate(QObject *view); - void deactivate(); - void clientMessage(const QByteArray &message); - -private: - QPointer<AbstractViewInspector> m_inspector; -}; - -} // namespace QtQuick2 -} // namespace QmlJSDebugger - -#endif // QTQUICK2PLUGINPLUGIN_H diff --git a/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro b/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro new file mode 100644 index 0000000000..5e2d0874df --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro @@ -0,0 +1,22 @@ +TARGET = qmldbg_server +QT = qml-private core-private + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QQmlDebugServerFactory +load(qt_plugin) + +SOURCES += \ + $$PWD/qqmldebugserver.cpp \ + $$PWD/../shared/qpacketprotocol.cpp + +HEADERS += \ + $$PWD/qqmldebugserverfactory.h \ + $$PWD/../shared/qqmldebugserver.h \ + $$PWD/../shared/qpacketprotocol.h \ + $$PWD/../shared/qqmldebugserverconnection.h + +INCLUDEPATH += $$PWD \ + $$PWD/../shared + +OTHER_FILES += \ + qqmldebugserver.json diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp new file mode 100644 index 0000000000..1cfebea03c --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp @@ -0,0 +1,700 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldebugserver.h" +#include "qqmldebugserverfactory.h" +#include "qpacketprotocol.h" +#include "qqmldebugserverconnection.h" + +#include <private/qqmldebugservice_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlglobal_p.h> +#include <private/qqmldebugpluginmanager_p.h> + +#include <QtCore/QAtomicInt> +#include <QtCore/QDir> +#include <QtCore/QPluginLoader> +#include <QtCore/QStringList> +#include <QtCore/qwaitcondition.h> + +#include <private/qobject_p.h> +#include <private/qcoreapplication_p.h> + +QT_BEGIN_NAMESPACE + +/* + QQmlDebug Protocol (Version 1): + + handshake: + 1. Client sends + "QDeclarativeDebugServer" 0 version pluginNames [QDataStream version] + version: an int representing the highest protocol version the client knows + pluginNames: plugins available on client side + 2. Server sends + "QDeclarativeDebugClient" 0 version pluginNames pluginVersions [QDataStream version] + version: an int representing the highest protocol version the client & server know + pluginNames: plugins available on server side. plugins both in the client and server message are enabled. + client plugin advertisement + 1. Client sends + "QDeclarativeDebugServer" 1 pluginNames + server plugin advertisement (not implemented: all services are required to register before open()) + 1. Server sends + "QDeclarativeDebugClient" 1 pluginNames pluginVersions + plugin communication: + Everything send with a header different to "QDeclarativeDebugServer" is sent to the appropriate plugin. + */ + +Q_QML_DEBUG_PLUGIN_LOADER(QQmlDebugServerConnection) +Q_QML_IMPORT_DEBUG_PLUGIN(QTcpServerConnectionFactory) +Q_QML_IMPORT_DEBUG_PLUGIN(QLocalClientConnectionFactory) + +const int protocolVersion = 1; + +class QQmlDebugServerImpl; +class QQmlDebugServerThread : public QThread +{ +public: + QQmlDebugServerThread() : m_server(0), m_portFrom(-1), m_portTo(-1) {} + + void setServer(QQmlDebugServerImpl *server) + { + m_server = server; + } + + void setPortRange(int portFrom, int portTo, const QString &hostAddress) + { + m_pluginName = QLatin1String("QTcpServerConnection"); + m_portFrom = portFrom; + m_portTo = portTo; + m_hostAddress = hostAddress; + } + + void setFileName(const QString &fileName) + { + m_pluginName = QLatin1String("QLocalClientConnection"); + m_fileName = fileName; + } + + void run(); + +private: + QQmlDebugServerImpl *m_server; + QString m_pluginName; + int m_portFrom; + int m_portTo; + bool m_block; + QString m_hostAddress; + QString m_fileName; +}; + +class QQmlDebugServerImpl : public QQmlDebugServer +{ + Q_OBJECT +public: + QQmlDebugServerImpl(); + + bool blockingMode() const; + + QQmlDebugService *service(const QString &name) const; + + void addEngine(QQmlEngine *engine); + void removeEngine(QQmlEngine *engine); + + bool addService(const QString &name, QQmlDebugService *service); + bool removeService(const QString &name); + + bool open(const QVariantHash &configuration); + void setDevice(QIODevice *socket); + + void parseArguments(); + + static void cleanup(); + +private slots: + void wakeEngine(QQmlEngine *engine); + void sendMessage(const QString &name, const QByteArray &message); + void sendMessages(const QString &name, const QList<QByteArray> &messages); + void changeServiceState(const QString &serviceName, QQmlDebugService::State state); + void removeThread(); + void receiveMessage(); + void invalidPacket(); + +private: + friend class QQmlDebugServerThread; + friend class QQmlDebugServerFactory; + + class EngineCondition { + public: + EngineCondition() : numServices(0), condition(new QWaitCondition) {} + + bool waitForServices(QMutex *locked, int numEngines); + + void wake(); + private: + int numServices; + + // shared pointer to allow for QHash-inflicted copying. + QSharedPointer<QWaitCondition> condition; + }; + + bool canSendMessage(const QString &name); + void doSendMessage(const QString &name, const QByteArray &message); + + QQmlDebugServerConnection *m_connection; + QHash<QString, QQmlDebugService *> m_plugins; + QStringList m_clientPlugins; + bool m_gotHello; + bool m_blockingMode; + + QHash<QQmlEngine *, EngineCondition> m_engineConditions; + + QMutex m_helloMutex; + QWaitCondition m_helloCondition; + QQmlDebugServerThread m_thread; + QPacketProtocol *m_protocol; + QAtomicInt m_changeServiceStateCalls; +}; + +void QQmlDebugServerImpl::cleanup() +{ + QQmlDebugServerImpl *server = static_cast<QQmlDebugServerImpl *>( + QQmlDebugConnector::instance()); + if (!server) + return; + + for (QHash<QString, QQmlDebugService *>::ConstIterator i = server->m_plugins.constBegin(); + i != server->m_plugins.constEnd(); ++i) { + server->m_changeServiceStateCalls.ref(); + QMetaObject::invokeMethod(server, "changeServiceState", Qt::QueuedConnection, + Q_ARG(QString, i.key()), + Q_ARG(QQmlDebugService::State, + QQmlDebugService::NotConnected)); + } + + // Wait for changeServiceState calls to finish + // (while running an event loop because some services + // might again use slots to execute stuff in the GUI thread) + QEventLoop loop; + while (!server->m_changeServiceStateCalls.testAndSetOrdered(0, 0)) + loop.processEvents(); + + // Stop the thread while the application is still there. + server->m_thread.exit(); + server->m_thread.wait(); +} + +void QQmlDebugServerThread::run() +{ + Q_ASSERT_X(m_server != 0, Q_FUNC_INFO, "There should always be a debug server available here."); + QQmlDebugServerConnection *connection = loadQQmlDebugServerConnection(m_pluginName); + if (connection) { + connection->setServer(m_server); + + if (m_fileName.isEmpty()) { + if (!connection->setPortRange(m_portFrom, m_portTo, m_server->blockingMode(), + m_hostAddress)) { + delete connection; + return; + } + } else { + if (!connection->setFileName(m_fileName, m_server->blockingMode())) { + delete connection; + return; + } + } + + { + QMutexLocker connectionLocker(&m_server->m_helloMutex); + m_server->m_connection = connection; + m_server->m_helloCondition.wakeAll(); + } + + if (m_server->blockingMode()) + connection->waitForConnection(); + } else { + qWarning() << "QML Debugger: Couldn't load plugin" << m_pluginName; + return; + } + + exec(); + + // make sure events still waiting are processed + QEventLoop eventLoop; + eventLoop.processEvents(QEventLoop::AllEvents); +} + +bool QQmlDebugServerImpl::blockingMode() const +{ + return m_blockingMode; +} + +static void cleanupOnShutdown() +{ + // We cannot do this in the destructor as the connection plugin will get unloaded before the + // server plugin and we need the connection to send any remaining data. This function is + // triggered before any plugins are unloaded. + QQmlDebugServerImpl::cleanup(); +} + +QQmlDebugServerImpl::QQmlDebugServerImpl() : + m_connection(0), + m_gotHello(false), + m_blockingMode(false) +{ + static bool postRoutineAdded = false; + if (!postRoutineAdded) { + qAddPostRoutine(cleanupOnShutdown); + postRoutineAdded = true; + } + + // used in sendMessages + qRegisterMetaType<QList<QByteArray> >("QList<QByteArray>"); + // used in changeServiceState + qRegisterMetaType<QQmlDebugService::State>("QQmlDebugService::State"); + + m_thread.setServer(this); + moveToThread(&m_thread); + + // Remove the thread immmediately when it finishes, so that we don't have to wait for the + // event loop to signal that. + QObject::connect(&m_thread, SIGNAL(finished()), this, SLOT(removeThread()), + Qt::DirectConnection); + m_thread.setObjectName(QStringLiteral("QQmlDebugServerThread")); + parseArguments(); +} + +bool QQmlDebugServerImpl::open(const QVariantHash &configuration = QVariantHash()) +{ + if (m_thread.isRunning()) + return false; + if (!configuration.isEmpty()) { + m_blockingMode = configuration[QLatin1String("block")].toBool(); + if (configuration.contains(QLatin1String("portFrom"))) { + int portFrom = configuration[QLatin1String("portFrom")].toInt(); + int portTo = configuration[QLatin1String("portTo")].toInt(); + m_thread.setPortRange(portFrom, portTo == -1 ? portFrom : portTo, + configuration[QLatin1String("hostAddress")].toString()); + } else if (configuration.contains(QLatin1String("fileName"))) { + m_thread.setFileName(configuration[QLatin1String("fileName")].toString()); + } else { + return false; + } + } + + QMutexLocker locker(&m_helloMutex); + m_thread.start(); + m_helloCondition.wait(&m_helloMutex); // wait for connection + if (m_blockingMode && !m_gotHello) + m_helloCondition.wait(&m_helloMutex); // wait for hello + return true; +} + +void QQmlDebugServerImpl::parseArguments() +{ + // format: qmljsdebugger=port:<port_from>[,port_to],host:<ip address>][,block] + const QString args = commandLineArguments(); + if (args.isEmpty()) + return; // Manual initialization, through QQmlDebugServer::open() + + // ### remove port definition when protocol is changed + int portFrom = 0; + int portTo = 0; + bool block = false; + bool ok = false; + QString hostAddress; + QString fileName; + QStringList services; + + const QStringList lstjsDebugArguments = args.split(QLatin1Char(',')); + QStringList::const_iterator argsItEnd = lstjsDebugArguments.cend(); + QStringList::const_iterator argsIt = lstjsDebugArguments.cbegin(); + for (; argsIt != argsItEnd; ++argsIt) { + const QString strArgument = *argsIt; + if (strArgument.startsWith(QLatin1String("port:"))) { + portFrom = strArgument.mid(5).toInt(&ok); + portTo = portFrom; + QStringList::const_iterator argsNext = argsIt + 1; + if (argsNext == argsItEnd) + break; + const QString nextArgument = *argsNext; + + // Don't use QStringLiteral here. QRegExp has a global cache and will save an implicitly + // shared copy of the passed string. That copy isn't properly detached when the library + // is unloaded if the original string lives in the library's .rodata + if (ok && nextArgument.contains(QRegExp(QLatin1String("^\\s*\\d+\\s*$")))) { + portTo = nextArgument.toInt(&ok); + ++argsIt; + } + } else if (strArgument.startsWith(QLatin1String("host:"))) { + hostAddress = strArgument.mid(5); + } else if (strArgument == QLatin1String("block")) { + block = true; + } else if (strArgument.startsWith(QLatin1String("file:"))) { + fileName = strArgument.mid(5); + ok = !fileName.isEmpty(); + } else if (strArgument.startsWith(QLatin1String("services:"))) { + services.append(strArgument.mid(9)); + } else if (!services.isEmpty()) { + services.append(strArgument); + } else { + qWarning() << QString::fromLatin1("QML Debugger: Invalid argument '%1' " + "detected. Ignoring the same.") + .arg(strArgument); + } + } + + if (ok) { + setServices(services); + m_blockingMode = block; + if (!fileName.isEmpty()) + m_thread.setFileName(fileName); + else + m_thread.setPortRange(portFrom, portTo, hostAddress); + } else { + qWarning() << QString::fromLatin1("QML Debugger: Ignoring \"-qmljsdebugger=%1\". " + "Format is qmljsdebugger=port:<port_from>[,port_to],host:" + "<ip address>][,block]").arg(args); + } +} + +void QQmlDebugServerImpl::receiveMessage() +{ + typedef QHash<QString, QQmlDebugService*>::const_iterator DebugServiceConstIt; + + // to be executed in debugger thread + Q_ASSERT(QThread::currentThread() == thread()); + + if (!m_protocol) + return; + + QQmlDebugStream in(m_protocol->read().data()); + + QString name; + + in >> name; + if (name == QLatin1String("QDeclarativeDebugServer")) { + int op = -1; + in >> op; + if (op == 0) { + int version; + in >> version >> m_clientPlugins; + + //Get the supported QDataStream version + if (!in.atEnd()) { + in >> QQmlDebugStream::s_dataStreamVersion; + if (QQmlDebugStream::s_dataStreamVersion > QDataStream().version()) + QQmlDebugStream::s_dataStreamVersion = QDataStream().version(); + } + + // Send the hello answer immediately, since it needs to arrive before + // the plugins below start sending messages. + + QByteArray helloAnswer; + QQmlDebugStream out(&helloAnswer, QIODevice::WriteOnly); + QStringList pluginNames; + QList<float> pluginVersions; + const int count = m_plugins.count(); + pluginNames.reserve(count); + pluginVersions.reserve(count); + for (QHash<QString, QQmlDebugService *>::ConstIterator i = m_plugins.constBegin(); + i != m_plugins.constEnd(); ++i) { + pluginNames << i.key(); + pluginVersions << i.value()->version(); + } + + out << QString(QStringLiteral("QDeclarativeDebugClient")) << 0 << protocolVersion + << pluginNames << pluginVersions << QQmlDebugStream::s_dataStreamVersion; + + QPacket pack; + pack.writeRawData(helloAnswer.data(), helloAnswer.length()); + m_protocol->send(pack); + m_connection->flush(); + + QMutexLocker helloLock(&m_helloMutex); + m_gotHello = true; + + for (DebugServiceConstIt iter = m_plugins.constBegin(), cend = m_plugins.constEnd(); iter != cend; ++iter) { + QQmlDebugService::State newState = QQmlDebugService::Unavailable; + if (m_clientPlugins.contains(iter.key())) + newState = QQmlDebugService::Enabled; + m_changeServiceStateCalls.ref(); + changeServiceState(iter.key(), newState); + } + + m_helloCondition.wakeAll(); + + } else if (op == 1) { + // Service Discovery + QStringList oldClientPlugins = m_clientPlugins; + in >> m_clientPlugins; + + for (DebugServiceConstIt iter = m_plugins.constBegin(), cend = m_plugins.constEnd(); iter != cend; ++iter) { + const QString pluginName = iter.key(); + QQmlDebugService::State newState = QQmlDebugService::Unavailable; + if (m_clientPlugins.contains(pluginName)) + newState = QQmlDebugService::Enabled; + + if (oldClientPlugins.contains(pluginName) + != m_clientPlugins.contains(pluginName)) { + m_changeServiceStateCalls.ref(); + changeServiceState(iter.key(), newState); + } + } + + } else { + qWarning("QML Debugger: Invalid control message %d.", op); + invalidPacket(); + return; + } + + } else { + if (m_gotHello) { + QByteArray message; + in >> message; + + QHash<QString, QQmlDebugService *>::Iterator iter = m_plugins.find(name); + if (iter == m_plugins.end()) { + qWarning() << "QML Debugger: Message received for missing plugin" << name << '.'; + } else { + (*iter)->messageReceived(message); + } + } else { + qWarning("QML Debugger: Invalid hello message."); + } + + } +} + +void QQmlDebugServerImpl::changeServiceState(const QString &serviceName, + QQmlDebugService::State newState) +{ + // to be executed in debugger thread + Q_ASSERT(QThread::currentThread() == thread()); + + QQmlDebugService *service = m_plugins.value(serviceName); + if (service && service->state() != newState) { + service->stateAboutToBeChanged(newState); + service->setState(newState); + service->stateChanged(newState); + } + + m_changeServiceStateCalls.deref(); +} + +void QQmlDebugServerImpl::removeThread() +{ + Q_ASSERT(m_thread.isFinished()); + Q_ASSERT(QThread::currentThread() == thread()); + + QThread *parentThread = m_thread.thread(); + + delete m_connection; + m_connection = 0; + + // Move it back to the parent thread so that we can potentially restart it on a new thread. + moveToThread(parentThread); +} + +QQmlDebugService *QQmlDebugServerImpl::service(const QString &name) const +{ + return m_plugins.value(name); +} + +void QQmlDebugServerImpl::addEngine(QQmlEngine *engine) +{ + // to be executed outside of debugger thread + Q_ASSERT(QThread::currentThread() != &m_thread); + + QMutexLocker locker(&m_helloMutex); + foreach (QQmlDebugService *service, m_plugins) + service->engineAboutToBeAdded(engine); + + m_engineConditions[engine].waitForServices(&m_helloMutex, m_plugins.count()); + + foreach (QQmlDebugService *service, m_plugins) + service->engineAdded(engine); +} + +void QQmlDebugServerImpl::removeEngine(QQmlEngine *engine) +{ + // to be executed outside of debugger thread + Q_ASSERT(QThread::currentThread() != &m_thread); + + QMutexLocker locker(&m_helloMutex); + foreach (QQmlDebugService *service, m_plugins) + service->engineAboutToBeRemoved(engine); + + m_engineConditions[engine].waitForServices(&m_helloMutex, m_plugins.count()); + + foreach (QQmlDebugService *service, m_plugins) + service->engineRemoved(engine); +} + +bool QQmlDebugServerImpl::addService(const QString &name, QQmlDebugService *service) +{ + // to be executed before thread starts + Q_ASSERT(!m_thread.isRunning()); + + if (!service || m_plugins.contains(name)) + return false; + + connect(service, SIGNAL(messageToClient(QString,QByteArray)), + this, SLOT(sendMessage(QString,QByteArray))); + connect(service, SIGNAL(messagesToClient(QString,QList<QByteArray>)), + this, SLOT(sendMessages(QString,QList<QByteArray>))); + + connect(service, SIGNAL(attachedToEngine(QQmlEngine*)), + this, SLOT(wakeEngine(QQmlEngine*)), Qt::QueuedConnection); + connect(service, SIGNAL(detachedFromEngine(QQmlEngine*)), + this, SLOT(wakeEngine(QQmlEngine*)), Qt::QueuedConnection); + + service->setState(QQmlDebugService::Unavailable); + m_plugins.insert(name, service); + + return true; +} + +bool QQmlDebugServerImpl::removeService(const QString &name) +{ + // to be executed after thread ends + Q_ASSERT(!m_thread.isRunning()); + + QQmlDebugService *service = m_plugins.value(name); + if (!service) + return false; + + m_plugins.remove(name); + service->setState(QQmlDebugService::NotConnected); + + disconnect(service, SIGNAL(detachedFromEngine(QQmlEngine*)), + this, SLOT(wakeEngine(QQmlEngine*))); + disconnect(service, SIGNAL(attachedToEngine(QQmlEngine*)), + this, SLOT(wakeEngine(QQmlEngine*))); + + disconnect(service, SIGNAL(messagesToClient(QString,QList<QByteArray>)), + this, SLOT(sendMessages(QString,QList<QByteArray>))); + disconnect(service, SIGNAL(messageToClient(QString,QByteArray)), + this, SLOT(sendMessage(QString,QByteArray))); + + m_plugins.remove(service->name()); + + return true; +} + +bool QQmlDebugServerImpl::canSendMessage(const QString &name) +{ + // to be executed in debugger thread + Q_ASSERT(QThread::currentThread() == thread()); + return m_connection && m_connection->isConnected() && m_protocol && + m_clientPlugins.contains(name); +} + +void QQmlDebugServerImpl::doSendMessage(const QString &name, const QByteArray &message) +{ + QByteArray prefixed; + QQmlDebugStream out(&prefixed, QIODevice::WriteOnly); + out << name << message; + + QPacket pack; + pack.writeRawData(prefixed.data(), prefixed.length()); + m_protocol->send(pack); +} + +void QQmlDebugServerImpl::sendMessage(const QString &name, const QByteArray &message) +{ + if (canSendMessage(name)) { + doSendMessage(name, message); + m_connection->flush(); + } +} + +void QQmlDebugServerImpl::sendMessages(const QString &name, const QList<QByteArray> &messages) +{ + if (canSendMessage(name)) { + foreach (const QByteArray &message, messages) + doSendMessage(name, message); + m_connection->flush(); + } +} + +void QQmlDebugServerImpl::wakeEngine(QQmlEngine *engine) +{ + // to be executed in debugger thread + Q_ASSERT(QThread::currentThread() == thread()); + + QMutexLocker locker(&m_helloMutex); + m_engineConditions[engine].wake(); +} + +bool QQmlDebugServerImpl::EngineCondition::waitForServices(QMutex *locked, int num) +{ + Q_ASSERT_X(numServices == 0, Q_FUNC_INFO, "Request to wait again before previous wait finished"); + numServices = num; + return numServices > 0 ? condition->wait(locked) : true; +} + +void QQmlDebugServerImpl::EngineCondition::wake() +{ + if (--numServices == 0) + condition->wakeAll(); + Q_ASSERT_X(numServices >=0, Q_FUNC_INFO, "Woken more often than #services."); +} + +void QQmlDebugServerImpl::setDevice(QIODevice *socket) +{ + m_protocol = new QPacketProtocol(socket, this); + QObject::connect(m_protocol, SIGNAL(readyRead()), this, SLOT(receiveMessage())); + QObject::connect(m_protocol, SIGNAL(invalidPacket()), this, SLOT(invalidPacket())); + + if (blockingMode()) + m_protocol->waitForReadyRead(-1); +} + +void QQmlDebugServerImpl::invalidPacket() +{ + qWarning("QML Debugger: Received a corrupted packet! Giving up ..."); + m_connection->disconnect(); + // protocol might still be processing packages at this point + m_protocol->deleteLater(); + m_protocol = 0; +} + +QQmlDebugConnector *QQmlDebugServerFactory::create(const QString &key) +{ + // Cannot parent it to this because it gets moved to another thread + return (key == QLatin1String("QQmlDebugServer") ? new QQmlDebugServerImpl : 0); +} + +QT_END_NAMESPACE + +#include "qqmldebugserver.moc" diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.json b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.json new file mode 100644 index 0000000000..9b8dd3ae79 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "QQmlDebugServer" ] +} diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.h b/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.h new file mode 100644 index 0000000000..825a71bab8 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserverfactory.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDEBUGSERVERFACTORY_H +#define QQMLDEBUGSERVERFACTORY_H + +#include <private/qqmldebugconnector_p.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// ementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QQmlDebugServerFactory : public QQmlDebugConnectorFactory +{ + Q_OBJECT + + // The interface for the code that loads this thing is QQmlDebugConnector. + // QQmlDebugServer is for connection plugins. + Q_PLUGIN_METADATA(IID QQmlDebugConnectorFactory_iid FILE "qqmldebugserver.json") +public: + QQmlDebugConnector *create(const QString &key); +}; + +QT_END_NAMESPACE + +#endif // QQMLDEBUGSERVERFACTORY_H diff --git a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pri b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pri deleted file mode 100644 index 4b78707c3d..0000000000 --- a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pri +++ /dev/null @@ -1,14 +0,0 @@ -QT += network core-private - -SOURCES += \ - $$PWD/qtcpserverconnection.cpp \ - $$PWD/../shared/qpacketprotocol.cpp - -HEADERS += \ - $$PWD/qtcpserverconnection.h \ - $$PWD/../shared/qpacketprotocol.h - -INCLUDEPATH += $$PWD \ - $$PWD/../shared - -OTHER_FILES += $$PWD/qtcpserverconnection.json diff --git a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro index e1284b7233..fd419aeb56 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro +++ b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro @@ -1,8 +1,20 @@ TARGET = qmldbg_tcp -QT = qml-private core-private +QT = qml-private network PLUGIN_TYPE = qmltooling -PLUGIN_CLASS_NAME = QTcpServerConnection +PLUGIN_CLASS_NAME = QTcpServerConnectionFactory load(qt_plugin) -include(qmldbg_tcp.pri) +SOURCES += \ + $$PWD/qtcpserverconnection.cpp + +HEADERS += \ + $$PWD/qtcpserverconnectionfactory.h \ + $$PWD/../shared/qqmldebugserver.h \ + $$PWD/../shared/qqmldebugserverconnection.h + +INCLUDEPATH += $$PWD \ + $$PWD/../shared + +OTHER_FILES += \ + $$PWD/qtcpserverconnection.json diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp index 2ae4edfce4..c8010a4aa9 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp @@ -31,136 +31,125 @@ ** ****************************************************************************/ -#include "qtcpserverconnection.h" -#include "qpacketprotocol.h" +#include "qtcpserverconnectionfactory.h" +#include "qqmldebugserver.h" #include <QtCore/qplugin.h> #include <QtNetwork/qtcpserver.h> #include <QtNetwork/qtcpsocket.h> -#include <private/qqmldebugserver_p.h> - QT_BEGIN_NAMESPACE -class QTcpServerConnectionPrivate { +class QTcpServerConnection : public QQmlDebugServerConnection +{ + Q_OBJECT + Q_DISABLE_COPY(QTcpServerConnection) + public: - QTcpServerConnectionPrivate(); + QTcpServerConnection(); + ~QTcpServerConnection(); - int portFrom; - int portTo; - bool block; - QString hostaddress; - QTcpSocket *socket; - QPacketProtocol *protocol; - QTcpServer *tcpServer; + void setServer(QQmlDebugServer *server); + bool setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress); + bool setFileName(const QString &fileName, bool block); - QQmlDebugServer *debugServer; -}; + bool isConnected() const; + void disconnect(); -QTcpServerConnectionPrivate::QTcpServerConnectionPrivate() : - portFrom(0), - portTo(0), - block(false), - socket(0), - protocol(0), - tcpServer(0), - debugServer(0) -{ -} + void waitForConnection(); + void flush(); + +private slots: + void newConnection(); + +private: + bool listen(); + + int m_portFrom; + int m_portTo; + bool m_block; + QString m_hostaddress; + QTcpSocket *m_socket; + QTcpServer *m_tcpServer; + QQmlDebugServer *m_debugServer; +}; QTcpServerConnection::QTcpServerConnection() : - d_ptr(new QTcpServerConnectionPrivate) + m_portFrom(0), + m_portTo(0), + m_block(false), + m_socket(0), + m_tcpServer(0), + m_debugServer(0) { - } QTcpServerConnection::~QTcpServerConnection() { if (isConnected()) disconnect(); - delete d_ptr; } void QTcpServerConnection::setServer(QQmlDebugServer *server) { - Q_D(QTcpServerConnection); - d->debugServer = server; + m_debugServer = server; } bool QTcpServerConnection::isConnected() const { - Q_D(const QTcpServerConnection); - return d->socket && d->socket->state() == QTcpSocket::ConnectedState; -} - -void QTcpServerConnection::send(const QList<QByteArray> &messages) -{ - Q_D(QTcpServerConnection); - - if (!isConnected() - || !d->protocol || !d->socket) - return; - - foreach (const QByteArray &message, messages) { - QPacket pack; - pack.writeRawData(message.data(), message.length()); - d->protocol->send(pack); - } - d->socket->flush(); + return m_socket && m_socket->state() == QTcpSocket::ConnectedState; } void QTcpServerConnection::disconnect() { - Q_D(QTcpServerConnection); - - while (d->socket && d->socket->bytesToWrite() > 0) { - if (!d->socket->waitForBytesWritten()) { + while (m_socket && m_socket->bytesToWrite() > 0) { + if (!m_socket->waitForBytesWritten()) { qWarning("QML Debugger: Failed to send remaining %lld bytes on disconnect.", - d->socket->bytesToWrite()); + m_socket->bytesToWrite()); break; } } - // protocol might still be processing packages at this point - d->protocol->deleteLater(); - d->protocol = 0; - d->socket->deleteLater(); - d->socket = 0; -} - -bool QTcpServerConnection::waitForMessage() -{ - Q_D(QTcpServerConnection); - return d->protocol->waitForReadyRead(-1); + m_socket->deleteLater(); + m_socket = 0; } bool QTcpServerConnection::setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress) { - Q_D(QTcpServerConnection); - d->portFrom = portFrom; - d->portTo = portTo; - d->block = block; - d->hostaddress = hostaddress; + m_portFrom = portFrom; + m_portTo = portTo; + m_block = block; + m_hostaddress = hostaddress; return listen(); } +bool QTcpServerConnection::setFileName(const QString &fileName, bool block) +{ + Q_UNUSED(fileName); + Q_UNUSED(block); + return false; +} + void QTcpServerConnection::waitForConnection() { - Q_D(QTcpServerConnection); - d->tcpServer->waitForNewConnection(-1); + m_tcpServer->waitForNewConnection(-1); } -bool QTcpServerConnection::listen() +void QTcpServerConnection::flush() { - Q_D(QTcpServerConnection); + if (m_socket) + m_socket->flush(); +} - d->tcpServer = new QTcpServer(this); - QObject::connect(d->tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); +bool QTcpServerConnection::listen() +{ + m_tcpServer = new QTcpServer(this); + QObject::connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); QHostAddress hostaddress; - if (!d->hostaddress.isEmpty()) { - if (!hostaddress.setAddress(d->hostaddress)) { + if (!m_hostaddress.isEmpty()) { + if (!hostaddress.setAddress(m_hostaddress)) { hostaddress = QHostAddress::Any; qDebug("QML Debugger: Incorrect host address provided. So accepting connections " "from any host."); @@ -168,64 +157,45 @@ bool QTcpServerConnection::listen() } else { hostaddress = QHostAddress::Any; } - int port = d->portFrom; + int port = m_portFrom; do { - if (d->tcpServer->listen(hostaddress, port)) { + if (m_tcpServer->listen(hostaddress, port)) { qDebug("QML Debugger: Waiting for connection on port %d...", port); break; } ++port; - } while (port <= d->portTo); - if (port > d->portTo) { - if (d->portFrom == d->portTo) - qWarning("QML Debugger: Unable to listen to port %d.", d->portFrom); + } while (port <= m_portTo); + if (port > m_portTo) { + if (m_portFrom == m_portTo) + qWarning("QML Debugger: Unable to listen to port %d.", m_portFrom); else - qWarning("QML Debugger: Unable to listen to ports %d - %d.", d->portFrom, d->portTo); + qWarning("QML Debugger: Unable to listen to ports %d - %d.", m_portFrom, m_portTo); return false; } else { return true; } } - -void QTcpServerConnection::readyRead() -{ - Q_D(QTcpServerConnection); - if (!d->protocol) - return; - - QPacket packet = d->protocol->read(); - - QByteArray content = packet.data(); - d->debugServer->receiveMessage(content); -} - void QTcpServerConnection::newConnection() { - Q_D(QTcpServerConnection); - - if (d->socket && d->socket->peerPort()) { + if (m_socket && m_socket->peerPort()) { qWarning("QML Debugger: Another client is already connected."); - QTcpSocket *faultyConnection = d->tcpServer->nextPendingConnection(); + QTcpSocket *faultyConnection = m_tcpServer->nextPendingConnection(); delete faultyConnection; return; } - delete d->socket; - d->socket = d->tcpServer->nextPendingConnection(); - d->socket->setParent(this); - d->protocol = new QPacketProtocol(d->socket, this); - QObject::connect(d->protocol, SIGNAL(readyRead()), this, SLOT(readyRead())); - QObject::connect(d->protocol, SIGNAL(invalidPacket()), this, SLOT(invalidPacket())); - - if (d->block) { - d->protocol->waitForReadyRead(-1); - } + delete m_socket; + m_socket = m_tcpServer->nextPendingConnection(); + m_socket->setParent(this); + m_debugServer->setDevice(m_socket); } -void QTcpServerConnection::invalidPacket() +QQmlDebugServerConnection *QTcpServerConnectionFactory::create(const QString &key) { - qWarning("QML Debugger: Received a corrupted packet! Giving up ..."); + return (key == QLatin1String("QTcpServerConnection") ? new QTcpServerConnection : 0); } QT_END_NAMESPACE + +#include "qtcpserverconnection.moc" diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.json b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.json new file mode 100644 index 0000000000..201a1b3fcb --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "QTcpServerConnection" ] +} diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnectionfactory.h b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnectionfactory.h new file mode 100644 index 0000000000..97dde03087 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnectionfactory.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCPSERVERCONNECTIONFACTORY_H +#define QTCPSERVERCONNECTIONFACTORY_H + +#include "qqmldebugserverconnection.h" + +QT_BEGIN_NAMESPACE + +class QTcpServerConnectionFactory : public QQmlDebugServerConnectionFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlDebugServerConnectionFactory_iid FILE "qtcpserverconnection.json") + Q_INTERFACES(QQmlDebugServerConnectionFactory) +public: + QQmlDebugServerConnection *create(const QString &key); +}; + +QT_END_NAMESPACE + +#endif // QTCPSERVERCONNECTIONFACTORY_H diff --git a/src/plugins/qmltooling/qmltooling.pro b/src/plugins/qmltooling/qmltooling.pro index 3bc48a6b33..ae13826a4c 100644 --- a/src/plugins/qmltooling/qmltooling.pro +++ b/src/plugins/qmltooling/qmltooling.pro @@ -1,4 +1,10 @@ TEMPLATE = subdirs -SUBDIRS = qmldbg_tcp -qtHaveModule(quick): SUBDIRS += qmldbg_qtquick2 +SUBDIRS += \ + qmldbg_debugger \ + qmldbg_local \ + qmldbg_profiler \ + qmldbg_server \ + qmldbg_tcp + +qtHaveModule(quick): SUBDIRS += qmldbg_inspector diff --git a/src/plugins/qmltooling/shared/qmlinspectorconstants.h b/src/plugins/qmltooling/shared/qmlinspectorconstants.h deleted file mode 100644 index d76e172844..0000000000 --- a/src/plugins/qmltooling/shared/qmlinspectorconstants.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QMLINSPECTORCONSTANTS_H -#define QMLINSPECTORCONSTANTS_H - -#include <QtQml/private/qqmlglobal_p.h> - -namespace QmlJSDebugger { -namespace Constants { - -enum DesignTool { - NoTool = 0, - SelectionToolMode = 1, - MarqueeSelectionToolMode = 2, - MoveToolMode = 3, - ResizeToolMode = 4, - ZoomMode = 6 -}; - -static const int PressAndHoldTimeout = 800; - -static const double ZoomSnapDelta = 0.04; - -static const int EditorItemDataKey = 1000; - -enum GraphicsItemTypes { - EditorItemType = 0xEAAA, - ResizeHandleItemType = 0xEAEA -}; - - -} // namespace Constants -} // namespace QmlJSDebugger - -#endif // QMLINSPECTORCONSTANTS_H diff --git a/src/plugins/qmltooling/shared/qqmlconfigurabledebugservice.h b/src/plugins/qmltooling/shared/qqmlconfigurabledebugservice.h new file mode 100644 index 0000000000..9aa4531428 --- /dev/null +++ b/src/plugins/qmltooling/shared/qqmlconfigurabledebugservice.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QQMLCONFIGURABLEDEBUGSEVICE_H +#define QQMLCONFIGURABLEDEBUGSEVICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qqmldebugservice_p.h> +#include <private/qqmldebugconnector_p.h> +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +template <class Base> +class QQmlConfigurableDebugService : public Base +{ +protected: + QQmlConfigurableDebugService(float version, QObject *parent = 0) : + Base(version, parent), m_configMutex(QMutex::Recursive) + { + init(); + } + + void stopWaiting() + { + QMutexLocker lock(&m_configMutex); + m_waitingForConfiguration = false; + foreach (QQmlEngine *engine, m_waitingEngines) + emit Base::attachedToEngine(engine); + m_waitingEngines.clear(); + } + + void init() + { + QMutexLocker lock(&m_configMutex); + // If we're not enabled or not blocking, don't wait for configuration + m_waitingForConfiguration = (Base::state() == QQmlDebugService::Enabled && + QQmlDebugConnector::instance()->blockingMode()); + } + + void stateChanged(QQmlDebugService::State newState) + { + if (newState != QQmlDebugService::Enabled) + stopWaiting(); + else + init(); + } + + void engineAboutToBeAdded(QQmlEngine *engine) + { + QMutexLocker lock(&m_configMutex); + if (m_waitingForConfiguration) + m_waitingEngines.append(engine); + else + emit Base::attachedToEngine(engine); + } + + QMutex m_configMutex; + QList<QQmlEngine *> m_waitingEngines; + bool m_waitingForConfiguration; +}; + +QT_END_NAMESPACE + +#endif // QQMLCONFIGURABLEDEBUGSEVICE_H diff --git a/src/plugins/qmltooling/shared/qqmldebugserver.h b/src/plugins/qmltooling/shared/qqmldebugserver.h new file mode 100644 index 0000000000..a7c17075d9 --- /dev/null +++ b/src/plugins/qmltooling/shared/qqmldebugserver.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDEBUGSERVER_H +#define QQMLDEBUGSERVER_H + +#include <private/qqmldebugconnector_p.h> +#include <private/qtqmlglobal_p.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QQmlDebugServer : protected QQmlDebugConnector +{ + Q_OBJECT +public: + virtual void setDevice(QIODevice *socket) = 0; +}; + +QT_END_NAMESPACE + +#endif // QQMLDEBUGSERVER_H diff --git a/src/qml/debugger/qqmldebugserverconnection_p.h b/src/plugins/qmltooling/shared/qqmldebugserverconnection.h index 9170238b46..9bdb1bcc8b 100644 --- a/src/qml/debugger/qqmldebugserverconnection_p.h +++ b/src/plugins/qmltooling/shared/qqmldebugserverconnection.h @@ -34,8 +34,8 @@ #ifndef QQMLDEBUGSERVERCONNECTION_H #define QQMLDEBUGSERVERCONNECTION_H -#include <QtQml/qtqmlglobal.h> -#include <private/qqmlglobal_p.h> +#include <private/qtqmlglobal_p.h> +#include <QtCore/qobject.h> // // W A R N I N G @@ -52,24 +52,30 @@ QT_BEGIN_NAMESPACE class QQmlDebugServer; -class Q_QML_PRIVATE_EXPORT QQmlDebugServerConnection +class QQmlDebugServerConnection : public QObject { + Q_OBJECT public: - QQmlDebugServerConnection() {} - virtual ~QQmlDebugServerConnection() {} + QQmlDebugServerConnection(QObject *parent = 0) : QObject(parent) {} virtual void setServer(QQmlDebugServer *server) = 0; - virtual bool setPortRange(int portFrom, int portTo, bool bock, const QString &hostaddress) = 0; + virtual bool setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress) = 0; + virtual bool setFileName(const QString &fileName, bool block) = 0; virtual bool isConnected() const = 0; - virtual void send(const QList<QByteArray> &messages) = 0; virtual void disconnect() = 0; virtual void waitForConnection() = 0; - virtual bool waitForMessage() = 0; + virtual void flush() = 0; }; -#define QQmlDebugServerConnection_iid "org.qt-project.Qt.QQmlDebugServerConnection" +class QQmlDebugServerConnectionFactory : public QObject +{ + Q_OBJECT +public: + virtual QQmlDebugServerConnection *create(const QString &key) = 0; +}; -Q_DECLARE_INTERFACE(QQmlDebugServerConnection, QQmlDebugServerConnection_iid) +#define QQmlDebugServerConnectionFactory_iid "org.qt-project.Qt.QQmlDebugServerConnectionFactory" +Q_DECLARE_INTERFACE(QQmlDebugServerConnectionFactory, QQmlDebugServerConnectionFactory_iid) QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 63833504f1..4b1e3601dc 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -33,7 +33,7 @@ #include "qqmlirbuilder_p.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljslexer_p.h> @@ -1456,10 +1456,8 @@ JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR , _disableAcceleratedLookups(false) , _contextObject(0) , _scopeObject(0) - , _contextObjectTemp(-1) - , _scopeObjectTemp(-1) + , _qmlContextTemp(-1) , _importedScriptsTemp(-1) - , _idArrayTemp(-1) { _module = jsModule; _module->setFileName(fileName); @@ -1764,24 +1762,14 @@ static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, void JSCodeGen::beginFunctionBodyHook() { - _contextObjectTemp = _block->newTemp(); - _scopeObjectTemp = _block->newTemp(); + _qmlContextTemp = _block->newTemp(); _importedScriptsTemp = _block->newTemp(); - _idArrayTemp = _block->newTemp(); #ifndef V4_BOOTSTRAP - QV4::IR::Temp *temp = _block->TEMP(_contextObjectTemp); - temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - initMetaObjectResolver(temp->memberResolver, _contextObject); - move(temp, _block->NAME(QV4::IR::Name::builtin_qml_context_object, 0, 0)); - - temp = _block->TEMP(_scopeObjectTemp); - temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); - initMetaObjectResolver(temp->memberResolver, _scopeObject); - move(temp, _block->NAME(QV4::IR::Name::builtin_qml_scope_object, 0, 0)); + QV4::IR::Temp *temp = _block->TEMP(_qmlContextTemp); + move(temp, _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0)); move(_block->TEMP(_importedScriptsTemp), _block->NAME(QV4::IR::Name::builtin_qml_imported_scripts_object, 0, 0)); - move(_block->TEMP(_idArrayTemp), _block->NAME(QV4::IR::Name::builtin_qml_id_array, 0, 0)); #endif } @@ -1807,7 +1795,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int foreach (const IdMapping &mapping, _idObjects) if (name == mapping.name) { _function->idObjectDependencies.insert(mapping.idIndex); - QV4::IR::Expr *s = subscript(_block->TEMP(_idArrayTemp), _block->CONST(QV4::IR::SInt32Type, mapping.idIndex)); + QV4::IR::Expr *s = _block->MEMBER(_block->TEMP(_qmlContextTemp), _function->newString(name), 0, QV4::IR::Member::MemberOfIdObjectsArray, mapping.idIndex); QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); _block->MOVE(result, s); result = _block->TEMP(result->index); @@ -1857,7 +1845,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int if (propertyExistsButForceNameLookup) return 0; if (pd) { - QV4::IR::Temp *base = _block->TEMP(_scopeObjectTemp); + QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp); base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); initMetaObjectResolver(base->memberResolver, _scopeObject); return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlScopeObject); @@ -1870,7 +1858,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int if (propertyExistsButForceNameLookup) return 0; if (pd) { - QV4::IR::Temp *base = _block->TEMP(_contextObjectTemp); + QV4::IR::Temp *base = _block->TEMP(_qmlContextTemp); base->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>(); initMetaObjectResolver(base->memberResolver, _contextObject); return _block->MEMBER(base, _function->newString(name), pd, QV4::IR::Member::MemberOfQmlContextObject); diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 120de91321..5617912081 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -34,7 +34,6 @@ #define QQMLIRBUILDER_P_H #include <private/qqmljsast_p.h> -#include <private/qqmlpool_p.h> #include <private/qqmljsengine_p.h> #include <private/qv4compiler_p.h> #include <private/qv4compileddata_p.h> @@ -496,10 +495,8 @@ private: ObjectIdMapping _idObjects; QQmlPropertyCache *_contextObject; QQmlPropertyCache *_scopeObject; - int _contextObjectTemp; - int _scopeObjectTemp; + int _qmlContextTemp; int _importedScriptsTemp; - int _idArrayTemp; }; } // namespace QmlIR diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 4e9817aa0d..307f945d20 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -104,11 +104,10 @@ bool QQmlTypeCompiler::compile() } if (ref->type->containsRevisionedAttributes()) { - QQmlError cacheError; ref->typePropertyCache = engine->cache(ref->type, - resolvedType->minorVersion, - cacheError); + resolvedType->minorVersion); if (!ref->typePropertyCache) { + QQmlError cacheError; cacheError.setColumn(resolvedType->location.column); cacheError.setLine(resolvedType->location.line); recordError(cacheError); @@ -696,6 +695,7 @@ bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Ob const int paramCount = s->parameters->count; QList<QByteArray> names; + names.reserve(paramCount); QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); if (paramCount) { @@ -1022,10 +1022,10 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio } } - QHash<QString, QStringList>::ConstIterator entry = customSignals.find(propertyName); + QHash<QString, QStringList>::ConstIterator entry = customSignals.constFind(propertyName); if (entry == customSignals.constEnd() && propertyName.endsWith(QStringLiteral("Changed"))) { QString alternateName = propertyName.mid(0, propertyName.length() - static_cast<int>(strlen("Changed"))); - entry = customSignals.find(alternateName); + entry = customSignals.constFind(alternateName); } if (entry == customSignals.constEnd()) { @@ -2017,7 +2017,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD customParserBindingsPerObject->insert(objectIndex, customParserBindings); const QList<QQmlError> parserErrors = customParser->errors(); if (!parserErrors.isEmpty()) { - foreach (QQmlError error, parserErrors) + foreach (const QQmlError &error, parserErrors) compiler->recordError(error); return false; } @@ -2623,10 +2623,8 @@ void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move } if (QV4::IR::Name *n = move->source->asName()) { - if (n->builtin == QV4::IR::Name::builtin_qml_id_array - || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object - || n->builtin == QV4::IR::Name::builtin_qml_context_object - || n->builtin == QV4::IR::Name::builtin_qml_scope_object) { + if (n->builtin == QV4::IR::Name::builtin_qml_context + || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) { // these are free of side-effects return; } diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 9168889c8c..ea82d07e69 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -33,6 +33,7 @@ #include "qv4codegen_p.h" #include "qv4util_p.h" +#include "qv4engine_p.h" #include <QtCore/QCoreApplication> #include <QtCore/QStringList> @@ -43,7 +44,7 @@ #include <QtCore/QStack> #include <private/qqmljsast_p.h> #include <private/qv4string_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #ifndef V4_BOOTSTRAP #include <qv4context_p.h> diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 5d954eb4fc..20db5edaa3 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -33,7 +33,7 @@ #include "qv4compileddata_p.h" #include "qv4jsir_p.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #ifndef V4_BOOTSTRAP #include <private/qv4engine_p.h> #include <private/qv4function_p.h> diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 450889c275..ba4bde7a31 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -35,7 +35,7 @@ #include <qv4compileddata_p.h> #include <qv4isel_p.h> #include <private/qv4string_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> QV4::Compiler::StringTableGenerator::StringTableGenerator() { @@ -44,8 +44,8 @@ QV4::Compiler::StringTableGenerator::StringTableGenerator() int QV4::Compiler::StringTableGenerator::registerString(const QString &str) { - QHash<QString, int>::ConstIterator it = stringToId.find(str); - if (it != stringToId.end()) + QHash<QString, int>::ConstIterator it = stringToId.constFind(str); + if (it != stringToId.cend()) return *it; stringToId.insert(str, strings.size()); strings.append(str); @@ -169,6 +169,7 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *arg // ### re-use existing class definitions. QList<CompiledData::JSClassMember> members; + members.reserve(count); IR::ExprList *it = args; for (int i = 0; i < count; ++i, it = it->next) { diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 5c2ad45da2..6839c7f609 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -64,12 +64,19 @@ QT_BEGIN_NAMESPACE F(SetLookup, setLookup) \ F(StoreQObjectProperty, storeQObjectProperty) \ F(LoadQObjectProperty, loadQObjectProperty) \ + F(StoreScopeObjectProperty, storeScopeObjectProperty) \ + F(StoreContextObjectProperty, storeContextObjectProperty) \ + F(LoadScopeObjectProperty, loadScopeObjectProperty) \ + F(LoadContextObjectProperty, loadContextObjectProperty) \ + F(LoadIdObject, loadIdObject) \ F(LoadAttachedQObjectProperty, loadAttachedQObjectProperty) \ F(LoadSingletonQObjectProperty, loadQObjectProperty) \ F(Push, push) \ F(CallValue, callValue) \ F(CallProperty, callProperty) \ F(CallPropertyLookup, callPropertyLookup) \ + F(CallScopeObjectProperty, callScopeObjectProperty) \ + F(CallContextObjectProperty, callContextObjectProperty) \ F(CallElement, callElement) \ F(CallActivationProperty, callActivationProperty) \ F(CallGlobalLookup, callGlobalLookup) \ @@ -125,10 +132,8 @@ QT_BEGIN_NAMESPACE F(Sub, sub) \ F(BinopContext, binopContext) \ F(LoadThis, loadThis) \ - F(LoadQmlIdArray, loadQmlIdArray) \ + F(LoadQmlContext, loadQmlContext) \ F(LoadQmlImportedScripts, loadQmlImportedScripts) \ - F(LoadQmlContextObject, loadQmlContextObject) \ - F(LoadQmlScopeObject, loadQmlScopeObject) \ F(LoadQmlSingleton, loadQmlSingleton) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) @@ -293,6 +298,24 @@ union Instr Param base; Param result; }; + struct instr_loadScopeObjectProperty { + MOTH_INSTR_HEADER + int propertyIndex; + Param base; + Param result; + }; + struct instr_loadContextObjectProperty { + MOTH_INSTR_HEADER + int propertyIndex; + Param base; + Param result; + }; + struct instr_loadIdObject { + MOTH_INSTR_HEADER + int index; + Param base; + Param result; + }; struct instr_loadQObjectProperty { MOTH_INSTR_HEADER int propertyIndex; @@ -318,6 +341,18 @@ union Instr Param base; Param source; }; + struct instr_storeScopeObjectProperty { + MOTH_INSTR_HEADER + Param base; + int propertyIndex; + Param source; + }; + struct instr_storeContextObjectProperty { + MOTH_INSTR_HEADER + Param base; + int propertyIndex; + Param source; + }; struct instr_storeQObjectProperty { MOTH_INSTR_HEADER Param base; @@ -377,6 +412,22 @@ union Instr Param base; Param result; }; + struct instr_callScopeObjectProperty { + MOTH_INSTR_HEADER + int index; + quint32 argc; + quint32 callData; + Param base; + Param result; + }; + struct instr_callContextObjectProperty { + MOTH_INSTR_HEADER + int index; + quint32 argc; + quint32 callData; + Param base; + Param result; + }; struct instr_callElement { MOTH_INSTR_HEADER Param base; @@ -684,7 +735,7 @@ union Instr MOTH_INSTR_HEADER Param result; }; - struct instr_loadQmlIdArray { + struct instr_loadQmlContext { MOTH_INSTR_HEADER Param result; }; @@ -692,14 +743,6 @@ union Instr MOTH_INSTR_HEADER Param result; }; - struct instr_loadQmlContextObject { - MOTH_INSTR_HEADER - Param result; - }; - struct instr_loadQmlScopeObject { - MOTH_INSTR_HEADER - Param result; - }; struct instr_loadQmlSingleton { MOTH_INSTR_HEADER Param result; @@ -725,15 +768,22 @@ union Instr instr_storeElementLookup storeElementLookup; instr_loadProperty loadProperty; instr_getLookup getLookup; + instr_loadScopeObjectProperty loadScopeObjectProperty; + instr_loadContextObjectProperty loadContextObjectProperty; + instr_loadIdObject loadIdObject; instr_loadQObjectProperty loadQObjectProperty; instr_loadAttachedQObjectProperty loadAttachedQObjectProperty; instr_storeProperty storeProperty; instr_setLookup setLookup; + instr_storeScopeObjectProperty storeScopeObjectProperty; + instr_storeContextObjectProperty storeContextObjectProperty; instr_storeQObjectProperty storeQObjectProperty; instr_push push; instr_callValue callValue; instr_callProperty callProperty; instr_callPropertyLookup callPropertyLookup; + instr_callScopeObjectProperty callScopeObjectProperty; + instr_callContextObjectProperty callContextObjectProperty; instr_callElement callElement; instr_callActivationProperty callActivationProperty; instr_callGlobalLookup callGlobalLookup; @@ -789,10 +839,8 @@ union Instr instr_sub sub; instr_binopContext binopContext; instr_loadThis loadThis; - instr_loadQmlIdArray loadQmlIdArray; + instr_loadQmlContext loadQmlContext; instr_loadQmlImportedScripts loadQmlImportedScripts; - instr_loadQmlContextObject loadQmlContextObject; - instr_loadQmlScopeObject loadQmlScopeObject; instr_loadQmlSingleton loadQmlSingleton; static int size(Type type); diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index eb78a0c054..ede1e6938f 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -447,7 +447,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backend foreach (IR::Function *irFunction, irModule->functions) compilationUnit->codeRefs[i++] = codeRefs[irFunction]; QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; - result.take(compilationUnit.take()); + result.adopt(compilationUnit.take()); return result; } @@ -461,6 +461,29 @@ void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Ex addInstruction(call); } +void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) { + Instruction::CallScopeObjectProperty call; + call.base = getParam(base); + call.index = propertyIndex; + prepareCallArgs(args, call.argc); + call.callData = callDataStart(); + call.result = getResultParam(result); + addInstruction(call); + } else if (kind == IR::Member::MemberOfQmlContextObject) { + Instruction::CallContextObjectProperty call; + call.base = getParam(base); + call.index = propertyIndex; + prepareCallArgs(args, call.argc); + call.callData = callDataStart(); + call.result = getResultParam(result); + addInstruction(call); + } else { + Q_ASSERT(false); + } +} + void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { @@ -565,9 +588,9 @@ void InstructionSelection::loadThisObject(IR::Expr *e) addInstruction(load); } -void InstructionSelection::loadQmlIdArray(IR::Expr *e) +void InstructionSelection::loadQmlContext(IR::Expr *e) { - Instruction::LoadQmlIdArray load; + Instruction::LoadQmlContext load; load.result = getResultParam(e); addInstruction(load); } @@ -579,20 +602,6 @@ void InstructionSelection::loadQmlImportedScripts(IR::Expr *e) addInstruction(load); } -void InstructionSelection::loadQmlContextObject(IR::Expr *e) -{ - Instruction::LoadQmlContextObject load; - load.result = getResultParam(e); - addInstruction(load); -} - -void InstructionSelection::loadQmlScopeObject(IR::Expr *e) -{ - Instruction::LoadQmlScopeObject load; - load.result = getResultParam(e); - addInstruction(load); -} - void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *e) { Instruction::LoadQmlSingleton load; @@ -694,6 +703,25 @@ void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase, addInstruction(store); } +void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) { + Instruction::StoreScopeObjectProperty store; + store.base = getParam(targetBase); + store.propertyIndex = propertyIndex; + store.source = getParam(source); + addInstruction(store); + } else if (kind == IR::Member::MemberOfQmlContextObject) { + Instruction::StoreContextObjectProperty store; + store.base = getParam(targetBase); + store.propertyIndex = propertyIndex; + store.source = getParam(source); + addInstruction(store); + } else { + Q_ASSERT(false); + } +} + void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) { Instruction::StoreQObjectProperty store; @@ -703,6 +731,31 @@ void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *target addInstruction(store); } +void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) { + Instruction::LoadScopeObjectProperty load; + load.base = getParam(source); + load.propertyIndex = index; + load.result = getResultParam(target); + addInstruction(load); + } else if (kind == IR::Member::MemberOfQmlContextObject) { + Instruction::LoadContextObjectProperty load; + load.base = getParam(source); + load.propertyIndex = index; + load.result = getResultParam(target); + addInstruction(load); + } else if (kind == IR::Member::MemberOfIdObjectsArray) { + Instruction::LoadIdObject load; + load.base = getParam(source); + load.index = index; + load.result = getResultParam(target); + addInstruction(load); + } else { + Q_ASSERT(false); + } +} + void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) { if (attachedPropertiesId != 0) { @@ -1444,7 +1497,7 @@ ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &in void InstructionSelection::patchJumpAddresses() { typedef QHash<IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt; - for (PatchIt i = _patches.begin(), ei = _patches.end(); i != ei; ++i) { + for (PatchIt i = _patches.cbegin(), ei = _patches.cend(); i != ei; ++i) { Q_ASSERT(_addrs.contains(i.key())); ptrdiff_t target = _addrs.value(i.key()); diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index 4ea0f1d07f..ac1eaba740 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -95,6 +95,7 @@ protected: virtual void callBuiltinSetupArgumentObject(IR::Expr *result); virtual void callBuiltinConvertThisToObject(); virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result); + virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result); virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result); virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result); virtual void convertType(IR::Expr *source, IR::Expr *target); @@ -102,10 +103,8 @@ protected: virtual void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result); virtual void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result); virtual void loadThisObject(IR::Expr *e); - virtual void loadQmlIdArray(IR::Expr *e); + virtual void loadQmlContext(IR::Expr *e); virtual void loadQmlImportedScripts(IR::Expr *e); - virtual void loadQmlContextObject(IR::Expr *e); - virtual void loadQmlScopeObject(IR::Expr *e); virtual void loadQmlSingleton(const QString &name, IR::Expr *e); virtual void loadConst(IR::Const *sourceConst, IR::Expr *e); virtual void loadString(const QString &str, IR::Expr *target); @@ -115,7 +114,9 @@ protected: virtual void initClosure(IR::Closure *closure, IR::Expr *target); virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target); virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName); + virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex); virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex); + virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target); virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target); virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target); virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex); diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 54b184b4eb..9d172b1223 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -36,7 +36,7 @@ #include "qv4jsir_p.h" #include "qv4isel_p.h" #include "qv4isel_util_p.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #ifndef V4_BOOTSTRAP #include <private/qqmlpropertycache_p.h> #endif @@ -91,12 +91,8 @@ void IRDecoder::visitMove(IR::Move *s) if (IR::Name *n = s->source->asName()) { if (n->id && *n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. loadThisObject(s->target); - else if (n->builtin == IR::Name::builtin_qml_id_array) - loadQmlIdArray(s->target); - else if (n->builtin == IR::Name::builtin_qml_context_object) - loadQmlContextObject(s->target); - else if (n->builtin == IR::Name::builtin_qml_scope_object) - loadQmlScopeObject(s->target); + else if (n->builtin == IR::Name::builtin_qml_context) + loadQmlContext(s->target); else if (n->builtin == IR::Name::builtin_qml_imported_scripts_object) loadQmlImportedScripts(s->target); else if (n->qmlSingleton) @@ -140,8 +136,8 @@ void IRDecoder::visitMove(IR::Move *s) #else bool captureRequired = true; - Q_ASSERT(m->kind != IR::Member::MemberOfEnum); - const int attachedPropertiesId = m->attachedPropertiesIdOrEnumValue; + Q_ASSERT(m->kind != IR::Member::MemberOfEnum && m->kind != IR::Member::MemberOfIdObjectsArray); + const int attachedPropertiesId = m->attachedPropertiesId; const bool isSingletonProperty = m->kind == IR::Member::MemberOfSingletonObject; if (_function && attachedPropertiesId == 0 && !m->property->isConstant()) { @@ -153,9 +149,16 @@ void IRDecoder::visitMove(IR::Move *s) captureRequired = false; } } + if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { + getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex, s->target); + return; + } getQObjectProperty(m->base, m->property->coreIndex, captureRequired, isSingletonProperty, attachedPropertiesId, s->target); #endif // V4_BOOTSTRAP return; + } else if (m->kind == IR::Member::MemberOfIdObjectsArray) { + getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->idIndex, s->target); + return; } else if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) { getProperty(m->base, *m->name, s->target); return; @@ -174,6 +177,13 @@ void IRDecoder::visitMove(IR::Move *s) callBuiltin(c, s->target); return; } else if (Member *member = c->base->asMember()) { +#ifndef V4_BOOTSTRAP + Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); + if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { + callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex, c->args, s->target); + return; + } +#endif callProperty(member->base, *member->name, c->args, s->target); return; } else if (Subscript *ss = c->base->asSubscript()) { @@ -192,11 +202,16 @@ void IRDecoder::visitMove(IR::Move *s) if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) { if (s->source->asTemp() || s->source->asConst() || s->source->asArgLocal()) { Q_ASSERT(m->kind != IR::Member::MemberOfEnum); - const int attachedPropertiesId = m->attachedPropertiesIdOrEnumValue; + Q_ASSERT(m->kind != IR::Member::MemberOfIdObjectsArray); + const int attachedPropertiesId = m->attachedPropertiesId; if (m->property && attachedPropertiesId == 0) { #ifdef V4_BOOTSTRAP Q_UNIMPLEMENTED(); #else + if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) { + setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex); + return; + } setQObjectProperty(s->source, m->base, m->property->coreIndex); #endif return; @@ -238,6 +253,13 @@ void IRDecoder::visitExp(IR::Exp *s) callValue(c->base, c->args, 0); } else if (Member *member = c->base->asMember()) { Q_ASSERT(member->base->asTemp() || member->base->asArgLocal()); +#ifndef V4_BOOTSTRAP + Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray); + if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) { + callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex, c->args, 0); + return; + } +#endif callProperty(member->base, *member->name, c->args, 0); } else if (Subscript *s = c->base->asSubscript()) { callSubscript(s->base, s->index, c->args, 0); diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index 1e273df93e..2b8aa7eb33 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -129,6 +129,7 @@ public: // to implement by subclasses: virtual void callBuiltinSetupArgumentObject(IR::Expr *result) = 0; virtual void callBuiltinConvertThisToObject() = 0; virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0; + virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) = 0; virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0; virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) = 0; virtual void convertType(IR::Expr *source, IR::Expr *target) = 0; @@ -136,10 +137,8 @@ public: // to implement by subclasses: virtual void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) = 0; virtual void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) = 0; virtual void loadThisObject(IR::Expr *target) = 0; - virtual void loadQmlIdArray(IR::Expr *target) = 0; + virtual void loadQmlContext(IR::Expr *target) = 0; virtual void loadQmlImportedScripts(IR::Expr *target) = 0; - virtual void loadQmlContextObject(IR::Expr *target) = 0; - virtual void loadQmlScopeObject(IR::Expr *target) = 0; virtual void loadQmlSingleton(const QString &name, IR::Expr *target) = 0; virtual void loadConst(IR::Const *sourceConst, IR::Expr *target) = 0; virtual void loadString(const QString &str, IR::Expr *target) = 0; @@ -149,7 +148,9 @@ public: // to implement by subclasses: virtual void initClosure(IR::Closure *closure, IR::Expr *target) = 0; virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) = 0; virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) = 0; + virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target) = 0; virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) = 0; + virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) = 0; virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) = 0; virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) = 0; virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) = 0; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 5c9cc98ade..98b53c6b3b 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -341,14 +341,10 @@ const char *builtin_to_string(Name::Builtin b) return "builtin_setup_argument_object"; case IR::Name::builtin_convert_this_to_object: return "builtin_convert_this_to_object"; - case IR::Name::builtin_qml_id_array: - return "builtin_qml_id_array"; + case IR::Name::builtin_qml_context: + return "builtin_qml_context"; case IR::Name::builtin_qml_imported_scripts_object: return "builtin_qml_imported_scripts_object"; - case IR::Name::builtin_qml_scope_object: - return "builtin_qml_scope_object"; - case IR::Name::builtin_qml_context_object: - return "builtin_qml_context_object"; } return "builtin_(###FIXME)"; }; @@ -935,7 +931,7 @@ void CloneExpr::visitSubscript(Subscript *e) void CloneExpr::visitMember(Member *e) { Expr *clonedBase = clone(e->base); - cloned = block->MEMBER(clonedBase, e->name, e->property, e->kind, e->attachedPropertiesIdOrEnumValue); + cloned = block->MEMBER(clonedBase, e->name, e->property, e->kind, e->idIndex); } IRPrinter::IRPrinter(QTextStream *out) @@ -1239,9 +1235,9 @@ void IRPrinter::visitSubscript(Subscript *e) void IRPrinter::visitMember(Member *e) { - if (e->kind != Member::MemberOfEnum - && e->attachedPropertiesIdOrEnumValue != 0 && !e->base->asTemp()) - *out << "[[attached property from " << e->attachedPropertiesIdOrEnumValue << "]]"; + if (e->kind != Member::MemberOfEnum && e->kind != Member::MemberOfIdObjectsArray + && e->attachedPropertiesId != 0 && !e->base->asTemp()) + *out << "[[attached property from " << e->attachedPropertiesId << "]]"; else e->base->accept(this); *out << '.' << *e->name; @@ -1250,6 +1246,8 @@ void IRPrinter::visitMember(Member *e) *out << " (meta-property " << e->property->coreIndex << " <" << QMetaType::typeName(e->property->propType) << ">)"; + else if (e->kind == Member::MemberOfIdObjectsArray) + *out << "(id object " << e->idIndex << ")"; #endif } diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 8daad97e8b..568ded5337 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -337,10 +337,8 @@ struct Name: Expr { builtin_define_object_literal, builtin_setup_argument_object, builtin_convert_this_to_object, - builtin_qml_id_array, - builtin_qml_imported_scripts_object, - builtin_qml_context_object, - builtin_qml_scope_object + builtin_qml_context, + builtin_qml_imported_scripts_object }; const QString *id; @@ -558,13 +556,18 @@ struct Member: Expr { MemberOfEnum, MemberOfQmlScopeObject, MemberOfQmlContextObject, - MemberOfSingletonObject + MemberOfIdObjectsArray, + MemberOfSingletonObject, }; Expr *base; const QString *name; QQmlPropertyData *property; - int attachedPropertiesIdOrEnumValue; // depending on kind + union { // depending on kind + int attachedPropertiesId; + int enumValue; + int idIndex; + }; uchar freeOfSideEffects : 1; // This is set for example for for QObject properties. All sorts of extra behavior @@ -577,20 +580,20 @@ struct Member: Expr { void setEnumValue(int value) { kind = MemberOfEnum; - attachedPropertiesIdOrEnumValue = value; + enumValue = value; } void setAttachedPropertiesId(int id) { - Q_ASSERT(kind != MemberOfEnum); - attachedPropertiesIdOrEnumValue = id; + Q_ASSERT(kind != MemberOfEnum && kind != MemberOfIdObjectsArray); + attachedPropertiesId = id; } - void init(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = UnspecifiedMember, int attachedPropertiesIdOrEnumValue = 0) + void init(Expr *base, const QString *name, QQmlPropertyData *property = 0, uchar kind = UnspecifiedMember, int index = 0) { this->base = base; this->name = name; this->property = property; - this->attachedPropertiesIdOrEnumValue = attachedPropertiesIdOrEnumValue; + this->idIndex = index; this->freeOfSideEffects = false; this->inhibitTypeConversionOnWrite = property != 0; this->kind = kind; diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index c0669d3e47..c150666a11 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -3912,7 +3912,7 @@ void cfg2dot(IR::Function *f, const QVector<LoopDetection::LoopInfo *> &loops = QString name; if (f->name) name = *f->name; - else name = QString::fromLatin1("%1").arg((unsigned long long)f); + else name = QStringLiteral("%1").arg((unsigned long long)f); qout << "digraph \"" << name << "\" { ordering=out;\n"; foreach (LoopDetection::LoopInfo *l, loops) { @@ -4020,14 +4020,14 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df) if (Member *member = m->source->asMember()) { if (member->kind == Member::MemberOfEnum) { Const *c = function->New<Const>(); - const int enumValue = member->attachedPropertiesIdOrEnumValue; + const int enumValue = member->enumValue; c->init(SInt32Type, enumValue); replaceUses(targetTemp, c, W); defUses.removeDef(*targetTemp); W.remove(s); defUses.removeUse(s, *member->base->asTemp()); continue; - } else if (member->attachedPropertiesIdOrEnumValue != 0 && member->property && member->base->asTemp()) { + } else if (member->kind != IR::Member::MemberOfIdObjectsArray && member->attachedPropertiesId != 0 && member->property && member->base->asTemp()) { // Attached properties have no dependency on their base. Isel doesn't // need it and we can eliminate the temp used to initialize it. defUses.removeUse(s, *member->base->asTemp()); diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri index 77a3ba6490..30a44eedd1 100644 --- a/src/qml/debugger/debugger.pri +++ b/src/qml/debugger/debugger.pri @@ -1,36 +1,23 @@ +contains(QT_CONFIG, no-qml-debug):DEFINES += QT_NO_QML_DEBUGGER + SOURCES += \ + $$PWD/qqmldebug.cpp \ + $$PWD/qqmldebugconnector.cpp \ $$PWD/qqmldebugservice.cpp \ - $$PWD/qqmlprofilerservice.cpp \ - $$PWD/qqmldebugserver.cpp \ - $$PWD/qqmlinspectorservice.cpp \ - $$PWD/qqmlenginedebugservice.cpp \ - $$PWD/qdebugmessageservice.cpp \ - $$PWD/qv4debugservice.cpp \ - $$PWD/qqmlconfigurabledebugservice.cpp \ - $$PWD/qqmlenginecontrolservice.cpp \ + $$PWD/qqmldebugserviceinterfaces.cpp \ $$PWD/qqmlabstractprofileradapter.cpp \ - $$PWD/qv4profileradapter.cpp \ $$PWD/qqmlprofiler.cpp HEADERS += \ + $$PWD/qqmldebugconnector_p.h \ + $$PWD/qqmldebugpluginmanager_p.h \ $$PWD/qqmldebugservice_p.h \ - $$PWD/qqmldebugservice_p_p.h \ - $$PWD/qqmlprofilerservice_p.h \ - $$PWD/qqmldebugserver_p.h \ - $$PWD/qqmldebugserverconnection_p.h \ + $$PWD/qqmldebugservicefactory_p.h \ + $$PWD/qqmldebugserviceinterfaces_p.h \ $$PWD/qqmldebugstatesdelegate_p.h \ - $$PWD/qqmlinspectorservice_p.h \ - $$PWD/qqmlinspectorinterface_p.h \ - $$PWD/qqmlenginedebugservice_p.h \ $$PWD/qqmldebug.h \ - $$PWD/qdebugmessageservice_p.h \ - $$PWD/qv4debugservice_p.h \ - $$PWD/qqmlconfigurabledebugservice_p.h \ - $$PWD/qqmlconfigurabledebugservice_p_p.h \ - $$PWD/qqmlenginecontrolservice_p.h \ $$PWD/qqmlprofilerdefinitions_p.h \ $$PWD/qqmlabstractprofileradapter_p.h \ - $$PWD/qv4profileradapter_p.h \ $$PWD/qqmlprofiler_p.h INCLUDEPATH += $$PWD diff --git a/src/qml/debugger/qqmlconfigurabledebugservice.cpp b/src/qml/debugger/qqmlconfigurabledebugservice.cpp deleted file mode 100644 index e3693e00a4..0000000000 --- a/src/qml/debugger/qqmlconfigurabledebugservice.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlconfigurabledebugservice_p.h" -#include "qqmlconfigurabledebugservice_p_p.h" - -QT_BEGIN_NAMESPACE - -QQmlConfigurableDebugService::QQmlConfigurableDebugService(const QString &name, float version, - QObject *parent) : - QQmlDebugService((*new QQmlConfigurableDebugServicePrivate), name, version, parent) { init(); } - -QQmlConfigurableDebugService::QQmlConfigurableDebugService(QQmlDebugServicePrivate &dd, - const QString &name, float version, - QObject *parent) : - QQmlDebugService(dd, name, version, parent) { init(); } - -QMutex *QQmlConfigurableDebugService::configMutex() -{ - Q_D(QQmlConfigurableDebugService); - return &d->configMutex; -} - -void QQmlConfigurableDebugService::init() -{ - Q_D(QQmlConfigurableDebugService); - QMutexLocker lock(&d->configMutex); - // If we're not enabled or not blocking, don't wait for configuration - d->waitingForConfiguration = (registerService() == Enabled && blockingMode()); -} - -void QQmlConfigurableDebugService::stopWaiting() -{ - Q_D(QQmlConfigurableDebugService); - QMutexLocker lock(&d->configMutex); - d->waitingForConfiguration = false; - foreach (QQmlEngine *engine, d->waitingEngines) - emit attachedToEngine(engine); - d->waitingEngines.clear(); -} - -void QQmlConfigurableDebugService::stateChanged(QQmlDebugService::State newState) -{ - if (newState != Enabled) - stopWaiting(); -} - -void QQmlConfigurableDebugService::engineAboutToBeAdded(QQmlEngine *engine) -{ - Q_D(QQmlConfigurableDebugService); - QMutexLocker lock(&d->configMutex); - if (d->waitingForConfiguration) - d->waitingEngines.append(engine); - else - emit attachedToEngine(engine); -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp new file mode 100644 index 0000000000..83718921f1 --- /dev/null +++ b/src/qml/debugger/qqmldebug.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldebug.h" +#include "qqmldebugconnector_p.h" + +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning) +{ +#ifndef QQML_NO_DEBUG_PROTOCOL + if (!QQmlEnginePrivate::qml_debugging_enabled + && printWarning) { + qDebug("QML debugging is enabled. Only use this in a safe environment."); + } + QQmlEnginePrivate::qml_debugging_enabled = true; +#else + Q_UNUSED(printWarning); +#endif +} + +/*! + * \enum QQmlDebuggingEnabler::StartMode + * + * Defines the debug server's start behavior. You can interrupt QML engines starting while a debug + * client is connecting, in order to set breakpoints in or profile startup code. + * + * \value DoNotWaitForClient Run any QML engines as usual while the debug services are connecting. + * \value WaitForClient If a QML engine starts while the debug services are connecting, + * interrupt it until they are done. + */ + +/*! + * Enables debugging for QML engines created after calling this function. The debug server will + * listen on \a port at \a hostName and block the QML engine until it receives a connection if + * \a mode is \c WaitForClient. If \a mode is not specified it won't block and if \a hostName is not + * specified it will listen on all available interfaces. You can only start one debug server at a + * time. A debug server may have already been started if the -qmljsdebugger= command line argument + * was given. This method returns \c true if a new debug server was successfully started, or + * \c false otherwise. + */ +bool QQmlDebuggingEnabler::startTcpDebugServer(int port, StartMode mode, const QString &hostName) +{ +#ifndef QQML_NO_DEBUG_PROTOCOL + QQmlDebugConnector::setPluginKey(QLatin1String("QQmlDebugServer")); + QQmlDebugConnector *connector = QQmlDebugConnector::instance(); + if (connector) { + QVariantHash configuration; + configuration[QLatin1String("portFrom")] = configuration[QLatin1String("portTo")] = port; + configuration[QLatin1String("block")] = (mode == WaitForClient); + configuration[QLatin1String("hostAddress")] = hostName; + return connector->open(configuration); + } +#else + Q_UNUSED(port); + Q_UNUSED(block); + Q_UNUSED(hostName); +#endif + return false; +} + +/*! + * Enables debugging for QML engines created after calling this function. The debug server will + * connect to a debugger waiting on a local socket at the given \a socketFileName and block the QML + * engine until the connection is established if \a mode is \c WaitForClient. If \a mode is not + * specified it will not block. You can only start one debug server at a time. A debug server may + * have already been started if the -qmljsdebugger= command line argument was given. This method + * returns \c true if a new debug server was successfully started, or \c false otherwise. + */ +bool QQmlDebuggingEnabler::connectToLocalDebugger(const QString &socketFileName, StartMode mode) +{ +#ifndef QQML_NO_DEBUG_PROTOCOL + QQmlDebugConnector::setPluginKey(QLatin1String("QQmlDebugServer")); + QQmlDebugConnector *connector = QQmlDebugConnector::instance(); + if (connector) { + QVariantHash configuration; + configuration[QLatin1String("fileName")] = socketFileName; + configuration[QLatin1String("block")] = (mode == WaitForClient); + return connector->open(configuration); + } +#else + Q_UNUSED(fileName); + Q_UNUSED(block); +#endif + return false; +} + +QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmldebug.h b/src/qml/debugger/qqmldebug.h index 559c492dfd..5d65982a49 100644 --- a/src/qml/debugger/qqmldebug.h +++ b/src/qml/debugger/qqmldebug.h @@ -50,6 +50,8 @@ struct Q_QML_EXPORT QQmlDebuggingEnabler QQmlDebuggingEnabler(bool printWarning = true); static bool startTcpDebugServer(int port, StartMode mode = DoNotWaitForClient, const QString &hostName = QString()); + static bool connectToLocalDebugger(const QString &socketFileName, + StartMode mode = DoNotWaitForClient); }; // Execute code in constructor before first QQmlEngine is instantiated diff --git a/src/qml/debugger/qqmldebugconnector.cpp b/src/qml/debugger/qqmldebugconnector.cpp new file mode 100644 index 0000000000..8de734fa68 --- /dev/null +++ b/src/qml/debugger/qqmldebugconnector.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldebugpluginmanager_p.h" +#include "qqmldebugconnector_p.h" +#include "qqmldebugservicefactory_p.h" +#include <QtCore/QPluginLoader> +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtCore/QJsonArray> + +#include <private/qcoreapplication_p.h> +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +// We could add more plugins here, and distinguish by arguments to instance() +Q_QML_DEBUG_PLUGIN_LOADER(QQmlDebugConnector) +Q_QML_IMPORT_DEBUG_PLUGIN(QQmlDebugServerFactory) + +Q_QML_DEBUG_PLUGIN_LOADER(QQmlDebugService) +Q_QML_IMPORT_DEBUG_PLUGIN(QQmlInspectorServiceFactory) +Q_QML_IMPORT_DEBUG_PLUGIN(QQmlProfilerServiceFactory) +Q_QML_IMPORT_DEBUG_PLUGIN(QQmlDebuggerServiceFactory) + +struct QQmlDebugConnectorParams { + QString pluginKey; + QStringList services; + QString arguments; + QQmlDebugConnector *instance; + + QQmlDebugConnectorParams() : instance(0) + { + if (qApp) { + QCoreApplicationPrivate *appD = + static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp)); + if (appD) + arguments = appD->qmljsDebugArgumentsString(); + } + } +}; + +Q_GLOBAL_STATIC(QQmlDebugConnectorParams, qmlDebugConnectorParams) + +void QQmlDebugConnector::setPluginKey(const QString &key) +{ + QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); + if (params) { + if (params->instance) + qWarning() << "QML debugger: Cannot set plugin key after loading the plugin."; + else + params->pluginKey = key; + } +} + +void QQmlDebugConnector::setServices(const QStringList &services) +{ + QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); + if (params) + params->services = services; +} + +QString QQmlDebugConnector::commandLineArguments() +{ + QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); + if (!params) + return QString(); + return params->arguments; +} + +QQmlDebugConnector *QQmlDebugConnector::instance() +{ + QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); + if (!params) + return 0; + + if (!QQmlEnginePrivate::qml_debugging_enabled) { + if (!params->arguments.isEmpty()) { + qWarning() << QString::fromLatin1( + "QML Debugger: Ignoring \"-qmljsdebugger=%1\". Debugging has not " + "been enabled.").arg(params->arguments); + params->arguments.clear(); + } + return 0; + } + + if (!params->instance) { + if (!params->pluginKey.isEmpty()) { + if (params->pluginKey != QLatin1String("QQmlDebugServer")) + return 0; // We cannot load anything else, yet + } else if (params->arguments.isEmpty()) { + return 0; // no explicit class name given and no command line arguments + } + params->instance = loadQQmlDebugConnector(QLatin1String("QQmlDebugServer")); + if (params->instance) { + foreach (const QJsonObject &object, metaDataForQQmlDebugService()) { + foreach (const QJsonValue &key, object.value(QLatin1String("MetaData")).toObject() + .value(QLatin1String("Keys")).toArray()) { + QString keyString = key.toString(); + if (params->services.isEmpty() || params->services.contains(keyString)) + loadQQmlDebugService(keyString); + } + } + } + } + + return params->instance; +} + +QQmlDebugConnectorFactory::~QQmlDebugConnectorFactory() +{ + // This is triggered when the plugin is unloaded. + QQmlDebugConnectorParams *params = qmlDebugConnectorParams(); + if (params && params->instance) { + delete params->instance; + params->instance = 0; + } +} + +QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlinspectorservice_p.h b/src/qml/debugger/qqmldebugconnector_p.h index f49c36aacb..f5f5a87b56 100644 --- a/src/qml/debugger/qqmlinspectorservice_p.h +++ b/src/qml/debugger/qqmldebugconnector_p.h @@ -31,8 +31,13 @@ ** ****************************************************************************/ -#ifndef QQMLINSPECTORSERVICE_H -#define QQMLINSPECTORSERVICE_H +#ifndef QQMLDEBUGCONNECTOR_H +#define QQMLDEBUGCONNECTOR_H + +#include <QtQml/qtqmlglobal.h> +#include <QtCore/QVariantList> + +#include <private/qqmldebugservice_p.h> // // W A R N I N G @@ -45,45 +50,49 @@ // We mean it. // -#include "qqmldebugservice_p.h" - -#include <QtQml/qtqmlglobal.h> -#include <QtCore/QList> - QT_BEGIN_NAMESPACE - -class QQmlInspectorInterface; - -class Q_QML_PRIVATE_EXPORT QQmlInspectorService : public QQmlDebugService +class QQmlDebugService; +class Q_QML_PRIVATE_EXPORT QQmlDebugConnector : public QObject { Q_OBJECT - public: - QQmlInspectorService(); - static QQmlInspectorService *instance(); + static void setPluginKey(const QString &key); + static void setServices(const QStringList &services); + static QQmlDebugConnector *instance(); - void addView(QObject *); - void removeView(QObject *); + virtual bool blockingMode() const = 0; - void sendMessage(const QByteArray &message); + virtual QQmlDebugService *service(const QString &name) const = 0; -protected: - virtual void stateChanged(State state); - virtual void messageReceived(const QByteArray &); + virtual void addEngine(QQmlEngine *engine) = 0; + virtual void removeEngine(QQmlEngine *engine) = 0; + + virtual bool addService(const QString &name, QQmlDebugService *service) = 0; + virtual bool removeService(const QString &name) = 0; -private Q_SLOTS: - void processMessage(const QByteArray &message); - void updateState(); + virtual bool open(const QVariantHash &configuration = QVariantHash()) = 0; -private: - void loadInspectorPlugins(); + template<class Service> + static Service *service() + { + QQmlDebugConnector *inst = instance(); + return inst ? static_cast<Service *>(inst->service(Service::s_key)) : 0; + } + +protected: + static QString commandLineArguments(); +}; - QList<QObject*> m_views; - QQmlInspectorInterface *m_currentInspectorPlugin; - QList<QQmlInspectorInterface*> m_inspectorPlugins; +class Q_QML_PRIVATE_EXPORT QQmlDebugConnectorFactory : public QObject { + Q_OBJECT +public: + virtual QQmlDebugConnector *create(const QString &key) = 0; + ~QQmlDebugConnectorFactory(); }; +#define QQmlDebugConnectorFactory_iid "org.qt-project.Qt.QQmlDebugConnectorFactory" + QT_END_NAMESPACE -#endif // QQMLINSPECTORSERVICE_H +#endif // QQMLDEBUGCONNECTOR_H diff --git a/src/qml/debugger/qqmldebugpluginmanager_p.h b/src/qml/debugger/qqmldebugpluginmanager_p.h new file mode 100644 index 0000000000..cef463b8b3 --- /dev/null +++ b/src/qml/debugger/qqmldebugpluginmanager_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDEBUGPLUGINMANAGER_P_H +#define QQMLDEBUGPLUGINMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QDebug> +#include <private/qtqmlglobal_p.h> +#include <private/qfactoryloader_p.h> + +QT_BEGIN_NAMESPACE + +#if defined(QT_NO_QML_DEBUGGER) + +#define Q_QML_DEBUG_PLUGIN_LOADER(interfaceName)\ + interfaceName *load##interfaceName(const QString &key)\ + {\ + qWarning() << "Qml Debugger: QtQml is not configured for debugging. Ignoring request for"\ + << "debug plugin" << key;\ + return 0;\ + }\ + QList<QJsonObject> metaDataFor##interfaceName()\ + {\ + return QList<QJsonObject();\ + } +#define Q_QML_IMPORT_DEBUG_PLUGIN(className) + +#else // QT_NO_QML_DEBUGGER + +#ifdef QT_STATIC +#define Q_QML_IMPORT_DEBUG_PLUGIN(className)\ + Q_IMPORT_PLUGIN(className) +#else +#define Q_QML_IMPORT_DEBUG_PLUGIN(className) +#endif // QT_STATIC + +#define Q_QML_DEBUG_PLUGIN_LOADER(interfaceName)\ + Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, interfaceName##Loader,\ + (interfaceName##Factory_iid, QLatin1String("/qmltooling")))\ + interfaceName *load##interfaceName(const QString &key)\ + {\ + return qLoadPlugin<interfaceName, interfaceName##Factory>(interfaceName##Loader(), key);\ + }\ + QList<QJsonObject> metaDataFor##interfaceName()\ + {\ + return interfaceName##Loader()->metaData();\ + } + +#endif // QT_NO_QML_DEBUGGER + +QT_END_NAMESPACE +#endif // QQMLDEBUGPLUGINMANAGER_P_H diff --git a/src/qml/debugger/qqmldebugserver.cpp b/src/qml/debugger/qqmldebugserver.cpp deleted file mode 100644 index b0302181ee..0000000000 --- a/src/qml/debugger/qqmldebugserver.cpp +++ /dev/null @@ -1,787 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmldebugserver_p.h" -#include "qqmldebugservice_p.h" -#include "qqmldebugservice_p_p.h" -#include "qqmlenginedebugservice_p.h" -#include "qv4debugservice_p.h" -#include "qdebugmessageservice_p.h" -#include "qqmlprofilerservice_p.h" - -#include <private/qqmlengine_p.h> -#include <private/qqmlglobal_p.h> - -#include <QtCore/QAtomicInt> -#include <QtCore/QDir> -#include <QtCore/QPluginLoader> -#include <QtCore/QStringList> -#include <QtCore/qwaitcondition.h> - -#include <private/qobject_p.h> -#include <private/qcoreapplication_p.h> - -#if defined(QT_STATIC) && ! defined(QT_NO_QML_DEBUGGER) -#include "../../plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h" -#endif - -QT_BEGIN_NAMESPACE - -// We can't friend the Q_GLOBAL_STATIC to have the constructor available so we need a little -// workaround here. Using this wrapper we can also make QQmlEnginePrivate's cleanup() available to -// qAddPostRoutine(). We can't do the cleanup in the destructor because we need a QApplication to -// be available when stopping the plugins. -struct QQmlDebugServerInstanceWrapper { - QQmlDebugServer m_instance; - void cleanup(); -}; - -Q_GLOBAL_STATIC(QQmlDebugServerInstanceWrapper, debugServerInstance) - -/* - QQmlDebug Protocol (Version 1): - - handshake: - 1. Client sends - "QDeclarativeDebugServer" 0 version pluginNames [QDataStream version] - version: an int representing the highest protocol version the client knows - pluginNames: plugins available on client side - 2. Server sends - "QDeclarativeDebugClient" 0 version pluginNames pluginVersions [QDataStream version] - version: an int representing the highest protocol version the client & server know - pluginNames: plugins available on server side. plugins both in the client and server message are enabled. - client plugin advertisement - 1. Client sends - "QDeclarativeDebugServer" 1 pluginNames - server plugin advertisement - 1. Server sends - "QDeclarativeDebugClient" 1 pluginNames pluginVersions - plugin communication: - Everything send with a header different to "QDeclarativeDebugServer" is sent to the appropriate plugin. - */ - -const int protocolVersion = 1; -int QQmlDebugServer::s_dataStreamVersion = QDataStream::Qt_4_7; - -// print detailed information about loading of plugins -DEFINE_BOOL_CONFIG_OPTION(qmlDebugVerbose, QML_DEBUGGER_VERBOSE) - -class QQmlDebugServerThread; - -class QQmlDebugServerPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQmlDebugServer) -public: - QQmlDebugServerPrivate(); - - bool start(int portFrom, int portTo, bool block, const QString &hostAddress, - const QString &pluginName); - void advertisePlugins(); - void cleanup(); - QQmlDebugServerConnection *loadConnectionPlugin(const QString &pluginName); - - QQmlDebugServerConnection *connection; - QHash<QString, QQmlDebugService *> plugins; - mutable QReadWriteLock pluginsLock; - QStringList clientPlugins; - bool gotHello; - bool blockingMode; - - class EngineCondition { - public: - EngineCondition() : numServices(0), condition(new QWaitCondition) {} - - bool waitForServices(QReadWriteLock *locked, int numEngines); - - void wake(); - private: - int numServices; - - // shared pointer to allow for QHash-inflicted copying. - QSharedPointer<QWaitCondition> condition; - }; - - QHash<QQmlEngine *, EngineCondition> engineConditions; - - QMutex helloMutex; - QWaitCondition helloCondition; - QQmlDebugServerThread *thread; - QPluginLoader loader; - QAtomicInt changeServiceStateCalls; - -private: - // private slots - void _q_changeServiceState(const QString &serviceName, - QQmlDebugService::State newState); - void _q_sendMessages(const QList<QByteArray> &messages); - void _q_removeThread(); -}; - -void QQmlDebugServerInstanceWrapper::cleanup() -{ m_instance.d_func()->cleanup(); } - -class QQmlDebugServerThread : public QThread -{ -public: - void setPluginName(const QString &pluginName) { - m_pluginName = pluginName; - } - - void setPortRange(int portFrom, int portTo, bool block, const QString &hostAddress) { - m_portFrom = portFrom; - m_portTo = portTo; - m_block = block; - m_hostAddress = hostAddress; - } - - void run(); - -private: - QString m_pluginName; - int m_portFrom; - int m_portTo; - bool m_block; - QString m_hostAddress; -}; - -QQmlDebugServerPrivate::QQmlDebugServerPrivate() : - connection(0), - pluginsLock(QReadWriteLock::Recursive), - gotHello(false), - blockingMode(false), - thread(0) -{ - // used in _q_sendMessages - qRegisterMetaType<QList<QByteArray> >("QList<QByteArray>"); - // used in _q_changeServiceState - qRegisterMetaType<QQmlDebugService::State>("QQmlDebugService::State"); -} - -void QQmlDebugServerPrivate::advertisePlugins() -{ - Q_Q(QQmlDebugServer); - - if (!gotHello) - return; - - QByteArray message; - { - QQmlDebugStream out(&message, QIODevice::WriteOnly); - QStringList pluginNames; - QList<float> pluginVersions; - foreach (QQmlDebugService *service, plugins.values()) { - pluginNames << service->name(); - pluginVersions << service->version(); - } - out << QString(QStringLiteral("QDeclarativeDebugClient")) << 1 << pluginNames << pluginVersions; - } - - QMetaObject::invokeMethod(q, "_q_sendMessages", Qt::QueuedConnection, Q_ARG(QList<QByteArray>, QList<QByteArray>() << message)); -} - -void QQmlDebugServerPrivate::cleanup() -{ - Q_Q(QQmlDebugServer); - { - QReadLocker lock(&pluginsLock); - foreach (QQmlDebugService *service, plugins.values()) { - changeServiceStateCalls.ref(); - QMetaObject::invokeMethod(q, "_q_changeServiceState", Qt::QueuedConnection, - Q_ARG(QString, service->name()), - Q_ARG(QQmlDebugService::State, QQmlDebugService::NotConnected)); - } - } - - // Wait for changeServiceState calls to finish - // (while running an event loop because some services - // might again use slots to execute stuff in the GUI thread) - QEventLoop loop; - while (!changeServiceStateCalls.testAndSetOrdered(0, 0)) - loop.processEvents(); - - // Stop the thread while the application is still there. Copy here as the thread will set itself - // to 0 when it stops. It will also do deleteLater, but as long as we don't allow the GUI - // thread's event loop to run we're safe from that. - QThread *threadCopy = thread; - if (threadCopy) { - threadCopy->exit(); - threadCopy->wait(); - } -} - -QQmlDebugServerConnection *QQmlDebugServerPrivate::loadConnectionPlugin( - const QString &pluginName) -{ -#ifndef QT_NO_LIBRARY - QStringList pluginCandidates; - const QStringList paths = QCoreApplication::libraryPaths(); - foreach (const QString &libPath, paths) { - const QDir dir(libPath + QLatin1String("/qmltooling")); - if (dir.exists()) { - QStringList plugins(dir.entryList(QDir::Files)); - foreach (const QString &pluginPath, plugins) { - if (QFileInfo(pluginPath).fileName().contains(pluginName)) - pluginCandidates << dir.absoluteFilePath(pluginPath); - } - } - } - - QQmlDebugServerConnection *loadedConnection = 0; - foreach (const QString &pluginPath, pluginCandidates) { - if (qmlDebugVerbose()) - qDebug() << "QML Debugger: Trying to load plugin " << pluginPath << "..."; - - loader.setFileName(pluginPath); - if (!loader.load()) { - if (qmlDebugVerbose()) - qDebug() << "QML Debugger: Error while loading: " << loader.errorString(); - continue; - } - if (QObject *instance = loader.instance()) - loadedConnection = qobject_cast<QQmlDebugServerConnection*>(instance); - - if (loadedConnection) { - if (qmlDebugVerbose()) - qDebug() << "QML Debugger: Plugin successfully loaded."; - - return loadedConnection; - } - - if (qmlDebugVerbose()) - qDebug() << "QML Debugger: Plugin does not implement interface QQmlDebugServerConnection."; - - loader.unload(); - } -#endif - return 0; -} - -void QQmlDebugServerThread::run() -{ - QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); - Q_ASSERT_X(wrapper != 0, Q_FUNC_INFO, "There should always be a debug server available here."); - QQmlDebugServer *server = &wrapper->m_instance; -#if defined(QT_STATIC) && ! defined(QT_NO_QML_DEBUGGER) - QQmlDebugServerConnection *connection - = new QTcpServerConnection; -#else - QQmlDebugServerConnection *connection - = server->d_func()->loadConnectionPlugin(m_pluginName); -#endif - if (connection) { - connection->setServer(server); - if (!connection->setPortRange(m_portFrom, m_portTo, m_block, m_hostAddress)) { - delete connection; - return; - } - server->d_func()->connection = connection; - if (m_block) - connection->waitForConnection(); - } else { - qWarning() << "QML Debugger: Couldn't load plugin" << m_pluginName; - return; - } - - exec(); - - // make sure events still waiting are processed - QEventLoop eventLoop; - eventLoop.processEvents(QEventLoop::AllEvents); -} - -bool QQmlDebugServer::hasDebuggingClient() const -{ - Q_D(const QQmlDebugServer); - return d->connection - && d->connection->isConnected() - && d->gotHello; -} - -bool QQmlDebugServer::hasThread() const -{ - Q_D(const QQmlDebugServer); - return d->thread != 0; -} - -bool QQmlDebugServer::hasConnection() const -{ - Q_D(const QQmlDebugServer); - return d->connection != 0; -} - -bool QQmlDebugServer::blockingMode() const -{ - Q_D(const QQmlDebugServer); - return d->blockingMode; -} - -QQmlDebugServer *QQmlDebugServer::instance() -{ - QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); - if (wrapper && wrapper->m_instance.d_func()->thread) { - QQmlDebugServer *ret = &(wrapper->m_instance); - QQmlDebugServerPrivate *d = ret->d_func(); - QMutexLocker locker(&d->helloMutex); - if (d->blockingMode && !d->gotHello) - d->helloCondition.wait(&d->helloMutex); - return ret; - } else { - return 0; - } -} - -static void cleanupOnShutdown() -{ - QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); - if (wrapper) - wrapper->cleanup(); -} - -bool QQmlDebugServerPrivate::start(int portFrom, int portTo, bool block, const QString &hostAddress, - const QString &pluginName) -{ - if (!QQmlEnginePrivate::qml_debugging_enabled) - return false; - if (thread) - return false; - static bool postRoutineAdded = false; - if (!postRoutineAdded) { - qAddPostRoutine(cleanupOnShutdown); - postRoutineAdded = true; - } - Q_Q(QQmlDebugServer); - thread = new QQmlDebugServerThread; - q->moveToThread(thread); - - // Remove the thread immmediately when it finishes, so that we don't have to wait for the event - // loop to signal that. - QObject::connect(thread, SIGNAL(finished()), q, SLOT(_q_removeThread()), Qt::DirectConnection); - - thread->setObjectName(QStringLiteral("QQmlDebugServerThread")); - thread->setPluginName(pluginName); - thread->setPortRange(portFrom, portTo == -1 ? portFrom : portTo, block, hostAddress); - blockingMode = block; - thread->start(); - return true; -} - -QQmlDebugServer::QQmlDebugServer() - : QObject(*(new QQmlDebugServerPrivate)) -{ - if (qApp == 0) - return; - QCoreApplicationPrivate *appD = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp)); -#ifndef QT_NO_QML_DEBUGGER - // ### remove port definition when protocol is changed - int portFrom = 0; - int portTo = 0; - bool block = false; - bool ok = false; - QString hostAddress; - - // format: qmljsdebugger=port:<port_from>[,port_to],host:<ip address>][,block] - if (!appD->qmljsDebugArgumentsString().isEmpty()) { - if (!QQmlEnginePrivate::qml_debugging_enabled) { - qWarning() << QString(QLatin1String( - "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " - "Debugging has not been enabled.")).arg( - appD->qmljsDebugArgumentsString()); - return; - } - - QString pluginName; - QStringList lstjsDebugArguments = appD->qmljsDebugArgumentsString() - .split(QLatin1Char(',')); - QStringList::const_iterator argsItEnd = lstjsDebugArguments.end(); - QStringList::const_iterator argsIt = lstjsDebugArguments.begin(); - for (; argsIt != argsItEnd; ++argsIt) { - const QString strArgument = *argsIt; - if (strArgument.startsWith(QLatin1String("port:"))) { - pluginName = QLatin1String("qmldbg_tcp"); - portFrom = strArgument.mid(5).toInt(&ok); - portTo = portFrom; - QStringList::const_iterator argsNext = argsIt + 1; - if (argsNext == argsItEnd) - break; - const QString nextArgument = *argsNext; - if (ok && nextArgument.contains(QRegExp(QStringLiteral("^\\s*\\d+\\s*$")))) { - portTo = nextArgument.toInt(&ok); - ++argsIt; - } - } else if (strArgument.startsWith(QLatin1String("host:"))) { - hostAddress = strArgument.mid(5); - } else if (strArgument == QLatin1String("block")) { - block = true; - } else { - qWarning() << QString::fromLatin1("QML Debugger: Invalid argument '%1' " - "detected. Ignoring the same.") - .arg(strArgument); - } - } - - if (ok) { - Q_D(QQmlDebugServer); - d->start(portFrom, portTo, block, hostAddress, pluginName); - } else { - qWarning() << QString(QLatin1String( - "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " - "Format is qmljsdebugger=port:<port_from>[,port_to],host:" - "<ip address>][,block]")).arg(appD->qmljsDebugArgumentsString()); - } - } -#else - if (!appD->qmljsDebugArgumentsString().isEmpty()) { - qWarning() << QString(QLatin1String( - "QML Debugger: Ignoring \"-qmljsdebugger=%1\". " - "QtQml is not configured for debugging.")).arg( - appD->qmljsDebugArgumentsString()); - } -#endif -} - -void QQmlDebugServer::receiveMessage(const QByteArray &message) -{ - typedef QHash<QString, QQmlDebugService*>::const_iterator DebugServiceConstIt; - - // to be executed in debugger thread - Q_ASSERT(QThread::currentThread() == thread()); - - Q_D(QQmlDebugServer); - - QQmlDebugStream in(message); - - QString name; - - in >> name; - if (name == QLatin1String("QDeclarativeDebugServer")) { - int op = -1; - in >> op; - if (op == 0) { - QWriteLocker lock(&d->pluginsLock); - int version; - in >> version >> d->clientPlugins; - - //Get the supported QDataStream version - if (!in.atEnd()) { - in >> s_dataStreamVersion; - if (s_dataStreamVersion > QDataStream().version()) - s_dataStreamVersion = QDataStream().version(); - } - - // Send the hello answer immediately, since it needs to arrive before - // the plugins below start sending messages. - - QByteArray helloAnswer; - QQmlDebugStream out(&helloAnswer, QIODevice::WriteOnly); - QStringList pluginNames; - QList<float> pluginVersions; - foreach (QQmlDebugService *service, d->plugins.values()) { - pluginNames << service->name(); - pluginVersions << service->version(); - } - - out << QString(QStringLiteral("QDeclarativeDebugClient")) << 0 << protocolVersion - << pluginNames << pluginVersions << s_dataStreamVersion; - - d->connection->send(QList<QByteArray>() << helloAnswer); - - QMutexLocker helloLock(&d->helloMutex); - d->gotHello = true; - - for (DebugServiceConstIt iter = d->plugins.constBegin(), cend = d->plugins.constEnd(); iter != cend; ++iter) { - QQmlDebugService::State newState = QQmlDebugService::Unavailable; - if (d->clientPlugins.contains(iter.key())) - newState = QQmlDebugService::Enabled; - d->changeServiceStateCalls.ref(); - d->_q_changeServiceState(iter.value()->name(), newState); - } - - d->helloCondition.wakeAll(); - - } else if (op == 1) { - QWriteLocker lock(&d->pluginsLock); - - // Service Discovery - QStringList oldClientPlugins = d->clientPlugins; - in >> d->clientPlugins; - - for (DebugServiceConstIt iter = d->plugins.constBegin(), cend = d->plugins.constEnd(); iter != cend; ++iter) { - const QString pluginName = iter.key(); - QQmlDebugService::State newState = QQmlDebugService::Unavailable; - if (d->clientPlugins.contains(pluginName)) - newState = QQmlDebugService::Enabled; - - if (oldClientPlugins.contains(pluginName) - != d->clientPlugins.contains(pluginName)) { - d->changeServiceStateCalls.ref(); - d->_q_changeServiceState(iter.value()->name(), newState); - } - } - - } else { - qWarning("QML Debugger: Invalid control message %d.", op); - d->connection->disconnect(); - return; - } - - } else { - if (d->gotHello) { - QByteArray message; - in >> message; - - QReadLocker lock(&d->pluginsLock); - QHash<QString, QQmlDebugService *>::Iterator iter = d->plugins.find(name); - if (iter == d->plugins.end()) { - qWarning() << "QML Debugger: Message received for missing plugin" << name << '.'; - } else { - (*iter)->messageReceived(message); - } - } else { - qWarning("QML Debugger: Invalid hello message."); - } - - } -} - -void QQmlDebugServerPrivate::_q_changeServiceState(const QString &serviceName, - QQmlDebugService::State newState) -{ - // to be executed in debugger thread - Q_ASSERT(QThread::currentThread() == q_func()->thread()); - - QQmlDebugService *service = 0; - { - // Write lock here, because this can be called from receiveMessage which already has a write - // lock. We cannot downgrade it. We also don't want to give up the write lock and later get - // a read lock as that technique has great potential for deadlocks. - QWriteLocker lock(&pluginsLock); - service = plugins.value(serviceName); - } - - if (service && (service->d_func()->state != newState)) { - service->stateAboutToBeChanged(newState); - service->d_func()->state = newState; - service->stateChanged(newState); - } - - changeServiceStateCalls.deref(); -} - -void QQmlDebugServerPrivate::_q_sendMessages(const QList<QByteArray> &messages) -{ - // to be executed in debugger thread - Q_ASSERT(QThread::currentThread() == q_func()->thread()); - - if (connection) - connection->send(messages); -} - -void QQmlDebugServerPrivate::_q_removeThread() -{ - Q_ASSERT(thread->isFinished()); - Q_ASSERT(QThread::currentThread() == thread); - - QThread *parentThread = thread->thread(); - - // We cannot delete it right away as it will access its data after the finished() signal. - thread->deleteLater(); - thread = 0; - - delete connection; - connection = 0; - - // Move it back to the parent thread so that we can potentially restart it on a new thread. - q_func()->moveToThread(parentThread); -} - -QList<QQmlDebugService*> QQmlDebugServer::services() const -{ - Q_D(const QQmlDebugServer); - QReadLocker lock(&d->pluginsLock); - return d->plugins.values(); -} - -QStringList QQmlDebugServer::serviceNames() const -{ - Q_D(const QQmlDebugServer); - QReadLocker lock(&d->pluginsLock); - return d->plugins.keys(); -} - -void QQmlDebugServer::addEngine(QQmlEngine *engine) -{ - Q_D(QQmlDebugServer); - QWriteLocker lock(&d->pluginsLock); - - foreach (QQmlDebugService *service, d->plugins) - service->engineAboutToBeAdded(engine); - - d->engineConditions[engine].waitForServices(&d->pluginsLock, d->plugins.count()); - - foreach (QQmlDebugService *service, d->plugins) - service->engineAdded(engine); -} - -void QQmlDebugServer::removeEngine(QQmlEngine *engine) -{ - Q_D(QQmlDebugServer); - QWriteLocker lock(&d->pluginsLock); - - foreach (QQmlDebugService *service, d->plugins) - service->engineAboutToBeRemoved(engine); - - d->engineConditions[engine].waitForServices(&d->pluginsLock, d->plugins.count()); - - foreach (QQmlDebugService *service, d->plugins) - service->engineRemoved(engine); -} - -bool QQmlDebugServer::addService(QQmlDebugService *service) -{ - Q_D(QQmlDebugServer); - - // to be executed outside of debugger thread - Q_ASSERT(QThread::currentThread() != thread()); - - connect(service, SIGNAL(attachedToEngine(QQmlEngine*)), - this, SLOT(wakeEngine(QQmlEngine*)), Qt::QueuedConnection); - connect(service, SIGNAL(detachedFromEngine(QQmlEngine*)), - this, SLOT(wakeEngine(QQmlEngine*)), Qt::QueuedConnection); - - - QWriteLocker lock(&d->pluginsLock); - if (!service || d->plugins.contains(service->name())) - return false; - d->plugins.insert(service->name(), service); - d->advertisePlugins(); - QQmlDebugService::State newState = QQmlDebugService::Unavailable; - if (d->clientPlugins.contains(service->name())) - newState = QQmlDebugService::Enabled; - service->d_func()->state = newState; - return true; -} - -bool QQmlDebugServer::removeService(QQmlDebugService *service) -{ - Q_D(QQmlDebugServer); - - // to be executed outside of debugger thread - Q_ASSERT(QThread::currentThread() != thread()); - - QWriteLocker lock(&d->pluginsLock); - QQmlDebugService::State newState = QQmlDebugService::NotConnected; - - d->changeServiceStateCalls.ref(); - QMetaObject::invokeMethod(this, "_q_changeServiceState", Qt::QueuedConnection, - Q_ARG(QString, service->name()), - Q_ARG(QQmlDebugService::State, newState)); - - if (!service || !d->plugins.contains(service->name())) - return false; - d->plugins.remove(service->name()); - - d->advertisePlugins(); - - return true; -} - -void QQmlDebugServer::sendMessages(QQmlDebugService *service, - const QList<QByteArray> &messages) -{ - QList<QByteArray> prefixedMessages; - foreach (const QByteArray &message, messages) { - QByteArray prefixed; - QQmlDebugStream out(&prefixed, QIODevice::WriteOnly); - out << service->name() << message; - prefixedMessages << prefixed; - } - - QMetaObject::invokeMethod(this, "_q_sendMessages", Qt::QueuedConnection, - Q_ARG(QList<QByteArray>, prefixedMessages)); -} - -bool QQmlDebugServer::enable(int portFrom, int portTo, bool block, const QString &hostAddress) -{ -#ifndef QT_NO_QML_DEBUGGER - QQmlDebugServerInstanceWrapper *wrapper = debugServerInstance(); - if (!wrapper) - return false; - QQmlDebugServerPrivate *d = wrapper->m_instance.d_func(); - if (d->thread) - return false; - if (!d->start(portFrom, portTo, block, hostAddress, QLatin1String("qmldbg_tcp"))) - return false; - while (!wrapper->m_instance.hasConnection()) { - if (!wrapper->m_instance.hasThread()) - return false; - } - return true; -#else - Q_UNUSED(portFrom); - Q_UNUSED(portTo); - Q_UNUSED(block); - Q_UNUSED(hostAddress); - return false; -#endif -} - -void QQmlDebugServer::wakeEngine(QQmlEngine *engine) -{ - // to be executed in debugger thread - Q_ASSERT(QThread::currentThread() == thread()); - - Q_D(QQmlDebugServer); - QWriteLocker lock(&d->pluginsLock); - d->engineConditions[engine].wake(); -} - -bool QQmlDebugServerPrivate::EngineCondition::waitForServices(QReadWriteLock *locked, int num) -{ - // to be executed outside of debugger thread - Q_ASSERT(QThread::currentThread() != QQmlDebugServer::instance()->thread()); - - Q_ASSERT_X(numServices == 0, Q_FUNC_INFO, "Request to wait again before previous wait finished"); - numServices = num; - return condition->wait(locked); -} - -void QQmlDebugServerPrivate::EngineCondition::wake() -{ - if (--numServices == 0) - condition->wakeAll(); - Q_ASSERT_X(numServices >=0, Q_FUNC_INFO, "Woken more often than #services."); -} - -QT_END_NAMESPACE - -#include "moc_qqmldebugserver_p.cpp" diff --git a/src/qml/debugger/qqmldebugserver_p.h b/src/qml/debugger/qqmldebugserver_p.h deleted file mode 100644 index b2bd0b2463..0000000000 --- a/src/qml/debugger/qqmldebugserver_p.h +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLDEBUGSERVER_H -#define QQMLDEBUGSERVER_H - -#include <QtQml/qtqmlglobal.h> -#include <private/qqmldebugserverconnection_p.h> -#include <private/qqmldebugservice_p.h> - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_BEGIN_NAMESPACE - - -class QQmlDebugServerPrivate; -class Q_QML_PRIVATE_EXPORT QQmlDebugServer : public QObject -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlDebugServer) - Q_DISABLE_COPY(QQmlDebugServer) -public: - - static QQmlDebugServer *instance(); - - bool hasThread() const; - bool hasConnection() const; - bool hasDebuggingClient() const; - bool blockingMode() const; - - QList<QQmlDebugService*> services() const; - QStringList serviceNames() const; - - void addEngine(QQmlEngine *engine); - void removeEngine(QQmlEngine *engine); - - bool addService(QQmlDebugService *service); - bool removeService(QQmlDebugService *service); - - void receiveMessage(const QByteArray &message); - - void sendMessages(QQmlDebugService *service, const QList<QByteArray> &messages); - static bool enable(int portFrom, int portTo, bool block, const QString &hostAddress); - -private slots: - void wakeEngine(QQmlEngine *engine); - -private: - friend class QQmlDebugService; - friend class QQmlDebugServicePrivate; - friend class QQmlDebugServerThread; - friend struct QQmlDebugServerInstanceWrapper; - QQmlDebugServer(); - Q_PRIVATE_SLOT(d_func(), void _q_changeServiceState(const QString &serviceName, - QQmlDebugService::State state)) - Q_PRIVATE_SLOT(d_func(), void _q_sendMessages(QList<QByteArray>)) - Q_PRIVATE_SLOT(d_func(), void _q_removeThread()) - -public: - static int s_dataStreamVersion; -}; - -QT_END_NAMESPACE - -#endif // QQMLDEBUGSERVICE_H diff --git a/src/qml/debugger/qqmldebugservice.cpp b/src/qml/debugger/qqmldebugservice.cpp index b37a7335a0..0b07f320ec 100644 --- a/src/qml/debugger/qqmldebugservice.cpp +++ b/src/qml/debugger/qqmldebugservice.cpp @@ -32,8 +32,7 @@ ****************************************************************************/ #include "qqmldebugservice_p.h" -#include "qqmldebugservice_p_p.h" -#include "qqmldebugserver_p.h" +#include "qqmldebugconnector_p.h" #include <private/qqmldata_p.h> #include <private/qqmlcontext_p.h> @@ -43,58 +42,55 @@ QT_BEGIN_NAMESPACE -QQmlDebugServicePrivate::QQmlDebugServicePrivate() -{ -} +class QQmlDebugServer; -QQmlDebugService::QQmlDebugService(const QString &name, float version, QObject *parent) - : QObject(*(new QQmlDebugServicePrivate), parent) +class QQmlDebugServicePrivate : public QObjectPrivate { - QQmlDebugServer::instance(); // create it when it isn't there yet. + Q_DECLARE_PUBLIC(QQmlDebugService) +public: + QQmlDebugServicePrivate(const QString &name, float version); - Q_D(QQmlDebugService); - d->name = name; - d->version = version; - d->state = QQmlDebugService::NotConnected; -} + const QString name; + const float version; + QQmlDebugService::State state; +}; -QQmlDebugService::QQmlDebugService(QQmlDebugServicePrivate &dd, - const QString &name, float version, QObject *parent) - : QObject(dd, parent) +QQmlDebugServicePrivate::QQmlDebugServicePrivate(const QString &name, float version) : + name(name), version(version), state(QQmlDebugService::NotConnected) { - Q_D(QQmlDebugService); - d->name = name; - d->version = version; - d->state = QQmlDebugService::NotConnected; } -/** - Registers the service. This should be called in the constructor of the inherited class. From - then on the service might get asynchronous calls to messageReceived(). - */ -QQmlDebugService::State QQmlDebugService::registerService() +QQmlDebugService::QQmlDebugService(const QString &name, float version, QObject *parent) + : QObject(*(new QQmlDebugServicePrivate(name, version)), parent) { Q_D(QQmlDebugService); - QQmlDebugServer *server = QQmlDebugServer::instance(); + QQmlDebugConnector *server = QQmlDebugConnector::instance(); if (!server) - return NotConnected; + return; - if (server->serviceNames().contains(d->name)) { + if (server->service(d->name)) { qWarning() << "QQmlDebugService: Conflicting plugin name" << d->name; } else { - server->addService(this); + server->addService(d->name, this); } - return state(); } QQmlDebugService::~QQmlDebugService() { - if (QQmlDebugServer *inst = QQmlDebugServer::instance()) - inst->removeService(this); + Q_D(QQmlDebugService); + QQmlDebugConnector *server = QQmlDebugConnector::instance(); + + if (!server) + return; + + if (server->service(d->name) != this) + qWarning() << "QQmlDebugService: Plugin" << d->name << "is not registered."; + else + server->removeService(d->name); } -QString QQmlDebugService::name() const +const QString &QQmlDebugService::name() const { Q_D(const QQmlDebugService); return d->name; @@ -112,27 +108,38 @@ QQmlDebugService::State QQmlDebugService::state() const return d->state; } -namespace { - -struct ObjectReference +void QQmlDebugService::setState(QQmlDebugService::State newState) { - QPointer<QObject> object; - int id; -}; + Q_D(QQmlDebugService); + d->state = newState; +} -struct ObjectReferenceHash +namespace { +class ObjectReferenceHash : public QObject { + Q_OBJECT +public: ObjectReferenceHash() : nextId(0) {} - QHash<QObject *, ObjectReference> objects; + QHash<QObject *, int> objects; QHash<int, QObject *> ids; int nextId; -}; +private slots: + void remove(QObject *obj); +}; } Q_GLOBAL_STATIC(ObjectReferenceHash, objectReferenceHash) +void ObjectReferenceHash::remove(QObject *obj) +{ + QHash<QObject *, int>::Iterator iter = objects.find(obj); + if (iter != objects.end()) { + ids.remove(iter.value()); + objects.erase(iter); + } +} /*! Returns a unique id for \a object. Calling this method multiple times @@ -144,158 +151,23 @@ int QQmlDebugService::idForObject(QObject *object) return -1; ObjectReferenceHash *hash = objectReferenceHash(); - QHash<QObject *, ObjectReference>::Iterator iter = - hash->objects.find(object); + QHash<QObject *, int>::Iterator iter = hash->objects.find(object); if (iter == hash->objects.end()) { int id = hash->nextId++; - - hash->ids.insert(id, object); - iter = hash->objects.insert(object, ObjectReference()); - iter->object = object; - iter->id = id; - } else if (iter->object != object) { - int id = hash->nextId++; - - hash->ids.remove(iter->id); - hash->ids.insert(id, object); - iter->object = object; - iter->id = id; - } - return iter->id; -} - -/*! - Returns the object for unique \a id. If the object has not previously been - assigned an id, through idForObject(), then 0 is returned. If the object - has been destroyed, 0 is returned. -*/ -QObject *QQmlDebugService::objectForId(int id) -{ - ObjectReferenceHash *hash = objectReferenceHash(); - - QHash<int, QObject *>::Iterator iter = hash->ids.find(id); - if (iter == hash->ids.end()) - return 0; - - - QHash<QObject *, ObjectReference>::Iterator objIter = - hash->objects.find(*iter); - Q_ASSERT(objIter != hash->objects.end()); - - if (objIter->object == 0) { - hash->ids.erase(iter); - hash->objects.erase(objIter); - // run a loop to remove other invalid objects - removeInvalidObjectsFromHash(); - return 0; - } else { - return *iter; + iter = hash->objects.insert(object, id); + connect(object, SIGNAL(destroyed(QObject*)), hash, SLOT(remove(QObject*))); } + return iter.value(); } /*! - Returns a list of objects matching the given filename, line and column. + Returns the mapping of objects to unique \a ids, created through calls to idForObject(). */ -QList<QObject*> QQmlDebugService::objectForLocationInfo(const QString &filename, - int lineNumber, int columnNumber) -{ - ObjectReferenceHash *hash = objectReferenceHash(); - QList<QObject*> objects; - QHash<int, QObject *>::Iterator iter = hash->ids.begin(); - while (iter != hash->ids.end()) { - QHash<QObject *, ObjectReference>::Iterator objIter = - hash->objects.find(*iter); - Q_ASSERT(objIter != hash->objects.end()); - - if (objIter->object == 0) { - iter = hash->ids.erase(iter); - hash->objects.erase(objIter); - } else { - QQmlData *ddata = QQmlData::get(iter.value()); - if (ddata && ddata->outerContext) { - if (QFileInfo(ddata->outerContext->urlString()).fileName() == filename && - ddata->lineNumber == lineNumber && - ddata->columnNumber >= columnNumber) { - objects << *iter; - } - } - ++iter; - } - } - return objects; -} - -void QQmlDebugService::removeInvalidObjectsFromHash() -{ - ObjectReferenceHash *hash = objectReferenceHash(); - QHash<int, QObject *>::Iterator iter = hash->ids.begin(); - while (iter != hash->ids.end()) { - QHash<QObject *, ObjectReference>::Iterator objIter = - hash->objects.find(*iter); - Q_ASSERT(objIter != hash->objects.end()); - - if (objIter->object == 0) { - iter = hash->ids.erase(iter); - hash->objects.erase(objIter); - } else { - ++iter; - } - } -} - -void QQmlDebugService::clearObjectsFromHash() -{ - ObjectReferenceHash *hash = objectReferenceHash(); - hash->ids.clear(); - hash->objects.clear(); -} - -bool QQmlDebugService::isDebuggingEnabled() -{ - return QQmlDebugServer::instance() != 0; -} - -bool QQmlDebugService::hasDebuggingClient() +const QHash<int, QObject *> &QQmlDebugService::objectsForIds() { - return QQmlDebugServer::instance() != 0 - && QQmlDebugServer::instance()->hasDebuggingClient(); -} - -bool QQmlDebugService::blockingMode() -{ - return QQmlDebugServer::instance() != 0 - && QQmlDebugServer::instance()->blockingMode(); -} - -QString QQmlDebugService::objectToString(QObject *obj) -{ - if(!obj) - return QStringLiteral("NULL"); - - QString objectName = obj->objectName(); - if(objectName.isEmpty()) - objectName = QStringLiteral("<unnamed>"); - - QString rv = QString::fromUtf8(obj->metaObject()->className()) + - QLatin1String(": ") + objectName; - - return rv; -} - -void QQmlDebugService::sendMessage(const QByteArray &message) -{ - sendMessages(QList<QByteArray>() << message); -} - -void QQmlDebugService::sendMessages(const QList<QByteArray> &messages) -{ - if (state() != Enabled) - return; - - if (QQmlDebugServer *inst = QQmlDebugServer::instance()) - inst->sendMessages(this, messages); + return objectReferenceHash()->ids; } void QQmlDebugService::stateAboutToBeChanged(State) @@ -328,28 +200,32 @@ void QQmlDebugService::engineRemoved(QQmlEngine *) { } +int QQmlDebugStream::s_dataStreamVersion = QDataStream::Qt_4_7; + QQmlDebugStream::QQmlDebugStream() : QDataStream() { - setVersion(QQmlDebugServer::s_dataStreamVersion); + setVersion(s_dataStreamVersion); } QQmlDebugStream::QQmlDebugStream(QIODevice *d) : QDataStream(d) { - setVersion(QQmlDebugServer::s_dataStreamVersion); + setVersion(s_dataStreamVersion); } QQmlDebugStream::QQmlDebugStream(QByteArray *ba, QIODevice::OpenMode flags) : QDataStream(ba, flags) { - setVersion(QQmlDebugServer::s_dataStreamVersion); + setVersion(s_dataStreamVersion); } QQmlDebugStream::QQmlDebugStream(const QByteArray &ba) : QDataStream(ba) { - setVersion(QQmlDebugServer::s_dataStreamVersion); + setVersion(s_dataStreamVersion); } QT_END_NAMESPACE + +#include "qqmldebugservice.moc" diff --git a/src/qml/debugger/qqmldebugservice_p.h b/src/qml/debugger/qqmldebugservice_p.h index 9cdfc34ff1..3d692133cc 100644 --- a/src/qml/debugger/qqmldebugservice_p.h +++ b/src/qml/debugger/qqmldebugservice_p.h @@ -35,7 +35,8 @@ #define QQMLDEBUGSERVICE_H #include <QtCore/qobject.h> -#include <QtCore/QDataStream> +#include <QtCore/qdatastream.h> +#include <QtCore/qhash.h> #include <private/qtqmlglobal_p.h> @@ -62,35 +63,14 @@ class Q_QML_PRIVATE_EXPORT QQmlDebugService : public QObject Q_DISABLE_COPY(QQmlDebugService) public: - explicit QQmlDebugService(const QString &, float version, QObject *parent = 0); ~QQmlDebugService(); - QString name() const; + const QString &name() const; float version() const; enum State { NotConnected, Unavailable, Enabled }; State state() const; - - void sendMessage(const QByteArray &); - void sendMessages(const QList<QByteArray> &); - - static int idForObject(QObject *); - static QObject *objectForId(int); - static QList<QObject*> objectForLocationInfo(const QString &filename, - int lineNumber, int columnNumber); - static void removeInvalidObjectsFromHash(); - static void clearObjectsFromHash(); - - static QString objectToString(QObject *obj); - - static bool isDebuggingEnabled(); - static bool hasDebuggingClient(); - static bool blockingMode(); - -protected: - QQmlDebugService(QQmlDebugServicePrivate &dd, const QString &name, float version, QObject *parent = 0); - - State registerService(); + void setState(State newState); virtual void stateAboutToBeChanged(State); virtual void stateChanged(State); @@ -101,18 +81,26 @@ protected: virtual void engineAdded(QQmlEngine *); virtual void engineRemoved(QQmlEngine *); + static const QHash<int, QObject *> &objectsForIds(); + static int idForObject(QObject *); + static QObject *objectForId(int id) { return objectsForIds().value(id); } + +protected: + explicit QQmlDebugService(const QString &, float version, QObject *parent = 0); + signals: void attachedToEngine(QQmlEngine *); void detachedFromEngine(QQmlEngine *); -private: - friend class QQmlDebugServer; - friend class QQmlDebugServerPrivate; + void messageToClient(const QString &name, const QByteArray &message); + void messagesToClient(const QString &name, const QList<QByteArray> &messages); }; class Q_QML_PRIVATE_EXPORT QQmlDebugStream : public QDataStream { public: + static int s_dataStreamVersion; + QQmlDebugStream(); explicit QQmlDebugStream(QIODevice *d); QQmlDebugStream(QByteArray *ba, QIODevice::OpenMode flags); diff --git a/src/qml/debugger/qqmldebugservice_p_p.h b/src/qml/debugger/qqmldebugservicefactory_p.h index 7d60739f3a..af50cd4635 100644 --- a/src/qml/debugger/qqmldebugservice_p_p.h +++ b/src/qml/debugger/qqmldebugservicefactory_p.h @@ -31,8 +31,8 @@ ** ****************************************************************************/ -#ifndef QQMLDEBUGSERVICE_P_H -#define QQMLDEBUGSERVICE_P_H +#ifndef QQMLDEBUGSERVICEFACTORY_P_H +#define QQMLDEBUGSERVICEFACTORY_P_H // // W A R N I N G @@ -45,25 +45,19 @@ // We mean it. // -#include <QtCore/qglobal.h> -#include <private/qobject_p.h> +#include "qqmldebugservice_p.h" QT_BEGIN_NAMESPACE - -class QQmlDebugServer; - -class QQmlDebugServicePrivate : public QObjectPrivate +class Q_QML_PRIVATE_EXPORT QQmlDebugServiceFactory : public QObject { - Q_DECLARE_PUBLIC(QQmlDebugService) + Q_OBJECT public: - QQmlDebugServicePrivate(); - - QString name; - float version; - QQmlDebugService::State state; + virtual QQmlDebugService *create(const QString &key) = 0; }; +#define QQmlDebugServiceFactory_iid "org.qt-project.Qt.QQmlDebugServiceFactory" + QT_END_NAMESPACE -#endif // QQMLDEBUGSERVICE_P_H +#endif // QQMLDEBUGSERVICEFACTORY_P_H diff --git a/src/qml/jsruntime/qv4qmlextensions_p.h b/src/qml/debugger/qqmldebugserviceinterfaces.cpp index e4b9f75298..2bf9f2785c 100644 --- a/src/qml/jsruntime/qv4qmlextensions_p.h +++ b/src/qml/debugger/qqmldebugserviceinterfaces.cpp @@ -30,29 +30,14 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#ifndef QV4QMLEXTENSIONS_P_H -#define QV4QMLEXTENSIONS_P_H -#include <qtqmlglobal.h> -#include <qv4global_p.h> +#include "qqmldebugserviceinterfaces_p.h" QT_BEGIN_NAMESPACE -namespace QV4 { - -struct Q_QML_EXPORT QmlExtensions -{ - QmlExtensions() - : valueTypeWrapperPrototype(0) - {} - - Heap::Object *valueTypeWrapperPrototype; - - void markObjects(ExecutionEngine *e); -}; - -} // namespace QV4 +const QString QV4DebugService::s_key = QStringLiteral("V8Debugger"); +const QString QQmlEngineDebugService::s_key = QStringLiteral("QmlDebugger"); +const QString QQmlInspectorService::s_key = QStringLiteral("QmlInspector"); +const QString QQmlProfilerService::s_key = QStringLiteral("CanvasFrameRate"); QT_END_NAMESPACE - -#endif diff --git a/src/qml/debugger/qqmldebugserviceinterfaces_p.h b/src/qml/debugger/qqmldebugserviceinterfaces_p.h new file mode 100644 index 0000000000..a2ba670608 --- /dev/null +++ b/src/qml/debugger/qqmldebugserviceinterfaces_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDEBUGSERVICEINTERFACES_P_H +#define QQMLDEBUGSERVICEINTERFACES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstring.h> +#include <private/qtqmlglobal_p.h> +#include <private/qqmldebugservice_p.h> +#include <private/qqmldebugstatesdelegate_p.h> +#include <private/qqmlabstractprofileradapter_p.h> +#include <private/qqmlboundsignal_p.h> + +#include <limits> + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QV4DebugService : protected QQmlDebugService +{ + Q_OBJECT +public: + virtual void signalEmitted(const QString &signal) = 0; + +protected: + friend class QQmlDebugConnector; + + QV4DebugService(float version, QObject *parent = 0) : + QQmlDebugService(s_key, version, parent) {} + + static const QString s_key; +}; + +class Q_QML_PRIVATE_EXPORT QQmlProfilerService : protected QQmlDebugService +{ + Q_OBJECT +public: + virtual void addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) = 0; + virtual void removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) = 0; + + virtual void startProfiling(QQmlEngine *engine, + quint64 features = std::numeric_limits<quint64>::max()) = 0; + virtual void stopProfiling(QQmlEngine *engine) = 0; + + virtual void dataReady(QQmlAbstractProfilerAdapter *profiler) = 0; + +protected: + friend class QQmlDebugConnector; + + QQmlProfilerService(float version, QObject *parent = 0) : + QQmlDebugService(s_key, version, parent) {} + + static const QString s_key; +}; + +class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : protected QQmlDebugService +{ + Q_OBJECT +public: + virtual void objectCreated(QQmlEngine *engine, QObject *object) = 0; + virtual void setStatesDelegate(QQmlDebugStatesDelegate *) = 0; + +protected: + friend class QQmlDebugConnector; + + QQmlEngineDebugService(float version, QObject *parent = 0) : + QQmlDebugService(s_key, version, parent) {} + + QQmlBoundSignal *nextSignal(QQmlBoundSignal *prev) { return prev->m_nextSignal; } + + static const QString s_key; +}; + +class Q_QML_PRIVATE_EXPORT QQmlInspectorService : protected QQmlDebugService +{ + Q_OBJECT +public: + virtual void addView(QObject *) = 0; + virtual void removeView(QObject *) = 0; + +protected: + friend class QQmlDebugConnector; + + QQmlInspectorService(float version, QObject *parent = 0) : + QQmlDebugService(s_key, version, parent) {} + + static const QString s_key; +}; + +QT_END_NAMESPACE + +#endif // QQMLDEBUGSERVICEINTERFACES_P_H + diff --git a/src/qml/debugger/qqmlinspectorservice.cpp b/src/qml/debugger/qqmlinspectorservice.cpp deleted file mode 100644 index 5a8c4487d9..0000000000 --- a/src/qml/debugger/qqmlinspectorservice.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlinspectorservice_p.h" -#include "qqmlinspectorinterface_p.h" -#include "qqmldebugserver_p.h" - -#include <private/qqmlglobal_p.h> - -#include <QtCore/QCoreApplication> -#include <QtCore/QDebug> -#include <QtCore/QDir> -#include <QtCore/QPluginLoader> - -// print detailed information about loading of plugins -DEFINE_BOOL_CONFIG_OPTION(qmlDebugVerbose, QML_DEBUGGER_VERBOSE) - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QQmlInspectorService, serviceInstance) - -QQmlInspectorService::QQmlInspectorService() - : QQmlDebugService(QStringLiteral("QmlInspector"), 1) - , m_currentInspectorPlugin(0) -{ - registerService(); -} - -QQmlInspectorService *QQmlInspectorService::instance() -{ - return serviceInstance(); -} - -void QQmlInspectorService::addView(QObject *view) -{ - m_views.append(view); - updateState(); -} - -void QQmlInspectorService::removeView(QObject *view) -{ - m_views.removeAll(view); - updateState(); -} - -void QQmlInspectorService::sendMessage(const QByteArray &message) -{ - if (state() != Enabled) - return; - - QQmlDebugService::sendMessage(message); -} - -void QQmlInspectorService::stateChanged(State /*state*/) -{ - QMetaObject::invokeMethod(this, "updateState", Qt::QueuedConnection); -} - -void QQmlInspectorService::updateState() -{ - if (m_views.isEmpty()) { - if (m_currentInspectorPlugin) { - m_currentInspectorPlugin->deactivate(); - m_currentInspectorPlugin = 0; - } - return; - } - - if (state() == Enabled) { - if (m_inspectorPlugins.isEmpty()) - loadInspectorPlugins(); - - if (m_inspectorPlugins.isEmpty()) { - qWarning() << "QQmlInspector: No plugins found."; - QQmlDebugServer::instance()->removeService(this); - return; - } - - m_currentInspectorPlugin = 0; - foreach (QQmlInspectorInterface *inspector, m_inspectorPlugins) { - if (inspector->canHandleView(m_views.first())) { - m_currentInspectorPlugin = inspector; - break; - } - } - - if (!m_currentInspectorPlugin) { - qWarning() << "QQmlInspector: No plugin available for view '" << m_views.first()->metaObject()->className() << "'."; - return; - } - m_currentInspectorPlugin->activate(m_views.first()); - } else { - if (m_currentInspectorPlugin) { - m_currentInspectorPlugin->deactivate(); - m_currentInspectorPlugin = 0; - } - } -} - -void QQmlInspectorService::messageReceived(const QByteArray &message) -{ - QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message)); -} - -void QQmlInspectorService::processMessage(const QByteArray &message) -{ - if (m_currentInspectorPlugin) - m_currentInspectorPlugin->clientMessage(message); -} - -void QQmlInspectorService::loadInspectorPlugins() -{ - QStringList pluginCandidates; - const QStringList paths = QCoreApplication::libraryPaths(); - foreach (const QString &libPath, paths) { - const QDir dir(libPath + QLatin1String("/qmltooling")); - if (dir.exists()) - foreach (const QString &pluginPath, dir.entryList(QDir::Files)) - pluginCandidates << dir.absoluteFilePath(pluginPath); - } - - foreach (const QString &pluginPath, pluginCandidates) { - if (pluginPath.contains(QLatin1String("qmldbg_tcp"))) - continue; - if (qmlDebugVerbose()) - qDebug() << "QQmlInspector: Trying to load plugin " << pluginPath << "..."; - - QPluginLoader loader(pluginPath); - if (!loader.load()) { - if (qmlDebugVerbose()) - qDebug() << "QQmlInspector: Error while loading: " << loader.errorString(); - - continue; - } - - QQmlInspectorInterface *inspector = - qobject_cast<QQmlInspectorInterface*>(loader.instance()); - if (inspector) { - if (qmlDebugVerbose()) - qDebug() << "QQmlInspector: Plugin successfully loaded."; - m_inspectorPlugins << inspector; - } else { - if (qmlDebugVerbose()) - qDebug() << "QQmlInspector: Plugin does not implement interface QQmlInspectorInterface."; - - loader.unload(); - } - } -} - -QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofiler.cpp b/src/qml/debugger/qqmlprofiler.cpp index b35da8a714..a6423d769a 100644 --- a/src/qml/debugger/qqmlprofiler.cpp +++ b/src/qml/debugger/qqmlprofiler.cpp @@ -32,88 +32,13 @@ ****************************************************************************/ #include "qqmlprofiler_p.h" -#include "qqmlprofilerservice_p.h" #include "qqmldebugservice_p.h" QT_BEGIN_NAMESPACE -// convert to QByteArrays that can be sent to the debug client -// use of QDataStream can skew results -// (see tst_qqmldebugtrace::trace() benchmark) -void QQmlProfilerData::toByteArrays(QList<QByteArray> &messages) const -{ - QByteArray data; - Q_ASSERT_X(((messageType | detailType) & (1 << 31)) == 0, Q_FUNC_INFO, "You can use at most 31 message types and 31 detail types."); - for (uint decodedMessageType = 0; (messageType >> decodedMessageType) != 0; ++decodedMessageType) { - if ((messageType & (1 << decodedMessageType)) == 0) - continue; - - for (uint decodedDetailType = 0; (detailType >> decodedDetailType) != 0; ++decodedDetailType) { - if ((detailType & (1 << decodedDetailType)) == 0) - continue; - - //### using QDataStream is relatively expensive - QQmlDebugStream ds(&data, QIODevice::WriteOnly); - ds << time << decodedMessageType << decodedDetailType; - - switch (decodedMessageType) { - case QQmlProfilerDefinitions::RangeStart: - if (decodedDetailType == (int)QQmlProfilerDefinitions::Binding) - ds << QQmlProfilerDefinitions::QmlBinding; - break; - case QQmlProfilerDefinitions::RangeData: - ds << detailString; - break; - case QQmlProfilerDefinitions::RangeLocation: - ds << (detailUrl.isEmpty() ? detailString : detailUrl.toString()) << x << y; - break; - case QQmlProfilerDefinitions::RangeEnd: break; - default: - Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type."); - break; - } - messages << data; - data.clear(); - } - } -} - -QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine) : - QQmlAbstractProfilerAdapter(service) -{ - engine->enableProfiler(); - connect(this, SIGNAL(profilingEnabled(quint64)), engine->profiler, SLOT(startProfiling(quint64))); - connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)), - engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection); - connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling())); - connect(this, SIGNAL(profilingDisabledWhileWaiting()), - engine->profiler, SLOT(stopProfiling()), Qt::DirectConnection); - connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData())); - connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), - engine->profiler, SLOT(setTimer(QElapsedTimer))); - connect(engine->profiler, SIGNAL(dataReady(QList<QQmlProfilerData>)), - this, SLOT(receiveData(QList<QQmlProfilerData>))); -} - -qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) -{ - while (!data.empty() && data.front().time <= until) { - data.front().toByteArrays(messages); - data.pop_front(); - } - return data.empty() ? -1 : data.front().time; -} - -void QQmlProfilerAdapter::receiveData(const QList<QQmlProfilerData> &new_data) -{ - data = new_data; - service->dataReady(this); -} - - QQmlProfiler::QQmlProfiler() : featuresEnabled(0) { - static int metatype = qRegisterMetaType<QList<QQmlProfilerData> >(); + static int metatype = qRegisterMetaType<QVector<QQmlProfilerData> >(); Q_UNUSED(metatype); m_timer.start(); } @@ -127,16 +52,12 @@ void QQmlProfiler::stopProfiling() { featuresEnabled = false; reportData(); - m_data.clear(); } void QQmlProfiler::reportData() { - QList<QQmlProfilerData> result; - result.reserve(m_data.size()); - for (int i = 0; i < m_data.size(); ++i) - result.append(m_data[i]); - emit dataReady(result); + emit dataReady(m_data); + m_data.clear(); } QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 5c994b112f..3c8337c969 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -68,8 +68,9 @@ QT_BEGIN_NAMESPACE // This struct is somewhat dangerous to use: // The messageType is a bit field. You can pack multiple messages into // one object, e.g. RangeStart and RangeLocation. Each one will be read -// independently by toByteArrays. Thus you can only pack messages if their data -// doesn't overlap. It's up to you to figure that out. +// independently when converting to QByteArrays. Thus you can only pack +// messages if their data doesn't overlap. It's up to you to figure that +// out. struct Q_AUTOTEST_EXPORT QQmlProfilerData { QQmlProfilerData() {} @@ -103,8 +104,6 @@ struct Q_AUTOTEST_EXPORT QQmlProfilerData int x; //used by RangeLocation int y; //used by RangeLocation - - void toByteArrays(QList<QByteArray> &messages) const; }; Q_DECLARE_TYPEINFO(QQmlProfilerData, Q_MOVABLE_TYPE); @@ -171,24 +170,11 @@ public slots: void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: - void dataReady(const QList<QQmlProfilerData> &); + void dataReady(const QVector<QQmlProfilerData> &); protected: QElapsedTimer m_timer; - QVarLengthArray<QQmlProfilerData> m_data; -}; - -class QQmlProfilerAdapter : public QQmlAbstractProfilerAdapter { - Q_OBJECT -public: - QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine); - qint64 sendMessages(qint64 until, QList<QByteArray> &messages); - -public slots: - void receiveData(const QList<QQmlProfilerData> &new_data); - -private: - QList<QQmlProfilerData> data; + QVector<QQmlProfilerData> m_data; }; // @@ -332,6 +318,6 @@ private: }; QT_END_NAMESPACE -Q_DECLARE_METATYPE(QList<QQmlProfilerData>) +Q_DECLARE_METATYPE(QVector<QQmlProfilerData>) #endif // QQMLPROFILER_P_H diff --git a/src/qml/debugger/qv4debugservice.cpp b/src/qml/debugger/qv4debugservice.cpp deleted file mode 100644 index cefb29e031..0000000000 --- a/src/qml/debugger/qv4debugservice.cpp +++ /dev/null @@ -1,1265 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4debugservice_p.h" -#include "qqmlconfigurabledebugservice_p_p.h" -#include "qqmlengine.h" -#include "qv4debugging_p.h" -#include "qv4engine_p.h" -#include "qv4function_p.h" -#include "qqmldebugserver_p.h" - -#include <private/qv8engine_p.h> - -#include <QtCore/QJsonArray> -#include <QtCore/QJsonDocument> -#include <QtCore/QJsonObject> -#include <QtCore/QJsonValue> - -const char *V4_CONNECT = "connect"; -const char *V4_DISCONNECT = "disconnect"; -const char *V4_BREAK_ON_SIGNAL = "breakonsignal"; -const char *V4_ADD_BREAKPOINT = "addBreakpoint"; -const char *V4_REMOVE_BREAKPOINT = "removeBreakpoint"; -const char *V4_PAUSE = "interrupt"; -const char *V4_ALL = "all"; -const char *V4_BREAK = "break"; - -const char *V4_FILENAME = "filename"; -const char *V4_LINENUMBER = "linenumber"; - -#define NO_PROTOCOL_TRACING -#ifdef NO_PROTOCOL_TRACING -# define TRACE_PROTOCOL(x) -#else -#include <QtCore/QDebug> -# define TRACE_PROTOCOL(x) x -#endif - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QV4DebugService, v4ServiceInstance) - -class QV4DebugServicePrivate; - -class QV4DebuggerAgent : public QV4::Debugging::DebuggerAgent -{ -public: - QV4DebuggerAgent(QV4DebugServicePrivate *debugServicePrivate) - : debugServicePrivate(debugServicePrivate) - {} - - QV4::Debugging::Debugger *firstDebugger() const - { - // Currently only 1 single engine is supported, so: - if (m_debuggers.isEmpty()) - return 0; - else - return m_debuggers.first(); - } - - bool isRunning() const - { - // Currently only 1 single engine is supported, so: - if (QV4::Debugging::Debugger *debugger = firstDebugger()) - return debugger->state() == QV4::Debugging::Debugger::Running; - else - return false; - } - -public slots: - virtual void debuggerPaused(QV4::Debugging::Debugger *debugger, QV4::Debugging::PauseReason reason); - virtual void sourcesCollected(QV4::Debugging::Debugger *debugger, QStringList sources, int requestSequenceNr); - -private: - QV4DebugServicePrivate *debugServicePrivate; -}; - -class V8CommandHandler; -class UnknownV8CommandHandler; - -class VariableCollector: public QV4::Debugging::Debugger::Collector -{ -public: - VariableCollector(QV4::ExecutionEngine *engine) - : Collector(engine) - , destination(0) - {} - - virtual ~VariableCollector() {} - - void collectScope(QJsonArray *dest, QV4::Debugging::Debugger *debugger, int frameNr, int scopeNr) - { - qSwap(destination, dest); - bool oldIsProp = isProperty(); - setIsProperty(true); - debugger->collectArgumentsInContext(this, frameNr, scopeNr); - debugger->collectLocalsInContext(this, frameNr, scopeNr); - setIsProperty(oldIsProp); - qSwap(destination, dest); - } - - void setDestination(QJsonArray *dest) - { destination = dest; } - - QJsonArray retrieveRefsToInclude() - { - QJsonArray result; - qSwap(refsToInclude, result); - return result; - } - - QJsonValue lookup(int handle, bool addRefs = true) - { - if (handle < 0) - handle = -handle; - - if (addRefs) - foreach (int ref, refsByHandle[handle]) - refsToInclude.append(lookup(ref, false)); - return refs[handle]; - } - - QJsonObject makeRef(int refId) - { - QJsonObject ref; - ref[QLatin1String("ref")] = refId; - return ref; - } - - QJsonObject addFunctionRef(const QString &name) - { - const int refId = newRefId(); - - QJsonObject func; - func[QLatin1String("handle")] = refId; - func[QLatin1String("type")] = QStringLiteral("function"); - func[QLatin1String("className")] = QStringLiteral("Function"); - func[QLatin1String("name")] = name; - insertRef(func, refId); - - return makeRef(refId); - } - - QJsonObject addScriptRef(const QString &name) - { - const int refId = newRefId(); - - QJsonObject func; - func[QLatin1String("handle")] = refId; - func[QLatin1String("type")] = QStringLiteral("script"); - func[QLatin1String("name")] = name; - insertRef(func, refId); - - return makeRef(refId); - } - - QJsonObject addObjectRef(QJsonObject obj, bool anonymous) - { - int ref = newRefId(); - - if (anonymous) - ref = -ref; - obj[QLatin1String("handle")] = ref; - obj[QLatin1String("type")] = QStringLiteral("object"); - insertRef(obj, ref); - QSet<int> used; - qSwap(usedRefs, used); - refsByHandle.insert(ref, used); - - return makeRef(ref); - } - -protected: - virtual void addUndefined(const QString &name) - { - QJsonObject o; - addHandle(name, o, QStringLiteral("undefined")); - } - - virtual void addNull(const QString &name) - { - QJsonObject o; - addHandle(name, o, QStringLiteral("null")); - } - - virtual void addBoolean(const QString &name, bool value) - { - QJsonObject o; - o[QLatin1String("value")] = value; - addHandle(name, o, QStringLiteral("boolean")); - } - - virtual void addString(const QString &name, const QString &value) - { - QJsonObject o; - o[QLatin1String("value")] = value; - addHandle(name, o, QStringLiteral("string")); - } - - virtual void addObject(const QString &name, const QV4::Value &value) - { - QV4::Scope scope(engine()); - QV4::ScopedObject obj(scope, value.asObject()); - - int ref = cachedObjectRef(obj); - if (ref != -1) { - addNameRefPair(name, ref); - } else { - int ref = newRefId(); - cacheObjectRef(obj, ref); - - QJsonArray properties, *prev = &properties; - QSet<int> used; - qSwap(usedRefs, used); - qSwap(destination, prev); - collect(obj); - qSwap(destination, prev); - qSwap(usedRefs, used); - - QJsonObject o; - o[QLatin1String("properties")] = properties; - addHandle(name, o, QStringLiteral("object"), ref); - refsByHandle.insert(ref, used); - } - } - - virtual void addInteger(const QString &name, int value) - { - QJsonObject o; - o[QLatin1String("value")] = value; - addHandle(name, o, QStringLiteral("number")); - } - - virtual void addDouble(const QString &name, double value) - { - QJsonObject o; - o[QLatin1String("value")] = value; - addHandle(name, o, QStringLiteral("number")); - } - -private: - int addHandle(const QString &name, QJsonObject object, const QString &type, int suppliedRef = -1) - { - Q_ASSERT(destination); - - object[QLatin1String("type")] = type; - - QJsonDocument tmp; - tmp.setObject(object); - QByteArray key = tmp.toJson(QJsonDocument::Compact); - - int ref; - if (suppliedRef == -1) { - ref = refCache.value(key, -1); - if (ref == -1) { - ref = newRefId(); - object[QLatin1String("handle")] = ref; - insertRef(object, ref); - refCache.insert(key, ref); - } - } else { - ref = suppliedRef; - object[QLatin1String("handle")] = ref; - insertRef(object, ref); - refCache.insert(key, ref); - } - - addNameRefPair(name, ref); - return ref; - } - - void addNameRefPair(const QString &name, int ref) - { - QJsonObject nameValuePair; - nameValuePair[QLatin1String("name")] = name; - if (isProperty()) { - nameValuePair[QLatin1String("ref")] = ref; - } else { - QJsonObject refObj; - refObj[QLatin1String("ref")] = ref; - nameValuePair[QLatin1String("value")] = refObj; - } - destination->append(nameValuePair); - usedRefs.insert(ref); - } - - int newRefId() - { - int ref = refs.count(); - refs.insert(ref, QJsonValue()); - return ref; - } - - void insertRef(const QJsonValue &value, int refId) - { - if (refId < 0) - refId = -refId; - - refs.insert(refId, value); - refsToInclude.append(value); - } - - void cacheObjectRef(QV4::Value obj, int ref) - { - objectRefs.insert(obj.val, ref); - } - - int cachedObjectRef(QV4::Value obj) const - { - return objectRefs.value(obj.val, -1); - } - -private: - QJsonArray refsToInclude; - QHash<int, QJsonValue> refs; - QHash<QByteArray, int> refCache; - QJsonArray *destination; - QSet<int> usedRefs; - QHash<int, QSet<int> > refsByHandle; - QHash<quint64, int> objectRefs; -}; - -class QV4DebugServicePrivate : public QQmlConfigurableDebugServicePrivate -{ - Q_DECLARE_PUBLIC(QV4DebugService) - -public: - QV4DebugServicePrivate(); - ~QV4DebugServicePrivate() { qDeleteAll(handlers.values()); } - - static QByteArray packMessage(const QByteArray &command, const QByteArray &message = QByteArray()) - { - QByteArray reply; - QQmlDebugStream rs(&reply, QIODevice::WriteOnly); - static const QByteArray cmd("V8DEBUG"); - rs << cmd << command << message; - return reply; - } - - void send(QJsonObject v8Payload) - { - v8Payload[QLatin1String("seq")] = sequence++; - QJsonDocument doc; - doc.setObject(v8Payload); -#ifdef NO_PROTOCOL_TRACING - QByteArray responseData = doc.toJson(QJsonDocument::Compact); -#else - QByteArray responseData = doc.toJson(QJsonDocument::Indented); -#endif - - TRACE_PROTOCOL(qDebug() << "sending response for:" << responseData << endl); - - q_func()->sendMessage(packMessage("v8message", responseData)); - } - - void processCommand(const QByteArray &command, const QByteArray &data); - - QV4DebuggerAgent debuggerAgent; - - QStringList breakOnSignals; - QMap<int, QV4::Debugging::Debugger *> debuggerMap; - static int debuggerIndex; - static int sequence; - const int version; - - V8CommandHandler *v8CommandHandler(const QString &command) const; - - void clearHandles(QV4::ExecutionEngine *engine) - { - theCollector.reset(new VariableCollector(engine)); - } - - QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr, - QV4::Debugging::Debugger *debugger) - { - QJsonObject frame; - frame[QLatin1String("index")] = frameNr; - frame[QLatin1String("debuggerFrame")] = false; - frame[QLatin1String("func")] = theCollector->addFunctionRef(stackFrame.function); - frame[QLatin1String("script")] = theCollector->addScriptRef(stackFrame.source); - frame[QLatin1String("line")] = stackFrame.line - 1; - if (stackFrame.column >= 0) - frame[QLatin1String("column")] = stackFrame.column; - - QJsonArray properties; - theCollector->setDestination(&properties); - if (debugger->collectThisInContext(theCollector.data(), frameNr)) { - QJsonObject obj; - obj[QLatin1String("properties")] = properties; - frame[QLatin1String("receiver")] = theCollector->addObjectRef(obj, false); - } - - QJsonArray scopes; - // Only type and index are used by Qt Creator, so we keep it easy: - QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes = debugger->getScopeTypes(frameNr); - for (int i = 0, ei = scopeTypes.count(); i != ei; ++i) { - int type = encodeScopeType(scopeTypes[i]); - if (type == -1) - continue; - - QJsonObject scope; - scope[QLatin1String("index")] = i; - scope[QLatin1String("type")] = type; - scopes.push_back(scope); - } - frame[QLatin1String("scopes")] = scopes; - - return frame; - } - - int encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType) - { - switch (scopeType) { - case QV4::Heap::ExecutionContext::Type_GlobalContext: - return 0; - break; - case QV4::Heap::ExecutionContext::Type_CatchContext: - return 4; - break; - case QV4::Heap::ExecutionContext::Type_WithContext: - return 2; - break; - case QV4::Heap::ExecutionContext::Type_SimpleCallContext: - case QV4::Heap::ExecutionContext::Type_CallContext: - return 1; - break; - case QV4::Heap::ExecutionContext::Type_QmlContext: - default: - return -1; - } - } - - QJsonObject buildScope(int frameNr, int scopeNr, QV4::Debugging::Debugger *debugger) - { - QJsonObject scope; - - QJsonArray properties; - theCollector->collectScope(&properties, debugger, frameNr, scopeNr); - - QJsonObject anonymous; - anonymous[QLatin1String("properties")] = properties; - - QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes = debugger->getScopeTypes(frameNr); - scope[QLatin1String("type")] = encodeScopeType(scopeTypes[scopeNr]); - scope[QLatin1String("index")] = scopeNr; - scope[QLatin1String("frameIndex")] = frameNr; - scope[QLatin1String("object")] = theCollector->addObjectRef(anonymous, true); - - return scope; - } - - QJsonValue lookup(int refId) const { return theCollector->lookup(refId); } - - QJsonArray buildRefs() - { - return theCollector->retrieveRefsToInclude(); - } - - VariableCollector *collector() const - { - return theCollector.data(); - } - - void selectFrame(int frameNr) - { theSelectedFrame = frameNr; } - - int selectedFrame() const - { return theSelectedFrame; } - -private: - QScopedPointer<VariableCollector> theCollector; - int theSelectedFrame; - - void addHandler(V8CommandHandler* handler); - QHash<QString, V8CommandHandler*> handlers; - QScopedPointer<UnknownV8CommandHandler> unknownV8CommandHandler; -}; - -int QV4DebugServicePrivate::debuggerIndex = 0; -int QV4DebugServicePrivate::sequence = 0; - -class V8CommandHandler -{ -public: - V8CommandHandler(const QString &command) - : cmd(command) - {} - - virtual ~V8CommandHandler() - {} - - QString command() const { return cmd; } - - void handle(const QJsonObject &request, QQmlDebugService *s, QV4DebugServicePrivate *p) - { - TRACE_PROTOCOL(qDebug() << "handling command" << command() << "..."); - - req = request; - seq = req.value(QStringLiteral("seq")); - debugService = s; - debugServicePrivate = p; - - handleRequest(); - if (!response.isEmpty()) { - response[QLatin1String("type")] = QStringLiteral("response"); - debugServicePrivate->send(response); - } - - debugServicePrivate = 0; - debugService = 0; - seq = QJsonValue(); - req = QJsonObject(); - response = QJsonObject(); - } - - virtual void handleRequest() = 0; - -protected: - void addCommand() { response.insert(QStringLiteral("command"), cmd); } - void addRequestSequence() { response.insert(QStringLiteral("request_seq"), seq); } - void addSuccess(bool success) { response.insert(QStringLiteral("success"), success); } - void addBody(const QJsonObject &body) - { - response.insert(QStringLiteral("body"), body); - } - - void addRunning() - { - response.insert(QStringLiteral("running"), debugServicePrivate->debuggerAgent.isRunning()); - } - - void addRefs() - { - response.insert(QStringLiteral("refs"), debugServicePrivate->buildRefs()); - } - - void createErrorResponse(const QString &msg) - { - QJsonValue command = req.value(QStringLiteral("command")); - response.insert(QStringLiteral("command"), command); - addRequestSequence(); - addSuccess(false); - addRunning(); - response.insert(QStringLiteral("message"), msg); - } - - int requestSequenceNr() const - { return seq.toInt(-1); } - -protected: - QString cmd; - QJsonObject req; - QJsonValue seq; - QQmlDebugService *debugService; - QV4DebugServicePrivate *debugServicePrivate; - QJsonObject response; -}; - -class UnknownV8CommandHandler: public V8CommandHandler -{ -public: - UnknownV8CommandHandler(): V8CommandHandler(QString()) {} - - virtual void handleRequest() - { - QString msg = QStringLiteral("unimplemented command \""); - msg += req.value(QStringLiteral("command")).toString(); - msg += QStringLiteral("\""); - createErrorResponse(msg); - } -}; - -namespace { -class V8VersionRequest: public V8CommandHandler -{ -public: - V8VersionRequest(): V8CommandHandler(QStringLiteral("version")) {} - - virtual void handleRequest() - { - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - QJsonObject body; - body.insert(QStringLiteral("V8Version"), - QLatin1String("this is not V8, this is V4 in Qt " QT_VERSION_STR)); - addBody(body); - } -}; - -class V8SetBreakPointRequest: public V8CommandHandler -{ -public: - V8SetBreakPointRequest(): V8CommandHandler(QStringLiteral("setbreakpoint")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject args = req.value(QStringLiteral("arguments")).toObject(); - if (args.isEmpty()) - return; - - QString type = args.value(QStringLiteral("type")).toString(); - if (type != QStringLiteral("scriptRegExp")) { - createErrorResponse(QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type)); - return; - } - - QString fileName = args.value(QStringLiteral("target")).toString(); - if (fileName.isEmpty()) { - createErrorResponse(QStringLiteral("breakpoint has no file name")); - return; - } - - int line = args.value(QStringLiteral("line")).toInt(-1); - if (line < 0) { - createErrorResponse(QStringLiteral("breakpoint has an invalid line number")); - return; - } - - bool enabled = args.value(QStringLiteral("enabled")).toBool(true); - QString condition = args.value(QStringLiteral("condition")).toString(); - - // set the break point: - int id = debugServicePrivate->debuggerAgent.addBreakPoint(fileName, line + 1, enabled, condition); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - QJsonObject body; - body.insert(QStringLiteral("type"), type); - body.insert(QStringLiteral("breakpoint"), id); - // It's undocumented, but V8 sends back an actual_locations array too. However, our - // Debugger currently doesn't tell us when it resolved a breakpoint, so we'll leave them - // pending until the breakpoint is hit for the first time. - addBody(body); - } -}; - -class V8ClearBreakPointRequest: public V8CommandHandler -{ -public: - V8ClearBreakPointRequest(): V8CommandHandler(QStringLiteral("clearbreakpoint")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject args = req.value(QStringLiteral("arguments")).toObject(); - if (args.isEmpty()) - return; - - int id = args.value(QStringLiteral("breakpoint")).toInt(-1); - if (id < 0) { - createErrorResponse(QStringLiteral("breakpoint has an invalid number")); - return; - } - - // remove the break point: - debugServicePrivate->debuggerAgent.removeBreakPoint(id); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - QJsonObject body; - body.insert(QStringLiteral("type"), QStringLiteral("scriptRegExp")); - body.insert(QStringLiteral("breakpoint"), id); - addBody(body); - } -}; - -class V8BacktraceRequest: public V8CommandHandler -{ -public: - V8BacktraceRequest(): V8CommandHandler(QStringLiteral("backtrace")) {} - - virtual void handleRequest() - { - // decypher the payload: - - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - int fromFrame = arguments.value(QStringLiteral("fromFrame")).toInt(0); - int toFrame = arguments.value(QStringLiteral("toFrame")).toInt(fromFrame + 10); - // no idea what the bottom property is for, so we'll ignore it. - - QV4::Debugging::Debugger *debugger = debugServicePrivate->debuggerAgent.firstDebugger(); - - QJsonArray frameArray; - QVector<QV4::StackFrame> frames = debugger->stackTrace(toFrame); - for (int i = fromFrame; i < toFrame && i < frames.size(); ++i) - frameArray.push_back(debugServicePrivate->buildFrame(frames[i], i, debugger)); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - QJsonObject body; - if (frameArray.isEmpty()) { - body.insert(QStringLiteral("totalFrames"), 0); - } else { - body.insert(QStringLiteral("fromFrame"), fromFrame); - body.insert(QStringLiteral("toFrame"), fromFrame + frameArray.size()); - body.insert(QStringLiteral("frames"), frameArray); - } - addBody(body); - addRefs(); - } -}; - -class V8FrameRequest: public V8CommandHandler -{ -public: - V8FrameRequest(): V8CommandHandler(QStringLiteral("frame")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - const int frameNr = arguments.value(QStringLiteral("number")).toInt(debugServicePrivate->selectedFrame()); - - QV4::Debugging::Debugger *debugger = debugServicePrivate->debuggerAgent.firstDebugger(); - QVector<QV4::StackFrame> frames = debugger->stackTrace(frameNr + 1); - if (frameNr < 0 || frameNr >= frames.size()) { - createErrorResponse(QStringLiteral("frame command has invalid frame number")); - return; - } - - debugServicePrivate->selectFrame(frameNr); - QJsonObject frame = debugServicePrivate->buildFrame(frames[frameNr], frameNr, debugger); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(frame); - addRefs(); - } -}; - -class V8ScopeRequest: public V8CommandHandler -{ -public: - V8ScopeRequest(): V8CommandHandler(QStringLiteral("scope")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - const int frameNr = arguments.value(QStringLiteral("frameNumber")).toInt(debugServicePrivate->selectedFrame()); - const int scopeNr = arguments.value(QStringLiteral("number")).toInt(0); - - QV4::Debugging::Debugger *debugger = debugServicePrivate->debuggerAgent.firstDebugger(); - QVector<QV4::StackFrame> frames = debugger->stackTrace(frameNr + 1); - if (frameNr < 0 || frameNr >= frames.size()) { - createErrorResponse(QStringLiteral("scope command has invalid frame number")); - return; - } - if (scopeNr < 0) { - createErrorResponse(QStringLiteral("scope command has invalid scope number")); - return; - } - - QJsonObject scope = debugServicePrivate->buildScope(frameNr, scopeNr, debugger); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(scope); - addRefs(); - } -}; - -class V8LookupRequest: public V8CommandHandler -{ -public: - V8LookupRequest(): V8CommandHandler(QStringLiteral("lookup")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QJsonArray handles = arguments.value(QStringLiteral("handles")).toArray(); - - QJsonObject body; - foreach (QJsonValue handle, handles) - body[QString::number(handle.toInt())] = debugServicePrivate->lookup(handle.toInt()); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(body); - addRefs(); - } -}; - -class V8ContinueRequest: public V8CommandHandler -{ -public: - V8ContinueRequest(): V8CommandHandler(QStringLiteral("continue")) {} - - virtual void handleRequest() - { - // decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - - QV4::Debugging::Debugger *debugger = debugServicePrivate->debuggerAgent.firstDebugger(); - - if (arguments.empty()) { - debugger->resume(QV4::Debugging::Debugger::FullThrottle); - } else { - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString stepAction = arguments.value(QStringLiteral("stepaction")).toString(); - const int stepcount = arguments.value(QStringLiteral("stepcount")).toInt(1); - if (stepcount != 1) - qWarning() << "Step count other than 1 is not supported."; - - if (stepAction == QStringLiteral("in")) { - debugger->resume(QV4::Debugging::Debugger::StepIn); - } else if (stepAction == QStringLiteral("out")) { - debugger->resume(QV4::Debugging::Debugger::StepOut); - } else if (stepAction == QStringLiteral("next")) { - debugger->resume(QV4::Debugging::Debugger::StepOver); - } else { - createErrorResponse(QStringLiteral("continue command has invalid stepaction")); - return; - } - } - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - } -}; - -class V8DisconnectRequest: public V8CommandHandler -{ -public: - V8DisconnectRequest(): V8CommandHandler(QStringLiteral("disconnect")) {} - - virtual void handleRequest() - { - debugServicePrivate->debuggerAgent.removeAllBreakPoints(); - debugServicePrivate->debuggerAgent.resumeAll(); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - } -}; - -class V8SetExceptionBreakRequest: public V8CommandHandler -{ -public: - V8SetExceptionBreakRequest(): V8CommandHandler(QStringLiteral("setexceptionbreak")) {} - - virtual void handleRequest() - { - bool wasEnabled = debugServicePrivate->debuggerAgent.breakOnThrow(); - - //decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString type = arguments.value(QStringLiteral("type")).toString(); - bool enabled = arguments.value(QStringLiteral("number")).toBool(!wasEnabled); - - if (type == QStringLiteral("all")) { - // that's fine - } else if (type == QStringLiteral("uncaught")) { - createErrorResponse(QStringLiteral("breaking only on uncaught exceptions is not supported yet")); - return; - } else { - createErrorResponse(QStringLiteral("invalid type for break on exception")); - return; - } - - // do it: - debugServicePrivate->debuggerAgent.setBreakOnThrow(enabled); - - QJsonObject body; - body[QLatin1String("type")] = type; - body[QLatin1String("enabled")] = debugServicePrivate->debuggerAgent.breakOnThrow(); - - // response: - addBody(body); - addRunning(); - addSuccess(true); - addRequestSequence(); - addCommand(); - } -}; - -class V8ScriptsRequest: public V8CommandHandler -{ -public: - V8ScriptsRequest(): V8CommandHandler(QStringLiteral("scripts")) {} - - virtual void handleRequest() - { - //decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - int types = arguments.value(QStringLiteral("types")).toInt(-1); - if (types < 0 || types > 7) { - createErrorResponse(QStringLiteral("invalid types value in scripts command")); - return; - } else if (types != 4) { - createErrorResponse(QStringLiteral("unsupported types value in scripts command")); - return; - } - - // do it: - debugServicePrivate->debuggerAgent.firstDebugger()->gatherSources(requestSequenceNr()); - - // response will be send by - } -}; - -// Request: -// { -// "seq": 4, -// "type": "request", -// "command": "evaluate", -// "arguments": { -// "expression": "a", -// "frame": 0 -// } -// } -// -// Response: -// { -// "body": { -// "handle": 3, -// "type": "number", -// "value": 1 -// }, -// "command": "evaluate", -// "refs": [], -// "request_seq": 4, -// "running": false, -// "seq": 5, -// "success": true, -// "type": "response" -// } -// -// The "value" key in "body" is the result of evaluating the expression in the request. -class V8EvaluateRequest: public V8CommandHandler -{ -public: - V8EvaluateRequest(): V8CommandHandler(QStringLiteral("evaluate")) {} - - virtual void handleRequest() - { - //decypher the payload: - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString expression = arguments.value(QStringLiteral("expression")).toString(); - const int frame = arguments.value(QStringLiteral("frame")).toInt(0); - - QV4::Debugging::Debugger *debugger = debugServicePrivate->debuggerAgent.firstDebugger(); - Q_ASSERT(debugger->state() == QV4::Debugging::Debugger::Paused); - - VariableCollector *collector = debugServicePrivate->collector(); - QJsonArray dest; - collector->setDestination(&dest); - debugger->evaluateExpression(frame, expression, collector); - - const int ref = dest.at(0).toObject().value(QStringLiteral("value")).toObject() - .value(QStringLiteral("ref")).toInt(); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(collector->lookup(ref).toObject()); - addRefs(); - } -}; -} // anonymous namespace - -QV4DebugServicePrivate::QV4DebugServicePrivate() - : debuggerAgent(this) - , version(1) - , theSelectedFrame(0) - , unknownV8CommandHandler(new UnknownV8CommandHandler) -{ - addHandler(new V8VersionRequest); - addHandler(new V8SetBreakPointRequest); - addHandler(new V8ClearBreakPointRequest); - addHandler(new V8BacktraceRequest); - addHandler(new V8FrameRequest); - addHandler(new V8ScopeRequest); - addHandler(new V8LookupRequest); - addHandler(new V8ContinueRequest); - addHandler(new V8DisconnectRequest); - addHandler(new V8SetExceptionBreakRequest); - addHandler(new V8ScriptsRequest); - addHandler(new V8EvaluateRequest); -} - -void QV4DebugServicePrivate::addHandler(V8CommandHandler* handler) -{ - handlers[handler->command()] = handler; -} - -V8CommandHandler *QV4DebugServicePrivate::v8CommandHandler(const QString &command) const -{ - V8CommandHandler *handler = handlers.value(command, 0); - if (handler) - return handler; - else - return unknownV8CommandHandler.data(); -} - -QV4DebugService::QV4DebugService(QObject *parent) - : QQmlConfigurableDebugService(*(new QV4DebugServicePrivate()), - QStringLiteral("V8Debugger"), 1, parent) -{} - -QV4DebugService::~QV4DebugService() -{ -} - -QV4DebugService *QV4DebugService::instance() -{ - return v4ServiceInstance(); -} - -void QV4DebugService::engineAboutToBeAdded(QQmlEngine *engine) -{ - Q_D(QV4DebugService); - QMutexLocker lock(configMutex()); - if (engine) { - QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); - if (QQmlDebugServer *server = QQmlDebugServer::instance()) { - if (ee) { - ee->enableDebugger(); - QV4::Debugging::Debugger *debugger = ee->debugger; - d->debuggerMap.insert(d->debuggerIndex++, debugger); - d->debuggerAgent.addDebugger(debugger); - d->debuggerAgent.moveToThread(server->thread()); - moveToThread(server->thread()); - } - } - } - QQmlConfigurableDebugService::engineAboutToBeAdded(engine); -} - -void QV4DebugService::engineAboutToBeRemoved(QQmlEngine *engine) -{ - Q_D(QV4DebugService); - QMutexLocker lock(configMutex()); - if (engine){ - const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); - if (ee) { - QV4::Debugging::Debugger *debugger = ee->debugger; - typedef QMap<int, QV4::Debugging::Debugger *>::const_iterator DebuggerMapIterator; - const DebuggerMapIterator end = d->debuggerMap.constEnd(); - for (DebuggerMapIterator i = d->debuggerMap.constBegin(); i != end; ++i) { - if (i.value() == debugger) { - d->debuggerMap.remove(i.key()); - break; - } - } - d->debuggerAgent.removeDebugger(debugger); - } - } - QQmlConfigurableDebugService::engineAboutToBeRemoved(engine); -} - -void QV4DebugService::signalEmitted(const QString &signal) -{ - //This function is only called by QQmlBoundSignal - //only if there is a slot connected to the signal. Hence, there - //is no need for additional check. - Q_D(QV4DebugService); - - //Parse just the name and remove the class info - //Normalize to Lower case. - QString signalName = signal.left(signal.indexOf(QLatin1Char('('))).toLower(); - - foreach (const QString &signal, d->breakOnSignals) { - if (signal == signalName) { - // TODO: pause debugger - break; - } - } -} - -void QV4DebugService::messageReceived(const QByteArray &message) -{ - Q_D(QV4DebugService); - QMutexLocker lock(configMutex()); - - QQmlDebugStream ms(message); - QByteArray header; - ms >> header; - - TRACE_PROTOCOL(qDebug() << "received message with header" << header); - - if (header == "V8DEBUG") { - QByteArray type; - QByteArray payload; - ms >> type >> payload; - TRACE_PROTOCOL(qDebug() << "... type:" << type); - - if (type == V4_CONNECT) { - sendMessage(d->packMessage(type)); - stopWaiting(); - } else if (type == V4_PAUSE) { - d->debuggerAgent.pauseAll(); - sendSomethingToSomebody(type); - } else if (type == V4_BREAK_ON_SIGNAL) { - QByteArray signal; - bool enabled; - ms >> signal >> enabled; - //Normalize to lower case. - QString signalName(QString::fromUtf8(signal).toLower()); - if (enabled) - d->breakOnSignals.append(signalName); - else - d->breakOnSignals.removeOne(signalName); - } else if (type == "v8request") { - handleV8Request(payload); - } else if (type == V4_DISCONNECT) { - TRACE_PROTOCOL(qDebug() << "... payload:" << payload); - handleV8Request(payload); - } else { - sendSomethingToSomebody(type, 0); - } - } -} - -void QV4DebugService::sendSomethingToSomebody(const char *type, int magicNumber) -{ - Q_D(QV4DebugService); - - QByteArray response; - QQmlDebugStream rs(&response, QIODevice::WriteOnly); - rs << QByteArray(type) - << QByteArray::number(d->version) << QByteArray::number(magicNumber); - sendMessage(d->packMessage(type, response)); -} - -void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::Debugger *debugger, QV4::Debugging::PauseReason reason) -{ - Q_UNUSED(reason); - - debugServicePrivate->clearHandles(debugger->engine()); - - QJsonObject event, body, script; - event.insert(QStringLiteral("type"), QStringLiteral("event")); - - switch (reason) { - case QV4::Debugging::Step: - case QV4::Debugging::PauseRequest: - case QV4::Debugging::BreakPoint: { - event.insert(QStringLiteral("event"), QStringLiteral("break")); - QVector<QV4::StackFrame> frames = debugger->stackTrace(1); - if (frames.isEmpty()) - break; - - const QV4::StackFrame &topFrame = frames.first(); - body.insert(QStringLiteral("invocationText"), topFrame.function); - body.insert(QStringLiteral("sourceLine"), topFrame.line - 1); - if (topFrame.column > 0) - body.insert(QStringLiteral("sourceColumn"), topFrame.column); - QJsonArray breakPoints; - foreach (int breakPointId, breakPointIds(topFrame.source, topFrame.line)) - breakPoints.push_back(breakPointId); - body.insert(QStringLiteral("breakpoints"), breakPoints); - script.insert(QStringLiteral("name"), topFrame.source); - } break; - case QV4::Debugging::Throwing: - // TODO: complete this! - event.insert(QStringLiteral("event"), QStringLiteral("exception")); - break; - } - - if (!script.isEmpty()) - body.insert(QStringLiteral("script"), script); - if (!body.isEmpty()) - event.insert(QStringLiteral("body"), body); - debugServicePrivate->send(event); -} - -void QV4DebuggerAgent::sourcesCollected(QV4::Debugging::Debugger *debugger, QStringList sources, int requestSequenceNr) -{ - QJsonArray body; - foreach (const QString source, sources) { - QJsonObject src; - src[QLatin1String("name")] = source; - src[QLatin1String("scriptType")] = 4; - body.append(src); - } - - QJsonObject response; - response[QLatin1String("success")] = true; - response[QLatin1String("running")] = debugger->state() == QV4::Debugging::Debugger::Running; - response[QLatin1String("body")] = body; - response[QLatin1String("command")] = QStringLiteral("scripts"); - response[QLatin1String("request_seq")] = requestSequenceNr; - response[QLatin1String("type")] = QStringLiteral("response"); - debugServicePrivate->send(response); -} - -void QV4DebugService::handleV8Request(const QByteArray &payload) -{ - Q_D(QV4DebugService); - - TRACE_PROTOCOL(qDebug() << "v8request, payload:" << payload); - - QJsonDocument request = QJsonDocument::fromJson(payload); - QJsonObject o = request.object(); - QJsonValue type = o.value(QStringLiteral("type")); - if (type.toString() == QStringLiteral("request")) { - QJsonValue command = o.value(QStringLiteral("command")); - V8CommandHandler *h = d->v8CommandHandler(command.toString()); - if (h) - h->handle(o, this, d); - } -} - -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 09e5b14c97..b5765b5705 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -241,16 +241,16 @@ void Assembler::enterStandardStackFrame(const RegisterInformation ®ularRegist subPtr(TrustedImm32(frameSize), StackPointerRegister); Address slotAddr(StackFrameRegister, 0); - for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) { - Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); - slotAddr.offset -= RegisterSize; - storePtr(regularRegistersToSave.at(i).reg<RegisterID>(), slotAddr); - } 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); } + for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) { + Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); + slotAddr.offset -= RegisterSize; + storePtr(regularRegistersToSave.at(i).reg<RegisterID>(), slotAddr); + } } void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, @@ -259,16 +259,16 @@ void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegist Address slotAddr(StackFrameRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double)); // restore the callee saved registers - for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) { - Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); - JSC::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); - slotAddr.offset += sizeof(double); - } for (int i = regularRegistersToSave.size() - 1; i >= 0; --i) { Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); loadPtr(slotAddr, regularRegistersToSave.at(i).reg<RegisterID>()); slotAddr.offset += RegisterSize; } + for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) { + Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); + JSC::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); + slotAddr.offset += sizeof(double); + } Q_ASSERT(slotAddr.offset == 0); diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 3b65acb26c..ab74211d23 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -478,7 +478,7 @@ public: load64(addr, dest); } else { QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.val), dest); + move(TrustedImm64(undefined.rawValue()), dest); } } @@ -491,7 +491,7 @@ public: load64(addr, dest); } else { QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.val), dest); + move(TrustedImm64(undefined.rawValue()), dest); } } @@ -500,7 +500,7 @@ public: Q_UNUSED(argumentNumber); QV4::Value v = convertToValue(c); - move(TrustedImm64(v.val), dest); + move(TrustedImm64(v.rawValue()), dest); } void loadArgumentInRegister(IR::Expr* expr, RegisterID dest, int argumentNumber) @@ -509,7 +509,7 @@ public: if (!expr) { QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.val), dest); + move(TrustedImm64(undefined.rawValue()), dest); } else if (IR::Temp *t = expr->asTemp()){ loadArgumentInRegister(t, dest, argumentNumber); } else if (IR::ArgLocal *al = expr->asArgLocal()) { @@ -565,6 +565,8 @@ public: 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); @@ -595,6 +597,14 @@ public: 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) @@ -781,11 +791,11 @@ public: void storeValue(QV4::Primitive value, Address destination) { #ifdef VALUE_FITS_IN_REGISTER - store64(TrustedImm64(value.val), destination); + store64(TrustedImm64(value.rawValue()), destination); #else - store32(TrustedImm32(value.int_32), destination); + store32(TrustedImm32(value.int_32()), destination); destination.offset += 4; - store32(TrustedImm32(value.tag), destination); + store32(TrustedImm32(value.tag()), destination); #endif } @@ -820,6 +830,8 @@ public: else #if OS(WINDOWS) && CPU(X86_64) loadArgumentOnStack<argumentNumber>(value, argumentNumber); +#elif CPU(MIPS) // Stack space for 4 arguments needs to be allocated for MIPS platforms. + loadArgumentOnStack<argumentNumber>(value, argumentNumber + 4); #else // Sanity: loadArgumentOnStack<argumentNumber - RegisterArgumentCount>(value, argumentNumber); #endif @@ -945,8 +957,8 @@ public: tagAddr.offset += 4; QV4::Primitive v = convertToValue(c); - store32(TrustedImm32(v.int_32), addr); - store32(TrustedImm32(v.tag), tagAddr); + store32(TrustedImm32(v.int_32()), addr); + store32(TrustedImm32(v.tag()), tagAddr); return Pointer(addr); } @@ -961,7 +973,7 @@ public: { store32(reg, addr); addr.offset += 4; - store32(TrustedImm32(QV4::Primitive::fromBoolean(0).tag), addr); + store32(TrustedImm32(QV4::Primitive::fromBoolean(0).tag()), addr); } void storeBool(RegisterID src, RegisterID dest) @@ -1005,7 +1017,7 @@ public: { store32(reg, addr); addr.offset += 4; - store32(TrustedImm32(QV4::Primitive::fromInt32(0).tag), addr); + store32(TrustedImm32(QV4::Primitive::fromInt32(0).tag()), addr); } void storeInt32(RegisterID reg, IR::Expr *target) @@ -1084,7 +1096,7 @@ public: RegisterID toInt32Register(IR::Expr *e, RegisterID scratchReg) { if (IR::Const *c = e->asConst()) { - move(TrustedImm32(convertToValue(c).int_32), scratchReg); + move(TrustedImm32(convertToValue(c).int_32()), scratchReg); return scratchReg; } diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index c6c8023cd7..8b051fcb3d 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -352,7 +352,9 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta } } - if (op == IR::OpSub) { + // Special cases: + switch (op) { + case IR::OpSub: if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister && targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister @@ -368,11 +370,27 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta } as->storeInt32(targetReg, target); return true; + + case IR::OpLShift: + case IR::OpRShift: + case IR::OpURShift: + if (IR::Const *c = rightSource->asConst()) { + if ((QV4::Primitive::toUInt32(c->value) & 0x1f) == 0) { + Assembler::RegisterID r = as->toInt32Register(leftSource, targetReg); + as->storeInt32(r, target); + return true; + } + } + break; + + default: + break; } Assembler::RegisterID l = as->toInt32Register(leftSource, targetReg); if (IR::Const *c = rightSource->asConst()) { // All cases of Y = X op Const Assembler::TrustedImm32 r(int(c->value)); + Assembler::TrustedImm32 ur(QV4::Primitive::toUInt32(c->value) & 0x1f); switch (op) { case IR::OpBitAnd: as->and32(r, l, targetReg); break; @@ -381,9 +399,9 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta case IR::OpAdd: as->add32(r, l, targetReg); break; case IR::OpMul: as->mul32(r, l, targetReg); break; - case IR::OpLShift: r.m_value &= 0x1f; as->lshift32(l, r, targetReg); break; - case IR::OpRShift: r.m_value &= 0x1f; as->rshift32(l, r, targetReg); break; - case IR::OpURShift: r.m_value &= 0x1f; as->urshift32(l, r, targetReg); + case IR::OpLShift: as->lshift32(l, ur, targetReg); break; + case IR::OpRShift: as->rshift32(l, ur, targetReg); break; + case IR::OpURShift: as->urshift32(l, ur, 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; @@ -415,32 +433,33 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta case IR::OpAdd: as->add32(l, r, targetReg); break; case IR::OpMul: as->mul32(l, r, targetReg); break; -#if CPU(ARM) || CPU(X86) || CPU(X86_64) - // The ARM assembler will generate an and with 0x1f for us, and Intel will do it on the CPU. - +#if CPU(X86) || CPU(X86_64) + // Intel does the & 0x1f on the CPU, so: case IR::OpLShift: as->lshift32(l, r, targetReg); break; case IR::OpRShift: as->rshift32(l, r, targetReg); break; case IR::OpURShift: as->urshift32(l, r, 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; #else - case IR::OpLShift: - as->move(r, Assembler::ScratchRegister); - as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister); - as->lshift32(l, Assembler::ScratchRegister, targetReg); - break; - - case IR::OpRShift: - as->move(r, Assembler::ScratchRegister); - as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister); - as->rshift32(l, Assembler::ScratchRegister, targetReg); - break; - + // 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(Assembler::TrustedImm32(0x1f), r, Assembler::ScratchRegister); \ + Assembler::Jump notZero = as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); \ + as->move(l, targetReg); \ + Assembler::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::OpURShift: - as->move(r, Assembler::ScratchRegister); - as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister); - as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! + CHECK_RHS(as->urshift32(l, Assembler::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; +#undef CHECK_RHS #endif case IR::OpSub: // already handled before diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index da511cd1eb..acead2088b 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -317,7 +317,7 @@ const void *InstructionSelection::addConstantTable(QVector<Primitive> *values) QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep() { QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; - result.take(compilationUnit.take()); + result.adopt(compilationUnit.take()); return result; } @@ -574,9 +574,9 @@ void InstructionSelection::loadThisObject(IR::Expr *temp) #endif } -void InstructionSelection::loadQmlIdArray(IR::Expr *temp) +void InstructionSelection::loadQmlContext(IR::Expr *temp) { - generateFunctionCall(temp, Runtime::getQmlIdArray, Assembler::EngineRegister); + generateFunctionCall(temp, Runtime::getQmlContext, Assembler::EngineRegister); } void InstructionSelection::loadQmlImportedScripts(IR::Expr *temp) @@ -584,16 +584,6 @@ void InstructionSelection::loadQmlImportedScripts(IR::Expr *temp) generateFunctionCall(temp, Runtime::getQmlImportedScripts, Assembler::EngineRegister); } -void InstructionSelection::loadQmlContextObject(IR::Expr *temp) -{ - generateFunctionCall(temp, Runtime::getQmlContextObject, Assembler::EngineRegister); -} - -void InstructionSelection::loadQmlScopeObject(IR::Expr *temp) -{ - generateFunctionCall(temp, Runtime::getQmlScopeObject, Assembler::EngineRegister); -} - void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *temp) { generateFunctionCall(temp, Runtime::getQmlSingleton, Assembler::EngineRegister, Assembler::StringToIndex(name)); @@ -614,7 +604,7 @@ void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target) _as->toUInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); } else if (targetTemp->type == IR::BoolType) { Q_ASSERT(sourceConst->type == IR::BoolType); - _as->move(Assembler::TrustedImm32(convertToValue(sourceConst).int_32), + _as->move(Assembler::TrustedImm32(convertToValue(sourceConst).int_32()), (Assembler::RegisterID) targetTemp->index); } else { Q_UNREACHABLE(); @@ -680,6 +670,18 @@ void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR:: } } +void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, IR::Expr *target) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) + generateFunctionCall(target, Runtime::getQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); + else if (kind == IR::Member::MemberOfQmlContextObject) + generateFunctionCall(target, Runtime::getQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); + else if (kind == IR::Member::MemberOfIdObjectsArray) + generateFunctionCall(target, Runtime::getQmlIdObject, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); + else + Q_ASSERT(false); +} + void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) { if (attachedPropertiesId != 0) @@ -708,6 +710,18 @@ void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase, } } +void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) +{ + if (kind == IR::Member::MemberOfQmlScopeObject) + generateFunctionCall(Assembler::Void, Runtime::setQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), + Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + else if (kind == IR::Member::MemberOfQmlContextObject) + generateFunctionCall(Assembler::Void, Runtime::setQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), + Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + else + Q_ASSERT(false); +} + void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) { generateFunctionCall(Assembler::Void, Runtime::setQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), @@ -901,6 +915,24 @@ void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr binop.generate(leftSource, rightSource, target); } +void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) +{ + prepareCallData(args, base); + + if (kind == IR::Member::MemberOfQmlScopeObject) + generateFunctionCall(result, Runtime::callQmlScopeObjectProperty, + Assembler::EngineRegister, + Assembler::TrustedImm32(propertyIndex), + baseAddressForCallData()); + else if (kind == IR::Member::MemberOfQmlContextObject) + generateFunctionCall(result, Runtime::callQmlContextObjectProperty, + Assembler::EngineRegister, + Assembler::TrustedImm32(propertyIndex), + baseAddressForCallData()); + else + Q_ASSERT(false); +} + void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { @@ -1297,11 +1329,11 @@ void InstructionSelection::visitCJump(IR::CJump *s) } else { Address temp = _as->loadAddress(Assembler::ScratchRegister, s->cond); Address tag = temp; - tag.offset += qOffsetOf(QV4::Value, tag); + tag.offset += QV4::Value::tagOffset(); Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type)); Address data = temp; - data.offset += qOffsetOf(QV4::Value, int_32); + data.offset += QV4::Value::valueOffset(); _as->load32(data, Assembler::ReturnValueRegister); Assembler::Jump testBoolean = _as->jump(); @@ -1383,11 +1415,14 @@ void InstructionSelection::visitRet(IR::Ret *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) +#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; @@ -1472,13 +1507,16 @@ void InstructionSelection::visitRet(IR::Ret *s) } 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); + _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); + _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.val), Assembler::ReturnValueRegister); + _as->move(Assembler::TrustedImm64(retVal.rawValue()), Assembler::ReturnValueRegister); #endif } else { Q_UNREACHABLE(); @@ -1499,13 +1537,16 @@ void InstructionSelection::visitRet(IR::Ret *s) _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); + _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); + _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.val), Assembler::ReturnValueRegister); + _as->move(Assembler::TrustedImm64(retVal.rawValue()), Assembler::ReturnValueRegister); #endif _as->jump(leaveStackFrame); } diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 87b4a20bfc..6e842f5fb4 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -91,14 +91,13 @@ protected: virtual void callBuiltinSetupArgumentObject(IR::Expr *result); virtual void callBuiltinConvertThisToObject(); virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result); + virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result); virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result); virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result); virtual void convertType(IR::Expr *source, IR::Expr *target); virtual void loadThisObject(IR::Expr *temp); - virtual void loadQmlIdArray(IR::Expr *target); + virtual void loadQmlContext(IR::Expr *target); virtual void loadQmlImportedScripts(IR::Expr *target); - virtual void loadQmlContextObject(IR::Expr *target); - virtual void loadQmlScopeObject(IR::Expr *target); virtual void loadQmlSingleton(const QString &name, IR::Expr *target); virtual void loadConst(IR::Const *sourceConst, IR::Expr *target); virtual void loadString(const QString &str, IR::Expr *target); @@ -107,8 +106,10 @@ protected: virtual void setActivationProperty(IR::Expr *source, const QString &targetName); virtual void initClosure(IR::Closure *closure, IR::Expr *target); virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target); + virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target); virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target); virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName); + virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex); virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex); virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target); virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex); diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index ae06a99d2a..b9178c0ea0 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -35,7 +35,7 @@ #include <QtCore/QDebug> #include "qv4regalloc_p.h" #include "qv4alloca_p.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <algorithm> #if defined(Q_CC_MINGW) @@ -299,6 +299,16 @@ protected: // IRDecoder addCall(); } + virtual void callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int propertyIndex, IR::ExprList *args, IR::Expr *result) + { + Q_UNUSED(propertyIndex) + + addDef(result); + addUses(base->asTemp(), Use::CouldHaveRegister); + addUses(args, Use::CouldHaveRegister); + addCall(); + } + virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { @@ -421,7 +431,7 @@ protected: // IRDecoder addDef(temp); } - virtual void loadQmlIdArray(IR::Expr *temp) + virtual void loadQmlContext(IR::Expr *temp) { addDef(temp); addCall(); @@ -433,20 +443,6 @@ protected: // IRDecoder addCall(); } - virtual void loadQmlContextObject(Expr *temp) - { - addDef(temp); - addCall(); - } - - virtual void loadQmlScopeObject(Expr *temp) - { - Q_UNUSED(temp); - - addDef(temp); - addCall(); - } - virtual void loadQmlSingleton(const QString &/*name*/, Expr *temp) { Q_UNUSED(temp); @@ -511,6 +507,13 @@ protected: // IRDecoder addCall(); } + virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind /*kind*/, int /*propertyIndex*/) + { + addUses(source->asTemp(), Use::CouldHaveRegister); + addUses(targetBase->asTemp(), Use::CouldHaveRegister); + addCall(); + } + virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int /*propertyIndex*/) { addUses(source->asTemp(), Use::CouldHaveRegister); @@ -518,6 +521,13 @@ protected: // IRDecoder addCall(); } + virtual void getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int /*index*/, IR::Expr *target) + { + addDef(target); + addUses(base->asTemp(), Use::CouldHaveRegister); + addCall(); + } + virtual void getQObjectProperty(IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/, bool /*isSingleton*/, int /*attachedPropertiesId*/, IR::Expr *target) { addDef(target); diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h index 05741f0ae5..76c768e4f9 100644 --- a/src/qml/jit/qv4targetplatform_p.h +++ b/src/qml/jit/qv4targetplatform_p.h @@ -346,6 +346,71 @@ public: static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(JSC::ARMRegisters::lr); } #endif // Linux on ARM (32 bit) +#if defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) + enum { RegAllocIsSupported = 1 }; + + static const JSC::MacroAssembler::RegisterID StackFrameRegister = 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() + { + typedef RegisterInfo RI; + return 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) + << RI(JSC::MIPSRegisters::t6, QStringLiteral("t6"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::t7, QStringLiteral("t7"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::t8, QStringLiteral("t8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::s0, QStringLiteral("s0"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::MIPSRegisters::s1, QStringLiteral("s1"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::MIPSRegisters::s2, QStringLiteral("s2"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::MIPSRegisters::s3, QStringLiteral("s3"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f4, QStringLiteral("f4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f6, QStringLiteral("f6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f8, QStringLiteral("f8"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f10, QStringLiteral("f10"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f18, QStringLiteral("f18"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f20, QStringLiteral("f20"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f22, QStringLiteral("f22"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f24, QStringLiteral("f24"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f26, QStringLiteral("f26"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::MIPSRegisters::f28, QStringLiteral("f28"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + ; + } + +#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 JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + JSC::MIPSRegisters::a0, + JSC::MIPSRegisters::a1, + JSC::MIPSRegisters::a2, + JSC::MIPSRegisters::a3 + }; + + Q_ASSERT(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + + static const int StackAlignment = 8; + 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) { as->push(JSC::MIPSRegisters::ra); } + static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(JSC::MIPSRegisters::ra); } +#endif // Linux on MIPS (32 bit) + public: // utility functions static RegisterInformation getRegisterInfo() { diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index ee27c21aed..d9615d496f 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -77,7 +77,6 @@ Q_DECLARE_METATYPE(QList<int>) \ingroup qtjavascript \inmodule QtQml - \mainclass \section1 Evaluating Scripts @@ -271,7 +270,7 @@ void QJSEngine::installTranslatorFunctions(const QJSValue &object) if (val) obj = val; if (!obj) - obj = scope.engine->globalObject(); + obj = scope.engine->globalObject; #ifndef QT_NO_TRANSLATION obj->defineDefaultProperty(QStringLiteral("qsTranslate"), QV4::GlobalExtensions::method_qsTranslate); obj->defineDefaultProperty(QStringLiteral("QT_TRANSLATE_NOOP"), QV4::GlobalExtensions::method_qsTranslateNoOp); @@ -281,7 +280,7 @@ void QJSEngine::installTranslatorFunctions(const QJSValue &object) obj->defineDefaultProperty(QStringLiteral("QT_TRID_NOOP"), QV4::GlobalExtensions::method_qsTrIdNoOp); // string prototype extension - scope.engine->stringPrototype.asObject()->defineDefaultProperty(QStringLiteral("arg"), + scope.engine->stringPrototype()->defineDefaultProperty(QStringLiteral("arg"), QV4::GlobalExtensions::method_string_arg); #endif } @@ -319,7 +318,7 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in QV4::ExecutionEngine *v4 = d->m_v4Engine; QV4::Scope scope(v4); QV4::ScopedContext ctx(scope, v4->currentContext()); - if (ctx->d() != v4->rootContext()) + if (ctx->d() != v4->rootContext()->d()) ctx = v4->pushGlobalContext(); QV4::ScopedValue result(scope); @@ -331,7 +330,7 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in result = script.run(); if (scope.engine->hasException) result = v4->catchException(); - if (ctx->d() != v4->rootContext()) + if (ctx->d() != v4->rootContext()->d()) v4->popContext(); return QJSValue(v4, result->asReturnedValue()); } @@ -414,7 +413,7 @@ QJSValue QJSEngine::globalObject() const { Q_D(const QJSEngine); QV4::Scope scope(d->m_v4Engine); - QV4::ScopedValue v(scope, d->m_v4Engine->globalObject()); + QV4::ScopedValue v(scope, d->m_v4Engine->globalObject); return QJSValue(d->m_v4Engine, v->asReturnedValue()); } diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 50669c46a8..6d0c7cfdda 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -37,13 +37,14 @@ #include "qjsengine.h" #include "qjsvalue.h" #include "qjsvalue_p.h" -#include "qv4value_inl_p.h" +#include "qv4value_p.h" #include "qv4object_p.h" #include "qv4functionobject_p.h" #include "qv4dateobject_p.h" #include "qv4runtime_p.h" #include "qv4variantobject_p.h" #include "qv4regexpobject_p.h" +#include "qv4errorobject_p.h" #include "private/qv8engine_p.h" #include <private/qv4mm_p.h> #include <private/qv4scopedvalue_p.h> @@ -57,7 +58,6 @@ \ingroup qtjavascript \inmodule QtQml - \mainclass QJSValue supports the types defined in the \l{ECMA-262} standard: The primitive types, which are Undefined, Null, Boolean, @@ -317,8 +317,7 @@ bool QJSValue::isError() const QV4::Value *val = QJSValuePrivate::getValue(this); if (!val) return false; - Object *o = val->asObject(); - return o && o->asErrorObject(); + return val->as<ErrorObject>(); } /*! @@ -332,7 +331,7 @@ bool QJSValue::isArray() const QV4::Value *val = QJSValuePrivate::getValue(this); if (!val) return false; - return val->asArrayObject(); + return val->as<ArrayObject>(); } /*! @@ -349,7 +348,7 @@ bool QJSValue::isObject() const QV4::Value *val = QJSValuePrivate::getValue(this); if (!val) return false; - return val->asObject(); + return val->as<Object>(); } /*! @@ -363,7 +362,7 @@ bool QJSValue::isCallable() const QV4::Value *val = QJSValuePrivate::getValue(this); if (!val) return false; - return val->asFunctionObject(); + return val->as<FunctionObject>(); } /*! @@ -589,7 +588,7 @@ QVariant QJSValue::toVariant() const QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch); Q_ASSERT(val); - if (Object *o = val->asObject()) + if (Object *o = val->as<Object>()) return o->engine()->toVariant(*val, /*typeHint*/ -1, /*createJSValueForObjects*/ false); if (val->isString()) @@ -628,7 +627,7 @@ QJSValue QJSValue::call(const QJSValueList &args) if (!val) return QJSValue(); - FunctionObject *f = val->asFunctionObject(); + FunctionObject *f = val->as<FunctionObject>(); if (!f) return QJSValue(); @@ -637,7 +636,7 @@ QJSValue QJSValue::call(const QJSValueList &args) Scope scope(engine); ScopedCallData callData(scope, args.length()); - callData->thisObject = engine->globalObject()->asReturnedValue(); + callData->thisObject = engine->globalObject; for (int i = 0; i < args.size(); ++i) { if (!QJSValuePrivate::checkEngine(engine, args.at(i))) { qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine"); @@ -679,7 +678,7 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList if (!val) return QJSValue(); - FunctionObject *f = val->asFunctionObject(); + FunctionObject *f = val->as<FunctionObject>(); if (!f) return QJSValue(); @@ -733,7 +732,7 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) if (!val) return QJSValue(); - FunctionObject *f = val->asFunctionObject(); + FunctionObject *f = val->as<FunctionObject>(); if (!f) return QJSValue(); @@ -789,7 +788,7 @@ QJSValue QJSValue::prototype() const if (!engine) return QJSValue(); QV4::Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)->asObject()); + ScopedObject o(scope, QJSValuePrivate::getValue(this)->as<Object>()); if (!o) return QJSValue(); ScopedObject p(scope, o->prototype()); @@ -1029,7 +1028,7 @@ QJSValue QJSValue::property(quint32 arrayIndex) const if (!o) return QJSValue(); - QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax) : o->getIndexed(arrayIndex)); + QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax()) : o->getIndexed(arrayIndex)); if (engine->hasException) engine->catchException(); return QJSValue(engine, result->asReturnedValue()); @@ -1108,7 +1107,7 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) if (arrayIndex != UINT_MAX) o->putIndexed(arrayIndex, v); else - o->put(engine->id_uintMax, v); + o->put(engine->id_uintMax(), v); if (engine->hasException) engine->catchException(); } @@ -1224,7 +1223,7 @@ QDateTime QJSValue::toDateTime() const { QV4::Value *val = QJSValuePrivate::getValue(this); if (val) { - QV4::DateObject *date = val->asDateObject(); + QV4::DateObject *date = val->as<DateObject>(); if (date) return date->toQDateTime(); } @@ -1238,7 +1237,7 @@ QDateTime QJSValue::toDateTime() const bool QJSValue::isDate() const { QV4::Value *val = QJSValuePrivate::getValue(this); - return val && val->asDateObject(); + return val && val->as<DateObject>(); } /*! diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index 93a28a4a5f..08dc184412 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -47,7 +47,7 @@ #include <qjsvalue.h> #include <private/qtqmlglobal_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4string_p.h> #include <private/qv4engine_p.h> #include <private/qv4object_p.h> diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp index 1ee4121f5c..a24953ae3f 100644 --- a/src/qml/jsapi/qjsvalueiterator.cpp +++ b/src/qml/jsapi/qjsvalueiterator.cpp @@ -52,9 +52,6 @@ QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v) QV4::Scope scope(e); QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&v)); iterator.set(e, e->newForEachIteratorObject(o)); - - currentName = (QV4::String *)0; - nextName = (QV4::String *)0; } @@ -102,8 +99,10 @@ QJSValueIterator::QJSValueIterator(const QJSValue& object) QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); it->d()->it.flags = QV4::ObjectIterator::NoFlags; QV4::ScopedString nm(scope); - it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &d_ptr->nextProperty, &d_ptr->nextAttributes); - d_ptr->nextName = nm; + QV4::Property nextProperty; + QV4::PropertyAttributes nextAttributes; + it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes); + d_ptr->nextName.set(v4, nm.asReturnedValue()); } /*! @@ -125,7 +124,7 @@ bool QJSValueIterator::hasNext() const QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value); if (!val || !val->isObject()) return false; - return !!d_ptr->nextName || d_ptr->nextIndex != UINT_MAX; + return d_ptr->nextName.as<QV4::String>() || d_ptr->nextIndex != UINT_MAX; } /*! @@ -143,8 +142,6 @@ bool QJSValueIterator::next() return false; d_ptr->currentName = d_ptr->nextName; d_ptr->currentIndex = d_ptr->nextIndex; - d_ptr->currentProperty.copy(&d_ptr->nextProperty, d_ptr->nextAttributes); - d_ptr->currentAttributes = d_ptr->nextAttributes; QV4::ExecutionEngine *v4 = d_ptr->iterator.engine(); if (!v4) @@ -152,9 +149,11 @@ bool QJSValueIterator::next() QV4::Scope scope(v4); QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); QV4::ScopedString nm(scope); - it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &d_ptr->nextProperty, &d_ptr->nextAttributes); - d_ptr->nextName = nm; - return !!d_ptr->currentName || d_ptr->currentIndex != UINT_MAX; + QV4::Property nextProperty; + QV4::PropertyAttributes nextAttributes; + it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes); + d_ptr->nextName.set(v4, nm.asReturnedValue()); + return d_ptr->currentName.as<QV4::String>() || d_ptr->currentIndex != UINT_MAX; } /*! @@ -168,8 +167,8 @@ QString QJSValueIterator::name() const QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value); if (!val || !val->isObject()) return QString(); - if (!!d_ptr->currentName) - return d_ptr->currentName->toQString(); + if (QV4::String *s = d_ptr->currentName.as<QV4::String>()) + return s->toQString(); if (d_ptr->currentIndex < UINT_MAX) return QString::number(d_ptr->currentIndex); return QString(); @@ -192,10 +191,10 @@ QJSValue QJSValueIterator::value() const if (!obj) return QJSValue(); - if (!d_ptr->currentName && d_ptr->currentIndex == UINT_MAX) + if (!d_ptr->currentName.as<QV4::String>() && d_ptr->currentIndex == UINT_MAX) return QJSValue(); - QV4::ScopedValue v(scope, obj->getValue(*obj, &d_ptr->currentProperty, d_ptr->currentAttributes)); + QV4::ScopedValue v(scope, d_ptr->currentIndex == UINT_MAX ? obj->get(d_ptr->currentName.as<QV4::String>()) : obj->getIndexed(d_ptr->currentIndex)); if (scope.hasException()) { engine->catchException(); return QJSValue(); @@ -214,8 +213,8 @@ QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) d_ptr->value = object; d_ptr->currentIndex = UINT_MAX; d_ptr->nextIndex = UINT_MAX; - d_ptr->currentName = (QV4::String *)0; - d_ptr->nextName = (QV4::String *)0; + d_ptr->currentName.clear(); + d_ptr->nextName.clear(); QV4::ExecutionEngine *v4 = d_ptr->iterator.engine(); if (!v4) { d_ptr->iterator.clear(); @@ -228,8 +227,10 @@ QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value()); it->d()->it.flags = QV4::ObjectIterator::NoFlags; QV4::ScopedString nm(scope); - it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &d_ptr->nextProperty, &d_ptr->nextAttributes); - d_ptr->nextName = nm; + QV4::Property nextProperty; + QV4::PropertyAttributes nextAttributes; + it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes); + d_ptr->nextName.set(v4, nm.asReturnedValue()); return *this; } diff --git a/src/qml/jsapi/qjsvalueiterator_p.h b/src/qml/jsapi/qjsvalueiterator_p.h index c17fedf73e..b0d0b2efda 100644 --- a/src/qml/jsapi/qjsvalueiterator_p.h +++ b/src/qml/jsapi/qjsvalueiterator_p.h @@ -48,15 +48,9 @@ public: QJSValue value; QV4::PersistentValue iterator; - // ### GC - QV4::Property currentProperty; - QV4::PropertyAttributes currentAttributes; - QV4::StringValue currentName; + QV4::PersistentValue currentName; uint currentIndex; - // ### GC - QV4::Property nextProperty; - QV4::PropertyAttributes nextAttributes; - QV4::StringValue nextName; + QV4::PersistentValue nextName; uint nextIndex; }; diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index ef44ca6f4d..5ffdebe328 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -10,7 +10,6 @@ SOURCES += \ $$PWD/qv4lookup.cpp \ $$PWD/qv4identifier.cpp \ $$PWD/qv4identifiertable.cpp \ - $$PWD/qv4mm.cpp \ $$PWD/qv4managed.cpp \ $$PWD/qv4internalclass.cpp \ $$PWD/qv4sparsearray.cpp \ @@ -40,7 +39,6 @@ SOURCES += \ $$PWD/qv4sequenceobject.cpp \ $$PWD/qv4include.cpp \ $$PWD/qv4qobjectwrapper.cpp \ - $$PWD/qv4qmlextensions.cpp \ $$PWD/qv4vme_moth.cpp \ $$PWD/qv4profiling.cpp \ $$PWD/qv4arraybuffer.cpp \ @@ -57,7 +55,6 @@ HEADERS += \ $$PWD/qv4lookup_p.h \ $$PWD/qv4identifier_p.h \ $$PWD/qv4identifiertable_p.h \ - $$PWD/qv4mm_p.h \ $$PWD/qv4managed_p.h \ $$PWD/qv4internalclass_p.h \ $$PWD/qv4sparsearray_p.h \ @@ -90,7 +87,6 @@ HEADERS += \ $$PWD/qv4sequenceobject_p.h \ $$PWD/qv4include_p.h \ $$PWD/qv4qobjectwrapper_p.h \ - $$PWD/qv4qmlextensions_p.h \ $$PWD/qv4vme_moth_p.h \ $$PWD/qv4profiling_p.h \ $$PWD/qv4arraybuffer_p.h \ @@ -102,7 +98,7 @@ HEADERS += \ HEADERS += \ $$PWD/qv4runtime_p.h \ - $$PWD/qv4value_inl_p.h \ + $$PWD/qv4value_p.h \ $$PWD/qv4string_p.h \ $$PWD/qv4value_p.h diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 92c77570af..39dce8efe0 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -33,6 +33,7 @@ #include <qv4argumentsobject_p.h> #include <qv4alloca_p.h> #include <qv4scopedvalue_p.h> +#include "qv4string_p.h" using namespace QV4; @@ -40,11 +41,11 @@ DEFINE_OBJECT_VTABLE(ArgumentsObject); Heap::ArgumentsObject::ArgumentsObject(QV4::CallContext *context) : Heap::Object(context->d()->strictMode ? context->d()->engine->strictArgumentsObjectClass : context->d()->engine->argumentsObjectClass, - context->d()->engine->objectPrototype.asObject()) + context->d()->engine->objectPrototype()) , context(context->d()) , fullyCreated(false) { - Q_ASSERT(vtable == QV4::ArgumentsObject::staticVTable()); + Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable()); ExecutionEngine *v4 = context->d()->engine; Scope scope(v4); @@ -53,21 +54,21 @@ Heap::ArgumentsObject::ArgumentsObject(QV4::CallContext *context) args->setArrayType(Heap::ArrayData::Complex); if (context->d()->strictMode) { - Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee)); - Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(context->d()->engine->id_caller)); - args->propertyAt(CalleePropertyIndex)->value = v4->thrower; - args->propertyAt(CalleePropertyIndex)->set = v4->thrower; - args->propertyAt(CallerPropertyIndex)->value = v4->thrower; - args->propertyAt(CallerPropertyIndex)->set = v4->thrower; + Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee())); + Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(context->d()->engine->id_caller())); + args->propertyAt(CalleePropertyIndex)->value = v4->thrower(); + args->propertyAt(CalleePropertyIndex)->set = v4->thrower(); + args->propertyAt(CallerPropertyIndex)->value = v4->thrower(); + args->propertyAt(CallerPropertyIndex)->set = v4->thrower(); args->arrayReserve(context->argc()); args->arrayPut(0, context->args(), context->argc()); args->d()->fullyCreated = true; } else { - Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee)); + Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee())); args->memberData()->data[CalleePropertyIndex] = context->d()->function->asReturnedValue(); } - Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(context->d()->engine->id_length)); + Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(context->d()->engine->id_length())); args->memberData()->data[LengthPropertyIndex] = Primitive::fromInt32(context->d()->callData->argc); } @@ -144,9 +145,9 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con return result; } -ReturnedValue ArgumentsObject::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *hasProperty) { - ArgumentsObject *args = static_cast<ArgumentsObject *>(m); + const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); if (args->fullyCreated()) return Object::getIndexed(m, index, hasProperty); @@ -199,11 +200,11 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction); -ReturnedValue ArgumentsGetterFunction::call(Managed *getter, CallData *callData) +ReturnedValue ArgumentsGetterFunction::call(const Managed *getter, CallData *callData) { - ExecutionEngine *v4 = static_cast<ArgumentsGetterFunction *>(getter)->engine(); + ExecutionEngine *v4 = static_cast<const ArgumentsGetterFunction *>(getter)->engine(); Scope scope(v4); - Scoped<ArgumentsGetterFunction> g(scope, static_cast<ArgumentsGetterFunction *>(getter)); + Scoped<ArgumentsGetterFunction> g(scope, static_cast<const ArgumentsGetterFunction *>(getter)); Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); if (!o) return v4->throwTypeError(); @@ -214,11 +215,11 @@ ReturnedValue ArgumentsGetterFunction::call(Managed *getter, CallData *callData) DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction); -ReturnedValue ArgumentsSetterFunction::call(Managed *setter, CallData *callData) +ReturnedValue ArgumentsSetterFunction::call(const Managed *setter, CallData *callData) { - ExecutionEngine *v4 = static_cast<ArgumentsSetterFunction *>(setter)->engine(); + ExecutionEngine *v4 = static_cast<const ArgumentsSetterFunction *>(setter)->engine(); Scope scope(v4); - Scoped<ArgumentsSetterFunction> s(scope, static_cast<ArgumentsSetterFunction *>(setter)); + Scoped<ArgumentsSetterFunction> s(scope, static_cast<const ArgumentsSetterFunction *>(setter)); Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); if (!o) return v4->throwTypeError(); diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 43cd6d1dee..aab5e2c156 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -59,9 +59,9 @@ struct ArgumentsObject : Object { CallerPropertyIndex = 3 }; ArgumentsObject(QV4::CallContext *context); - CallContext *context; + Pointer<CallContext> context; bool fullyCreated; - MemberData *mappedArguments; + Pointer<MemberData> mappedArguments; }; } @@ -71,7 +71,7 @@ struct ArgumentsGetterFunction: FunctionObject V4_OBJECT2(ArgumentsGetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(Managed *that, CallData *d); + static ReturnedValue call(const Managed *that, CallData *d); }; inline @@ -86,7 +86,7 @@ struct ArgumentsSetterFunction: FunctionObject V4_OBJECT2(ArgumentsSetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; inline @@ -106,12 +106,12 @@ struct ArgumentsObject: Object { Heap::MemberData *mappedArguments() { return d()->mappedArguments; } static bool isNonStrictArgumentsObject(Managed *m) { - return m->d()->vtable->type == Type_ArgumentsObject && + return m->d()->vtable()->type == Type_ArgumentsObject && !static_cast<ArgumentsObject *>(m)->context()->strictMode; } bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static void putIndexed(Managed *m, uint index, const Value &value); static bool deleteIndexedProperty(Managed *m, uint index); static PropertyAttributes queryIndexed(const Managed *m, uint index); diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index dc65b5d21a..23c9695cf4 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -33,6 +33,7 @@ #include "qv4arraybuffer_p.h" #include "qv4typedarray_p.h" #include "qv4dataview_p.h" +#include "qv4string_p.h" using namespace QV4; @@ -44,9 +45,9 @@ Heap::ArrayBufferCtor::ArrayBufferCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ArrayBufferCtor::construct(Managed *m, CallData *callData) +ReturnedValue ArrayBufferCtor::construct(const Managed *m, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); Scope scope(v4); ScopedValue l(scope, callData->argument(0)); @@ -64,7 +65,7 @@ ReturnedValue ArrayBufferCtor::construct(Managed *m, CallData *callData) } -ReturnedValue ArrayBufferCtor::call(Managed *that, CallData *callData) +ReturnedValue ArrayBufferCtor::call(const Managed *that, CallData *callData) { return construct(that, callData); } @@ -83,7 +84,7 @@ ReturnedValue ArrayBufferCtor::method_isView(CallContext *ctx) Heap::ArrayBuffer::ArrayBuffer(ExecutionEngine *e, size_t length) - : Heap::Object(e->emptyClass, e->arrayBufferPrototype.asObject()) + : Heap::Object(e->emptyClass, e->arrayBufferPrototype()) { data = QTypedArrayData<char>::allocate(length + 1); if (!data) { @@ -96,7 +97,7 @@ Heap::ArrayBuffer::ArrayBuffer(ExecutionEngine *e, size_t length) } Heap::ArrayBuffer::ArrayBuffer(ExecutionEngine *e, const QByteArray& array) - : Heap::Object(e->emptyClass, e->arrayBufferPrototype.asObject()) + : Heap::Object(e->emptyClass, e->arrayBufferPrototype()) , data(const_cast<QByteArray&>(array).data_ptr()) { data->ref.ref(); @@ -138,10 +139,10 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1); - defineDefaultProperty(engine->id_constructor, (o = ctor)); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, 0); defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); } @@ -172,7 +173,7 @@ ReturnedValue ArrayBufferPrototype::method_slice(CallContext *ctx) double first = (start < 0) ? qMax(a->d()->data->size + start, 0.) : qMin(start, (double)a->d()->data->size); double final = (end < 0) ? qMax(a->d()->data->size + end, 0.) : qMin(end, (double)a->d()->data->size); - ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor)); + ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); if (!constructor) return scope.engine->throwTypeError(); diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index fe3150618d..a7f9e92c80 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -61,8 +61,8 @@ struct ArrayBufferCtor: FunctionObject { V4_OBJECT2(ArrayBufferCtor, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); static ReturnedValue method_isView(CallContext *ctx); diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index afcfa00905..da91db6aae 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -33,13 +33,14 @@ #include "qv4arraydata_p.h" #include "qv4object_p.h" #include "qv4functionobject_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4runtime_p.h" #include "qv4argumentsobject_p.h" +#include "qv4string_p.h" using namespace QV4; -const QV4::ManagedVTable QV4::ArrayData::static_vtbl = { +const QV4::VTable QV4::ArrayData::static_vtbl = { 0, QV4::ArrayData::IsExecutionContext, QV4::ArrayData::IsString, @@ -90,6 +91,13 @@ const ArrayVTable SparseArrayData::static_vtbl = Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SimpleArrayData)); Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SparseArrayData)); +static Q_ALWAYS_INLINE void storeValue(ReturnedValue *target, uint value) +{ + Value v = Value::fromReturnedValue(*target); + v.setValue(value); + *target = v.asReturnedValue(); +} + void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAttributes) { Scope scope(o->engine()); @@ -165,7 +173,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt Heap::SparseArrayData *sparse = static_cast<Heap::SparseArrayData *>(newData->d()); - uint *lastFree; + ReturnedValue *lastFree; if (d && d->type() == Heap::ArrayData::Sparse) { Heap::SparseArrayData *old = static_cast<Heap::SparseArrayData *>(d->d()); sparse->sparse = old->sparse; @@ -180,20 +188,20 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt SparseArrayNode *n = sparse->sparse->insert(i); n->value = i; } else { - *lastFree = i; - sparse->arrayData[i].tag = Value::Empty_Type; - lastFree = &sparse->arrayData[i].uint_32; + storeValue(lastFree, i); + sparse->arrayData[i].setTag(Value::Empty_Type); + lastFree = &sparse->arrayData[i].rawValueRef(); } } } if (toCopy < sparse->alloc) { for (uint i = toCopy; i < sparse->alloc; ++i) { - *lastFree = i; - sparse->arrayData[i].tag = Value::Empty_Type; - lastFree = &sparse->arrayData[i].uint_32; + storeValue(lastFree, i); + sparse->arrayData[i].setTag(Value::Empty_Type); + lastFree = &sparse->arrayData[i].rawValueRef(); } - *lastFree = UINT_MAX; + storeValue(lastFree, UINT_MAX); } // ### Could explicitly free the old data } @@ -230,7 +238,7 @@ ReturnedValue SimpleArrayData::get(const Heap::ArrayData *d, uint index) bool SimpleArrayData::put(Object *o, uint index, const Value &value) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Q_ASSERT(index >= dd->len || !dd->attrs || !dd->attrs[index].isAccessor()); // ### honour attributes dd->data(index) = value; @@ -244,7 +252,7 @@ bool SimpleArrayData::put(Object *o, uint index, const Value &value) bool SimpleArrayData::del(Object *o, uint index) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (index >= dd->len) return true; @@ -266,12 +274,12 @@ void SimpleArrayData::setAttribute(Object *o, uint index, PropertyAttributes att void SimpleArrayData::push_front(Object *o, const Value *values, uint n) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Q_ASSERT(!dd->attrs); if (dd->len + n > dd->alloc) { realloc(o, Heap::ArrayData::Simple, dd->len + n, false); Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Simple); - dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } dd->offset = (dd->offset - n) % dd->alloc; dd->len += n; @@ -281,7 +289,7 @@ void SimpleArrayData::push_front(Object *o, const Value *values, uint n) ReturnedValue SimpleArrayData::pop_front(Object *o) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Q_ASSERT(!dd->attrs); if (!dd->len) return Encode::undefined(); @@ -294,7 +302,7 @@ ReturnedValue SimpleArrayData::pop_front(Object *o) uint SimpleArrayData::truncate(Object *o, uint newLen) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (dd->len < newLen) return newLen; @@ -318,10 +326,10 @@ uint SimpleArrayData::length(const Heap::ArrayData *d) bool SimpleArrayData::putArray(Object *o, uint index, const Value *values, uint n) { - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (index + n > dd->alloc) { reallocate(o, index + n + 1, false); - dd = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } for (uint i = dd->len; i < index; ++i) dd->data(i) = Primitive::emptyValue(); @@ -337,13 +345,10 @@ void SparseArrayData::free(Heap::ArrayData *d, uint idx) Value *v = d->arrayData + idx; if (d->attrs && d->attrs[idx].isAccessor()) { // double slot, free both. Order is important, so we have a double slot for allocation again afterwards. - v[1].tag = Value::Empty_Type; - v[1].uint_32 = d->freeList; - v[0].tag = Value::Empty_Type; - v[0].uint_32 = idx + 1; + v[1].setTagValue(Value::Empty_Type, Value::fromReturnedValue(d->freeList).value()); + v[0].setTagValue(Value::Empty_Type, idx + 1); } else { - v->tag = Value::Empty_Type; - v->uint_32 = d->freeList; + v->setTagValue(Value::Empty_Type, Value::fromReturnedValue(d->freeList).value()); } d->freeList = idx; if (d->attrs) @@ -369,35 +374,37 @@ Heap::ArrayData *SparseArrayData::reallocate(Object *o, uint n, bool enforceAttr uint SparseArrayData::allocate(Object *o, bool doubleSlot) { Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Sparse); - Heap::SparseArrayData *dd = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (doubleSlot) { - uint *last = &dd->freeList; + ReturnedValue *last = &dd->freeList; while (1) { - if (*last == UINT_MAX) { + if (Value::fromReturnedValue(*last).value() == UINT_MAX) { reallocate(o, dd->alloc + 2, true); - dd = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); last = &dd->freeList; - Q_ASSERT(*last != UINT_MAX); + Q_ASSERT(Value::fromReturnedValue(*last).value() != UINT_MAX); } - Q_ASSERT(dd->arrayData[*last].uint_32 != *last); - if (dd->arrayData[*last].uint_32 == (*last + 1)) { + Q_ASSERT(dd->arrayData[Value::fromReturnedValue(*last).value()].value() != Value::fromReturnedValue(*last).value()); + if (dd->arrayData[Value::fromReturnedValue(*last).value()].value() == (Value::fromReturnedValue(*last).value() + 1)) { // found two slots in a row - uint idx = *last; - *last = dd->arrayData[*last + 1].uint_32; + uint idx = Value::fromReturnedValue(*last).uint_32(); + Value lastV = Value::fromReturnedValue(*last); + lastV.setValue(dd->arrayData[lastV.value() + 1].value()); + *last = lastV.rawValue(); dd->attrs[idx] = Attr_Accessor; return idx; } - last = &dd->arrayData[*last].uint_32; + last = &dd->arrayData[Value::fromReturnedValue(*last).value()].rawValueRef(); } } else { - if (dd->freeList == UINT_MAX) { + if (Value::fromReturnedValue(dd->freeList).value() == UINT_MAX) { reallocate(o, dd->alloc + 1, false); - dd = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } - uint idx = dd->freeList; + uint idx = Value::fromReturnedValue(dd->freeList).value(); Q_ASSERT(idx != UINT_MAX); - dd->freeList = dd->arrayData[idx].uint_32; + dd->freeList = dd->arrayData[idx].uint_32(); if (dd->attrs) dd->attrs[idx] = Attr_Data; return idx; @@ -418,12 +425,12 @@ bool SparseArrayData::put(Object *o, uint index, const Value &value) if (value.isEmpty()) return true; - Heap::SparseArrayData *s = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *s = o->d()->arrayData.cast<Heap::SparseArrayData>(); SparseArrayNode *n = s->sparse->insert(index); Q_ASSERT(n->value == UINT_MAX || !s->attrs || !s->attrs[n->value].isAccessor()); if (n->value == UINT_MAX) n->value = allocate(o); - s = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + s = o->d()->arrayData.cast<Heap::SparseArrayData>(); s->arrayData[n->value] = value; if (s->attrs) s->attrs[n->value] = Attr_Data; @@ -432,7 +439,7 @@ bool SparseArrayData::put(Object *o, uint index, const Value &value) bool SparseArrayData::del(Object *o, uint index) { - Heap::SparseArrayData *dd = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *dd = o->d()->arrayData.cast<Heap::SparseArrayData>(); SparseArrayNode *n = dd->sparse->findNode(index); if (!n) @@ -452,13 +459,10 @@ bool SparseArrayData::del(Object *o, uint index) if (isAccessor) { // free up both indices - dd->arrayData[pidx + 1].tag = Value::Empty_Type; - dd->arrayData[pidx + 1].uint_32 = dd->freeList; - dd->arrayData[pidx].tag = Value::Undefined_Type; - dd->arrayData[pidx].uint_32 = pidx + 1; + dd->arrayData[pidx + 1].setTagValue(Value::Empty_Type, Value::fromReturnedValue(dd->freeList).value()); + dd->arrayData[pidx].setTagValue(Value::Undefined_Type, pidx + 1); } else { - dd->arrayData[pidx].tag = Value::Empty_Type; - dd->arrayData[pidx].uint_32 = dd->freeList; + dd->arrayData[pidx].setTagValue(Value::Empty_Type, Value::fromReturnedValue(dd->freeList).value()); } dd->freeList = pidx; @@ -468,28 +472,28 @@ bool SparseArrayData::del(Object *o, uint index) void SparseArrayData::setAttribute(Object *o, uint index, PropertyAttributes attrs) { - Heap::SparseArrayData *d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *d = o->d()->arrayData.cast<Heap::SparseArrayData>(); SparseArrayNode *n = d->sparse->insert(index); if (n->value == UINT_MAX) { n->value = allocate(o, attrs.isAccessor()); - d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + d = o->d()->arrayData.cast<Heap::SparseArrayData>(); } else if (attrs.isAccessor() != d->attrs[n->value].isAccessor()) { // need to convert the slot free(o->arrayData(), n->value); n->value = allocate(o, attrs.isAccessor()); - d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + d = o->d()->arrayData.cast<Heap::SparseArrayData>(); } d->attrs[n->value] = attrs; } void SparseArrayData::push_front(Object *o, const Value *values, uint n) { - Heap::SparseArrayData *d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *d = o->d()->arrayData.cast<Heap::SparseArrayData>(); Q_ASSERT(!d->attrs); for (int i = n - 1; i >= 0; --i) { uint idx = allocate(o); - d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + d = o->d()->arrayData.cast<Heap::SparseArrayData>(); d->arrayData[idx] = values[i]; d->sparse->push_front(idx); } @@ -497,7 +501,7 @@ void SparseArrayData::push_front(Object *o, const Value *values, uint n) ReturnedValue SparseArrayData::pop_front(Object *o) { - Heap::SparseArrayData *d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *d = o->d()->arrayData.cast<Heap::SparseArrayData>(); Q_ASSERT(!d->attrs); uint idx = d->sparse->pop_front(); ReturnedValue v; @@ -512,7 +516,7 @@ ReturnedValue SparseArrayData::pop_front(Object *o) uint SparseArrayData::truncate(Object *o, uint newLen) { - Heap::SparseArrayData *d = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *d = o->d()->arrayData.cast<Heap::SparseArrayData>(); SparseArrayNode *begin = d->sparse->lowerBound(newLen); if (begin != d->sparse->end()) { SparseArrayNode *it = d->sparse->end()->previousNode(); @@ -606,11 +610,11 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n) Property *ArrayData::insert(Object *o, uint index, bool isAccessor) { if (!isAccessor && o->d()->arrayData->type != Heap::ArrayData::Sparse) { - Heap::SimpleArrayData *d = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *d = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (index < 0x1000 || index < d->len + (d->len >> 2)) { if (index >= d->alloc) { o->arrayReserve(index + 1); - d = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + d = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } if (index >= d->len) { // mark possible hole in the array @@ -623,11 +627,11 @@ Property *ArrayData::insert(Object *o, uint index, bool isAccessor) } o->initSparseArray(); - Heap::SparseArrayData *s = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *s = o->d()->arrayData.cast<Heap::SparseArrayData>(); SparseArrayNode *n = s->sparse->insert(index); if (n->value == UINT_MAX) n->value = SparseArrayData::allocate(o, isAccessor); - s = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + s = o->d()->arrayData.cast<Heap::SparseArrayData>(); return reinterpret_cast<Property *>(s->arrayData + n->value); } @@ -737,7 +741,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c if (!arrayData || !arrayData->length()) return; - if (!(comparefn.isUndefined() || comparefn.asObject())) { + if (!(comparefn.isUndefined() || comparefn.as<Object>())) { engine->throwTypeError(); return; } @@ -755,7 +759,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c thisObject->setArrayData(0); ArrayData::realloc(thisObject, Heap::ArrayData::Simple, sparse->sparse()->nEntries(), sparse->attrs() ? true : false); - Heap::SimpleArrayData *d = static_cast<Heap::SimpleArrayData *>(thisObject->d()->arrayData); + Heap::SimpleArrayData *d = thisObject->d()->arrayData.cast<Heap::SimpleArrayData>(); SparseArrayNode *n = sparse->sparse()->begin(); uint i = 0; @@ -795,7 +799,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c } } else { - Heap::SimpleArrayData *d = static_cast<Heap::SimpleArrayData *>(thisObject->d()->arrayData); + Heap::SimpleArrayData *d = thisObject->d()->arrayData.cast<Heap::SimpleArrayData>(); if (len > d->len) len = d->len; diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 915e862bbb..729e657b1a 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -47,17 +47,17 @@ namespace QV4 { Q_MANAGED_CHECK \ typedef QV4::Heap::DataClass Data; \ static const QV4::ArrayVTable static_vtbl; \ - static inline const QV4::ManagedVTable *staticVTable() { return &static_vtbl.managedVTable; } \ + static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \ V4_MANAGED_SIZE_TEST \ - const Data *d() const { return static_cast<const Data *>(m); } \ - Data *d() { return static_cast<Data *>(m); } + const Data *d() const { return static_cast<const Data *>(m()); } \ + Data *d() { return static_cast<Data *>(m()); } struct ArrayData; struct ArrayVTable { - ManagedVTable managedVTable; + VTable vTable; uint type; Heap::ArrayData *(*reallocate)(Object *o, uint n, bool enforceAttributes); ReturnedValue (*get)(const Heap::ArrayData *d, uint index); @@ -86,7 +86,7 @@ struct ArrayData : public Base { PropertyAttributes *attrs; union { uint len; - uint freeList; + ReturnedValue freeList; }; union { uint offset; @@ -96,7 +96,7 @@ struct ArrayData : public Base { bool isSparse() const { return type == Sparse; } - const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable); } + const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable()); } inline ReturnedValue get(uint i) const { return vtable()->get(this, i); @@ -239,8 +239,8 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData V4_ARRAYDATA(SparseArrayData) V4_NEEDS_DESTROY - uint &freeList() { return d()->freeList; } - uint freeList() const { return d()->freeList; } + ReturnedValue &freeList() { return d()->freeList; } + ReturnedValue freeList() const { return d()->freeList; } SparseArray *sparse() const { return d()->sparse; } void setSparse(SparseArray *s) { d()->sparse = s; } diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 231eb93dd5..25d3d9329b 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -37,6 +37,7 @@ #include "qv4scopedvalue_p.h" #include "qv4argumentsobject_p.h" #include "qv4runtime_p.h" +#include "qv4string_p.h" using namespace QV4; @@ -47,9 +48,9 @@ Heap::ArrayCtor::ArrayCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ArrayCtor::construct(Managed *m, CallData *callData) +ReturnedValue ArrayCtor::construct(const Managed *m, CallData *callData) { - ExecutionEngine *v4 = static_cast<ArrayCtor *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const ArrayCtor *>(m)->engine(); Scope scope(v4); ScopedArrayObject a(scope, v4->newArrayObject()); uint len; @@ -72,7 +73,7 @@ ReturnedValue ArrayCtor::construct(Managed *m, CallData *callData) return a.asReturnedValue(); } -ReturnedValue ArrayCtor::call(Managed *that, CallData *callData) +ReturnedValue ArrayCtor::call(const Managed *that, CallData *callData) { return construct(that, callData); } @@ -81,11 +82,11 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineDefaultProperty(QStringLiteral("isArray"), method_isArray, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString, 0); + defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); defineDefaultProperty(QStringLiteral("concat"), method_concat, 1); defineDefaultProperty(QStringLiteral("join"), method_join, 1); @@ -110,7 +111,7 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) ReturnedValue ArrayPrototype::method_isArray(CallContext *ctx) { - bool isArray = ctx->argc() && ctx->args()[0].asArrayObject(); + bool isArray = ctx->argc() && ctx->args()[0].as<ArrayObject>(); return Encode(isArray); } @@ -185,7 +186,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) r4 = arg->toQString(); ScopedObject self(scope, ctx->thisObject()); - ScopedValue length(scope, self->get(ctx->d()->engine->id_length)); + ScopedValue length(scope, self->get(ctx->d()->engine->id_length())); const quint32 r2 = length->isUndefined() ? 0 : length->toUInt32(); if (!r2) @@ -194,7 +195,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx) QString R; // ### FIXME - if (ArrayObject *a = self->asArrayObject()) { + if (ArrayObject *a = self->as<ArrayObject>()) { ScopedValue e(scope); for (uint i = 0; i < a->getLength(); ++i) { if (i) @@ -242,7 +243,7 @@ ReturnedValue ArrayPrototype::method_pop(CallContext *ctx) if (!len) { if (!instance->isArrayObject()) - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromInt32(0))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))); return Encode::undefined(); } @@ -256,7 +257,7 @@ ReturnedValue ArrayPrototype::method_pop(CallContext *ctx) if (instance->isArrayObject()) instance->setArrayLength(len - 1); else - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(len - 1))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); return result->asReturnedValue(); } @@ -282,7 +283,7 @@ ReturnedValue ArrayPrototype::method_push(CallContext *ctx) } double newLen = l + ctx->argc(); if (!instance->isArrayObject()) - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(newLen))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); else { ScopedString str(scope, ctx->d()->engine->newString(QStringLiteral("Array.prototype.push: Overflow"))); return ctx->engine()->throwRangeError(str); @@ -303,7 +304,7 @@ ReturnedValue ArrayPrototype::method_push(CallContext *ctx) if (instance->isArrayObject()) instance->setArrayLengthUnchecked(len); else - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(len))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len))); return Encode(len); } @@ -354,7 +355,7 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) if (!len) { if (!instance->isArrayObject()) - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromInt32(0))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))); return Encode::undefined(); } @@ -388,7 +389,7 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx) if (instance->isArrayObject()) instance->setArrayLengthUnchecked(len - 1); else - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(len - 1))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); return result->asReturnedValue(); } @@ -523,7 +524,7 @@ ReturnedValue ArrayPrototype::method_splice(CallContext *ctx) } ctx->d()->strictMode = true; - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))); return newArray.asReturnedValue(); } @@ -561,7 +562,7 @@ ReturnedValue ArrayPrototype::method_unshift(CallContext *ctx) if (instance->isArrayObject()) instance->setArrayLengthUnchecked(newLen); else - instance->put(ctx->d()->engine->id_length, ScopedValue(scope, Primitive::fromDouble(newLen))); + instance->put(ctx->d()->engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); return Encode(newLen); } @@ -619,7 +620,7 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx) return Encode(-1); } else { Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple || instance->arrayType() == Heap::ArrayData::Complex); - Heap::SimpleArrayData *sa = static_cast<Heap::SimpleArrayData *>(instance->d()->arrayData); + Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>(); if (len > sa->len) len = sa->len; uint idx = fromIndex; diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index 4e67eb2e31..422a0de675 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -53,8 +53,8 @@ struct ArrayCtor: FunctionObject { V4_OBJECT2(ArrayCtor, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct ArrayPrototype: ArrayObject diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index 9c293e783b..53f8abf3f2 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -32,6 +32,7 @@ ****************************************************************************/ #include "qv4booleanobject_p.h" +#include "qv4string_p.h" using namespace QV4; @@ -43,14 +44,14 @@ Heap::BooleanCtor::BooleanCtor(QV4::ExecutionContext *scope) { } -ReturnedValue BooleanCtor::construct(Managed *m, CallData *callData) +ReturnedValue BooleanCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<BooleanCtor *>(m)->engine()); + Scope scope(static_cast<const BooleanCtor *>(m)->engine()); bool n = callData->argc ? callData->args[0].toBoolean() : false; return Encode(scope.engine->newBooleanObject(n)); } -ReturnedValue BooleanCtor::call(Managed *, CallData *callData) +ReturnedValue BooleanCtor::call(const Managed *, CallData *callData) { bool value = callData->argc ? callData->args[0].toBoolean() : 0; return Encode(value); @@ -60,11 +61,11 @@ void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString); - defineDefaultProperty(engine->id_valueOf, method_valueOf); + defineDefaultProperty(engine->id_toString(), method_toString); + defineDefaultProperty(engine->id_valueOf(), method_valueOf); } ReturnedValue BooleanPrototype::method_toString(CallContext *ctx) @@ -73,7 +74,7 @@ ReturnedValue BooleanPrototype::method_toString(CallContext *ctx) if (ctx->thisObject().isBoolean()) { result = ctx->thisObject().booleanValue(); } else { - BooleanObject *thisObject = ctx->thisObject().as<BooleanObject>(); + const BooleanObject *thisObject = ctx->thisObject().as<BooleanObject>(); if (!thisObject) return ctx->engine()->throwTypeError(); result = thisObject->value(); @@ -87,7 +88,7 @@ ReturnedValue BooleanPrototype::method_valueOf(CallContext *ctx) if (ctx->thisObject().isBoolean()) return ctx->thisObject().asReturnedValue(); - BooleanObject *thisObject = ctx->thisObject().as<BooleanObject>(); + const BooleanObject *thisObject = ctx->thisObject().as<BooleanObject>(); if (!thisObject) return ctx->engine()->throwTypeError(); diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index 903261bdce..77b5a74fde 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -53,8 +53,8 @@ struct BooleanCtor: FunctionObject { V4_OBJECT2(BooleanCtor, FunctionObject) - static ReturnedValue construct(Managed *, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct BooleanPrototype: BooleanObject diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 9330f10780..f50c5ab017 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -36,10 +36,12 @@ #include <qv4context_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include <qv4argumentsobject_p.h> #include "qv4function_p.h" #include "qv4errorobject_p.h" +#include "qv4string_p.h" +#include "private/qqmlcontextwrapper_p.h" using namespace QV4; @@ -48,8 +50,9 @@ DEFINE_MANAGED_VTABLE(CallContext); DEFINE_MANAGED_VTABLE(WithContext); DEFINE_MANAGED_VTABLE(CatchContext); DEFINE_MANAGED_VTABLE(GlobalContext); +DEFINE_MANAGED_VTABLE(QmlContext); -Heap::CallContext *ExecutionContext::newCallContext(FunctionObject *function, CallData *callData) +Heap::CallContext *ExecutionContext::newCallContext(const FunctionObject *function, CallData *callData) { Q_ASSERT(function->function()); @@ -90,31 +93,37 @@ Heap::CatchContext *ExecutionContext::newCatchContext(String *exceptionVarName, return d()->engine->memoryManager->alloc<CatchContext>(d()->engine, exceptionVarName, exceptionValue); } -Heap::CallContext *ExecutionContext::newQmlContext(FunctionObject *f, Object *qml) +Heap::QmlContext *ExecutionContext::newQmlContext(QmlContextWrapper *qml) { - Scope scope(this); - Scoped<CallContext> c(scope, d()->engine->memoryManager->allocManaged<CallContext>(requiredMemoryForExecutionContect(f, 0))); - new (c->d()) Heap::CallContext(d()->engine, qml, f); - return c->d(); + return d()->engine->memoryManager->alloc<QmlContext>(this, qml); } - void ExecutionContext::createMutableBinding(String *name, bool deletable) { Scope scope(this); // find the right context to create the binding on - ScopedObject activation(scope, d()->engine->globalObject()); + ScopedObject activation(scope, d()->engine->globalObject); ScopedContext ctx(scope, this); while (ctx) { - if (ctx->d()->type >= Heap::ExecutionContext::Type_CallContext) { + switch (ctx->d()->type) { + case Heap::ExecutionContext::Type_CallContext: + case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); if (!c->activation) c->activation = scope.engine->newObject(); activation = c->activation; break; } + case Heap::ExecutionContext::Type_QmlContext: { + Heap::QmlContext *qml = static_cast<Heap::QmlContext *>(ctx->d()); + activation = qml->qml; + break; + } + default: + break; + } ctx = ctx->d()->outer; } @@ -130,7 +139,7 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) Heap::GlobalContext::GlobalContext(ExecutionEngine *eng) : Heap::ExecutionContext(eng, Heap::ExecutionContext::Type_GlobalContext) { - global = eng->globalObject()->d(); + global = eng->globalObject->d(); } Heap::WithContext::WithContext(ExecutionEngine *engine, QV4::Object *with) @@ -153,34 +162,23 @@ Heap::CatchContext::CatchContext(ExecutionEngine *engine, QV4::String *exception lookups = parent->lookups; compilationUnit = parent->compilationUnit; - this->exceptionVarName = exceptionVarName; + this->exceptionVarName = exceptionVarName->d(); this->exceptionValue = exceptionValue; } -Heap::CallContext::CallContext(ExecutionEngine *engine, QV4::Object *qml, QV4::FunctionObject *function) - : Heap::ExecutionContext(engine, Heap::ExecutionContext::Type_QmlContext) +Heap::QmlContext::QmlContext(QV4::ExecutionContext *outer, QV4::QmlContextWrapper *qml) + : Heap::ExecutionContext(outer->engine(), Heap::ExecutionContext::Type_QmlContext) { - this->function = function->d(); - callData = reinterpret_cast<CallData *>(this + 1); - callData->tag = QV4::Value::_Integer_Type; - callData->argc = 0; - callData->thisObject = Primitive::undefinedValue(); - strictMode = false; - outer = function->scope(); - - activation = qml->d(); - - if (function->function()) { - compilationUnit = function->function()->compilationUnit; - lookups = compilationUnit->runtimeLookups; - } + callData = parent->callData; + this->outer = outer->d(); + lookups = parent->lookups; + compilationUnit = parent->compilationUnit; - locals = (Value *)(this + 1); - if (function->varCount()) - std::fill(locals, locals + function->varCount(), Primitive::undefinedValue()); + this->qml = qml->d(); } + Identifier * const *CallContext::formals() const { return (d()->function && d()->function->function) ? d()->function->function->internalClass->nameMap.constData() : 0; @@ -209,16 +207,28 @@ bool ExecutionContext::deleteProperty(String *name) bool hasWith = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { - if (ctx->d()->type == Heap::ExecutionContext::Type_WithContext) { + switch (ctx->d()->type) { + case Heap::ExecutionContext::Type_CatchContext: { + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + if (c->exceptionVarName->isEqualTo(name->d())) + return false; + break; + } + case Heap::ExecutionContext::Type_WithContext: { hasWith = true; ScopedObject withObject(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); if (withObject->hasProperty(name)) return withObject->deleteProperty(name); - } else if (ctx->d()->type == Heap::ExecutionContext::Type_CatchContext) { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); - if (c->exceptionVarName->isEqualTo(name)) - return false; - } else if (ctx->d()->type >= Heap::ExecutionContext::Type_CallContext) { + break; + } + case Heap::ExecutionContext::Type_GlobalContext: { + ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); + if (global->hasProperty(name)) + return global->deleteProperty(name); + break; + } + case Heap::ExecutionContext::Type_CallContext: + case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); ScopedFunctionObject f(scope, c->function); if (f->needsActivation() || hasWith) { @@ -227,13 +237,14 @@ bool ExecutionContext::deleteProperty(String *name) // ### throw in strict mode? return false; } - ScopedObject activation(scope, c->activation); - if (activation && activation->hasProperty(name)) - return activation->deleteProperty(name); - } else if (ctx->d()->type == Heap::ExecutionContext::Type_GlobalContext) { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); - if (global->hasProperty(name)) - return global->deleteProperty(name); + ScopedObject qml(scope, c->activation); + if (qml && qml->hasProperty(name)) + return qml->deleteProperty(name); + break; + } + case Heap::ExecutionContext::Type_QmlContext: + // can't delete properties on qml objects + break; } } @@ -254,7 +265,27 @@ void ExecutionContext::markObjects(Heap::Base *m, ExecutionEngine *engine) if (ctx->outer) ctx->outer->mark(engine); - if (ctx->type >= Heap::ExecutionContext::Type_CallContext) { + switch (ctx->type) { + case Heap::ExecutionContext::Type_CatchContext: { + CatchContext::Data *c = static_cast<CatchContext::Data *>(ctx); + c->exceptionVarName->mark(engine); + c->exceptionValue.mark(engine); + break; + } + case Heap::ExecutionContext::Type_WithContext: { + WithContext::Data *w = static_cast<WithContext::Data *>(ctx); + if (w->withObject) + w->withObject->mark(engine); + break; + } + case Heap::ExecutionContext::Type_GlobalContext: { + GlobalContext::Data *g = static_cast<GlobalContext::Data *>(ctx); + g->global->mark(engine); + break; + } + case Heap::ExecutionContext::Type_SimpleCallContext: + break; + case Heap::ExecutionContext::Type_CallContext: { QV4::Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); ctx->callData->thisObject.mark(engine); for (int arg = 0; arg < qMax(ctx->callData->argc, (int)c->function->formalParameterCount()); ++arg) @@ -264,17 +295,13 @@ void ExecutionContext::markObjects(Heap::Base *m, ExecutionEngine *engine) if (c->activation) c->activation->mark(engine); c->function->mark(engine); - } else if (ctx->type == Heap::ExecutionContext::Type_WithContext) { - WithContext::Data *w = static_cast<WithContext::Data *>(ctx); - if (w->withObject) - w->withObject->mark(engine); - } else if (ctx->type == Heap::ExecutionContext::Type_CatchContext) { - CatchContext::Data *c = static_cast<CatchContext::Data *>(ctx); - c->exceptionVarName->mark(engine); - c->exceptionValue.mark(engine); - } else if (ctx->type == Heap::ExecutionContext::Type_GlobalContext) { - GlobalContext::Data *g = static_cast<GlobalContext::Data *>(ctx); - g->global->mark(engine); + break; + } + case Heap::ExecutionContext::Type_QmlContext: { + QmlContext::Data *g = static_cast<QmlContext::Data *>(ctx); + g->qml->mark(engine); + break; + } } } @@ -282,57 +309,71 @@ void ExecutionContext::setProperty(String *name, const Value &value) { Scope scope(this); ScopedContext ctx(scope, this); + ScopedObject activation(scope); + for (; ctx; ctx = ctx->d()->outer) { - if (ctx->d()->type == Heap::ExecutionContext::Type_WithContext) { + activation = (Object *)0; + switch (ctx->d()->type) { + case Heap::ExecutionContext::Type_CatchContext: { + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + if (c->exceptionVarName->isEqualTo(name->d())) { + c->exceptionValue = value; + return; + } + break; + } + case Heap::ExecutionContext::Type_WithContext: { ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); if (w->hasProperty(name)) { w->put(name, value); return; } - } else if (ctx->d()->type == Heap::ExecutionContext::Type_CatchContext && static_cast<Heap::CatchContext *>(ctx->d())->exceptionVarName->isEqualTo(name)) { - static_cast<Heap::CatchContext *>(ctx->d())->exceptionValue = value; - return; - } else { - ScopedObject activation(scope, (Object *)0); - if (ctx->d()->type >= Heap::ExecutionContext::Type_CallContext) { - Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - if (c->function->function) { - uint index = c->function->function->internalClass->find(name); - if (index < UINT_MAX) { - if (index < c->function->formalParameterCount()) { - c->callData->args[c->function->formalParameterCount() - index - 1] = value; - } else { - index -= c->function->formalParameterCount(); - c->locals[index] = value; - } - return; + break; + } + case Heap::ExecutionContext::Type_GlobalContext: { + activation = static_cast<Heap::GlobalContext *>(ctx->d())->global; + break; + } + case Heap::ExecutionContext::Type_CallContext: + case Heap::ExecutionContext::Type_SimpleCallContext: { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); + if (c->function->function) { + uint index = c->function->function->internalClass->find(name); + if (index < UINT_MAX) { + if (index < c->function->formalParameterCount()) { + c->callData->args[c->function->formalParameterCount() - index - 1] = value; + } else { + index -= c->function->formalParameterCount(); + c->locals[index] = value; } + return; } - activation = c->activation; - } else if (ctx->d()->type == Heap::ExecutionContext::Type_GlobalContext) { - activation = static_cast<Heap::GlobalContext *>(ctx->d())->global; } + activation = c->activation; + break; + } + case Heap::ExecutionContext::Type_QmlContext: { + activation = static_cast<Heap::QmlContext *>(ctx->d())->qml; + activation->put(name, value); + return; + } + } - if (activation) { - if (ctx->d()->type == Heap::ExecutionContext::Type_QmlContext) { - activation->put(name, value); - return; - } else { - uint member = activation->internalClass()->find(name); - if (member < UINT_MAX) { - activation->putValue(activation->propertyAt(member), activation->internalClass()->propertyData[member], value); - return; - } - } + if (activation) { + uint member = activation->internalClass()->find(name); + if (member < UINT_MAX) { + activation->putValue(activation->propertyAt(member), activation->internalClass()->propertyData[member], value); + return; } } } - if (d()->strictMode || name->equals(d()->engine->id_this)) { + + if (d()->strictMode || name->equals(d()->engine->id_this())) { ScopedValue n(scope, name->asReturnedValue()); engine()->throwReferenceError(n); return; } - d()->engine->globalObject()->put(name, value); + d()->engine->globalObject->put(name, value); } ReturnedValue ExecutionContext::getProperty(String *name) @@ -341,14 +382,22 @@ ReturnedValue ExecutionContext::getProperty(String *name) ScopedValue v(scope); name->makeIdentifier(scope.engine); - if (name->equals(d()->engine->id_this)) + if (name->equals(d()->engine->id_this())) return thisObject().asReturnedValue(); bool hasWith = false; bool hasCatchScope = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { - if (ctx->d()->type == Heap::ExecutionContext::Type_WithContext) { + switch (ctx->d()->type) { + case Heap::ExecutionContext::Type_CatchContext: { + hasCatchScope = true; + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + if (c->exceptionVarName->isEqualTo(name->d())) + return c->exceptionValue.asReturnedValue(); + break; + } + case Heap::ExecutionContext::Type_WithContext: { ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); hasWith = true; bool hasProperty = false; @@ -356,17 +405,18 @@ ReturnedValue ExecutionContext::getProperty(String *name) if (hasProperty) { return v->asReturnedValue(); } - continue; + break; } - - else if (ctx->d()->type == Heap::ExecutionContext::Type_CatchContext) { - hasCatchScope = true; - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); - if (c->exceptionVarName->isEqualTo(name)) - return c->exceptionValue.asReturnedValue(); + case Heap::ExecutionContext::Type_GlobalContext: { + ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); + bool hasProperty = false; + v = global->get(name, &hasProperty); + if (hasProperty) + return v->asReturnedValue(); + break; } - - else if (ctx->d()->type >= Heap::ExecutionContext::Type_CallContext) { + case Heap::ExecutionContext::Type_CallContext: + case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); ScopedFunctionObject f(scope, c->function); if (f->function() && (f->needsActivation() || hasWith || hasCatchScope)) { @@ -387,85 +437,97 @@ ReturnedValue ExecutionContext::getProperty(String *name) if (f->function() && f->function()->isNamedExpression() && name->equals(ScopedString(scope, f->function()->name()))) return f.asReturnedValue(); + break; } - - else if (ctx->d()->type == Heap::ExecutionContext::Type_GlobalContext) { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); + case Heap::ExecutionContext::Type_QmlContext: { + ScopedObject qml(scope, static_cast<Heap::QmlContext *>(ctx->d())->qml); bool hasProperty = false; - v = global->get(name, &hasProperty); + v = qml->get(name, &hasProperty); if (hasProperty) return v->asReturnedValue(); + break; + } } } ScopedValue n(scope, name); return engine()->throwReferenceError(n); } -ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Heap::Object **base) +ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) { Scope scope(this); ScopedValue v(scope); - *base = (Heap::Object *)0; + base->setM(0); name->makeIdentifier(scope.engine); - if (name->equals(d()->engine->id_this)) + if (name->equals(d()->engine->id_this())) return thisObject().asReturnedValue(); bool hasWith = false; bool hasCatchScope = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { - if (ctx->d()->type == Heap::ExecutionContext::Type_WithContext) { + switch (ctx->d()->type) { + case Heap::ExecutionContext::Type_CatchContext: { + hasCatchScope = true; + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + if (c->exceptionVarName->isEqualTo(name->d())) + return c->exceptionValue.asReturnedValue(); + break; + } + case Heap::ExecutionContext::Type_WithContext: { ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); hasWith = true; bool hasProperty = false; v = w->get(name, &hasProperty); if (hasProperty) { - *base = w->d(); + base->setM(w->d()); return v->asReturnedValue(); } - continue; + break; } - - else if (ctx->d()->type == Heap::ExecutionContext::Type_CatchContext) { - hasCatchScope = true; - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); - if (c->exceptionVarName->isEqualTo(name)) - return c->exceptionValue.asReturnedValue(); + case Heap::ExecutionContext::Type_GlobalContext: { + ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); + bool hasProperty = false; + v = global->get(name, &hasProperty); + if (hasProperty) + return v->asReturnedValue(); + break; } - - else if (ctx->d()->type >= Heap::ExecutionContext::Type_CallContext) { + case Heap::ExecutionContext::Type_CallContext: + case Heap::ExecutionContext::Type_SimpleCallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); ScopedFunctionObject f(scope, c->function); if (f->function() && (f->needsActivation() || hasWith || hasCatchScope)) { uint index = f->function()->internalClass->find(name); if (index < UINT_MAX) { - if (index < f->formalParameterCount()) - return c->callData->args[f->formalParameterCount() - index - 1].asReturnedValue(); - return c->locals[index - f->formalParameterCount()].asReturnedValue(); + if (index < c->function->formalParameterCount()) + return c->callData->args[c->function->formalParameterCount() - index - 1].asReturnedValue(); + return c->locals[index - c->function->formalParameterCount()].asReturnedValue(); } } ScopedObject activation(scope, c->activation); if (activation) { bool hasProperty = false; v = activation->get(name, &hasProperty); - if (hasProperty) { - if (ctx->d()->type == Heap::ExecutionContext::Type_QmlContext) - *base = activation->d(); + if (hasProperty) return v->asReturnedValue(); - } } if (f->function() && f->function()->isNamedExpression() && name->equals(ScopedString(scope, f->function()->name()))) - return c->function->asReturnedValue(); + return f.asReturnedValue(); + break; } - - else if (ctx->d()->type == Heap::ExecutionContext::Type_GlobalContext) { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); + case Heap::ExecutionContext::Type_QmlContext: { + ScopedObject qml(scope, static_cast<Heap::QmlContext *>(ctx->d())->qml); bool hasProperty = false; - v = global->get(name, &hasProperty); - if (hasProperty) + v = qml->get(name, &hasProperty); + if (hasProperty) { + base->setM(qml->d()); return v->asReturnedValue(); + } + break; + } } } ScopedValue n(scope, name); @@ -487,3 +549,14 @@ Heap::FunctionObject *ExecutionContext::getFunctionObject() const return 0; } + + +QObject *QmlContext::qmlScope() const +{ + return d()->qml->scopeObject; +} + +QQmlContextData *QmlContext::qmlContext() const +{ + return d()->qml->context; +} diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 8392dd836d..2667bbe0b2 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -38,6 +38,9 @@ QT_BEGIN_NAMESPACE +class QQmlContextData; +class QObject; + namespace QV4 { namespace CompiledData { @@ -45,6 +48,8 @@ struct CompilationUnit; struct Function; } +struct QmlContextWrapper; +struct Identifier; struct CallContext; struct CatchContext; struct WithContext; @@ -74,9 +79,9 @@ struct ExecutionContext : Base { Type_GlobalContext = 0x1, Type_CatchContext = 0x2, Type_WithContext = 0x3, - Type_SimpleCallContext = 0x4, - Type_CallContext = 0x5, - Type_QmlContext = 0x6 + Type_QmlContext = 0x4, + Type_SimpleCallContext = 0x5, + Type_CallContext = 0x6 }; inline ExecutionContext(ExecutionEngine *engine, ContextType t); @@ -84,8 +89,8 @@ struct ExecutionContext : Base { CallData *callData; ExecutionEngine *engine; - ExecutionContext *parent; - ExecutionContext *outer; + Pointer<ExecutionContext> parent; + Pointer<ExecutionContext> outer; Lookup *lookups; CompiledData::CompilationUnit *compilationUnit; @@ -102,29 +107,34 @@ struct CallContext : ExecutionContext { locals = 0; activation = 0; } - CallContext(ExecutionEngine *engine, QV4::Object *qml, QV4::FunctionObject *function); - FunctionObject *function; + Pointer<FunctionObject> function; Value *locals; - Object *activation; + Pointer<Object> activation; }; struct GlobalContext : ExecutionContext { GlobalContext(ExecutionEngine *engine); - Object *global; + Pointer<Object> global; }; struct CatchContext : ExecutionContext { CatchContext(ExecutionEngine *engine, QV4::String *exceptionVarName, const Value &exceptionValue); - StringValue exceptionVarName; + Pointer<String> exceptionVarName; Value exceptionValue; }; struct WithContext : ExecutionContext { WithContext(ExecutionEngine *engine, QV4::Object *with); - Object *withObject; + Pointer<Object> withObject; }; +struct QmlContextWrapper; + +struct QmlContext : ExecutionContext { + QmlContext(QV4::ExecutionContext *outer, QV4::QmlContextWrapper *qml); + Pointer<QmlContextWrapper> qml; +}; } @@ -139,16 +149,16 @@ struct Q_QML_EXPORT ExecutionContext : public Managed ExecutionEngine *engine() const { return d()->engine; } - Heap::CallContext *newCallContext(FunctionObject *f, CallData *callData); + Heap::CallContext *newCallContext(const FunctionObject *f, CallData *callData); Heap::WithContext *newWithContext(Object *with); Heap::CatchContext *newCatchContext(String *exceptionVarName, const Value &exceptionValue); - Heap::CallContext *newQmlContext(FunctionObject *f, Object *qml); + Heap::QmlContext *newQmlContext(QmlContextWrapper *qml); void createMutableBinding(String *name, bool deletable); void setProperty(String *name, const Value &value); ReturnedValue getProperty(String *name); - ReturnedValue getPropertyAndBase(String *name, Heap::Object **base); + ReturnedValue getPropertyAndBase(String *name, Value *base); bool deleteProperty(String *name); inline CallContext *asCallContext(); @@ -160,7 +170,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed static void markObjects(Heap::Base *m, ExecutionEngine *e); - const Value &thisObject() const { + Value &thisObject() const { return d()->callData->thisObject; } int argc() const { @@ -174,7 +184,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed } }; -struct CallContext : public ExecutionContext +struct Q_QML_EXPORT CallContext : public ExecutionContext { V4_MANAGED(CallContext, ExecutionContext) @@ -208,6 +218,14 @@ struct WithContext : public ExecutionContext V4_MANAGED(WithContext, ExecutionContext) }; +struct QmlContext : public ExecutionContext +{ + V4_MANAGED(QmlContext, ExecutionContext) + + QObject *qmlScope() const; + QQmlContextData *qmlContext() const; +}; + inline CallContext *ExecutionContext::asCallContext() { return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<CallContext *>(this) : 0; diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index 8a66c2cbfc..a741d61d10 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -33,6 +33,7 @@ #include "qv4dataview_p.h" #include "qv4arraybuffer_p.h" +#include "qv4string_p.h" #include "qendian.h" @@ -46,9 +47,9 @@ Heap::DataViewCtor::DataViewCtor(QV4::ExecutionContext *scope) { } -ReturnedValue DataViewCtor::construct(Managed *m, CallData *callData) +ReturnedValue DataViewCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<Object *>(m)->engine()); + Scope scope(static_cast<const Object *>(m)->engine()); Scoped<ArrayBuffer> buffer(scope, callData->argument(0)); if (!buffer) return scope.engine->throwTypeError(); @@ -69,14 +70,14 @@ ReturnedValue DataViewCtor::construct(Managed *m, CallData *callData) } -ReturnedValue DataViewCtor::call(Managed *that, CallData *callData) +ReturnedValue DataViewCtor::call(const Managed *that, CallData *callData) { return construct(that, callData); } Heap::DataView::DataView(ExecutionEngine *e) - : Heap::Object(e->emptyClass, e->dataViewPrototype.asObject()), + : Heap::Object(e->emptyClass, e->dataViewPrototype()), buffer(0), byteLength(0), byteOffset(0) @@ -94,9 +95,9 @@ void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(3)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); - defineDefaultProperty(engine->id_constructor, (o = ctor)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, 0); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, 0); defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, 0); diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 3f0c1e9e23..e98239396a 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -48,7 +48,7 @@ struct DataViewCtor : FunctionObject { struct DataView : Object { DataView(ExecutionEngine *e); - ArrayBuffer *buffer; + Pointer<ArrayBuffer> buffer; uint byteLength; uint byteOffset; }; @@ -59,8 +59,8 @@ struct DataViewCtor: FunctionObject { V4_OBJECT2(DataViewCtor, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct DataView : Object diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 451ef2486d..3f45751695 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -36,6 +36,7 @@ #include "qv4objectproto_p.h" #include "qv4scopedvalue_p.h" #include "qv4runtime_p.h" +#include "qv4string_p.h" #include <QtCore/QDebug> #include <QtCore/QDateTime> @@ -628,14 +629,14 @@ static double getLocalTZA() DEFINE_OBJECT_VTABLE(DateObject); Heap::DateObject::DateObject(QV4::ExecutionEngine *engine, const QDateTime &date) - : Heap::Object(engine->emptyClass, engine->datePrototype.asObject()) + : Heap::Object(engine->emptyClass, engine->datePrototype()) { - value.setDouble(date.isValid() ? date.toMSecsSinceEpoch() : qSNaN()); + this->date = date.isValid() ? date.toMSecsSinceEpoch() : qSNaN(); } QDateTime DateObject::toQDateTime() const { - return ToDateTime(date().asDouble(), Qt::LocalTime); + return ToDateTime(date(), Qt::LocalTime); } DEFINE_OBJECT_VTABLE(DateCtor); @@ -645,9 +646,9 @@ Heap::DateCtor::DateCtor(QV4::ExecutionContext *scope) { } -ReturnedValue DateCtor::construct(Managed *m, CallData *callData) +ReturnedValue DateCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<DateCtor *>(m)->engine()); + Scope scope(static_cast<const DateCtor *>(m)->engine()); double t = 0; if (callData->argc == 0) @@ -655,15 +656,16 @@ ReturnedValue DateCtor::construct(Managed *m, CallData *callData) else if (callData->argc == 1) { ScopedValue arg(scope, callData->args[0]); - if (DateObject *d = arg->asDateObject()) - arg = d->date(); - else + if (DateObject *d = arg->as<DateObject>()) { + t = d->date(); + } else { arg = RuntimeHelpers::toPrimitive(arg, PREFERREDTYPE_HINT); - if (arg->isString()) - t = ParseString(arg->stringValue()->toQString()); - else - t = TimeClip(arg->toNumber()); + if (arg->isString()) + t = ParseString(arg->stringValue()->toQString()); + else + t = TimeClip(arg->toNumber()); + } } else { // d.argc > 1 @@ -683,18 +685,18 @@ ReturnedValue DateCtor::construct(Managed *m, CallData *callData) return Encode(scope.engine->newDateObject(Primitive::fromDouble(t))); } -ReturnedValue DateCtor::call(Managed *m, CallData *) +ReturnedValue DateCtor::call(const Managed *m, CallData *) { double t = currentTime(); - return static_cast<DateCtor *>(m)->engine()->newString(ToString(t))->asReturnedValue(); + return static_cast<const DateCtor *>(m)->engine()->newString(ToString(t))->asReturnedValue(); } 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->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(7)); LocalTZA = getLocalTZA(); ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1); @@ -702,13 +704,13 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) ctor->defineDefaultProperty(QStringLiteral("now"), method_now, 0); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString, 0); + defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("toDateString"), method_toDateString, 0); defineDefaultProperty(QStringLiteral("toTimeString"), method_toTimeString, 0); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); - defineDefaultProperty(engine->id_valueOf, method_valueOf, 0); + defineDefaultProperty(engine->id_valueOf(), method_valueOf, 0); defineDefaultProperty(QStringLiteral("getTime"), method_getTime, 0); defineDefaultProperty(QStringLiteral("getYear"), method_getYear, 0); defineDefaultProperty(QStringLiteral("getFullYear"), method_getFullYear, 0); @@ -752,8 +754,8 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) double DatePrototype::getThisDate(ExecutionContext *ctx) { - if (DateObject *thisObject = ctx->thisObject().asDateObject()) - return thisObject->date().asDouble(); + if (DateObject *thisObject = ctx->thisObject().as<DateObject>()) + return thisObject->date(); else { ctx->engine()->throwTypeError(); return 0; @@ -994,8 +996,8 @@ ReturnedValue DatePrototype::method_setTime(CallContext *ctx) return ctx->engine()->throwTypeError(); double t = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); - self->date().setDouble(TimeClip(t)); - return self->date().asReturnedValue(); + self->setDate(TimeClip(t)); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setMilliseconds(CallContext *ctx) @@ -1005,175 +1007,175 @@ ReturnedValue DatePrototype::method_setMilliseconds(CallContext *ctx) if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double ms = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); - self->date().setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); - return self->date().asReturnedValue(); + self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCMilliseconds(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double ms = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); - self->date().setDouble(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); - return self->date().asReturnedValue(); + self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setSeconds(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double sec = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double ms = (ctx->argc() < 2) ? msFromTime(t) : ctx->args()[1].toNumber(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCSeconds(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double sec = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double ms = (ctx->argc() < 2) ? msFromTime(t) : ctx->args()[1].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setMinutes(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double min = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double sec = (ctx->argc() < 2) ? SecFromTime(t) : ctx->args()[1].toNumber(); double ms = (ctx->argc() < 3) ? msFromTime(t) : ctx->args()[2].toNumber(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCMinutes(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double min = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double sec = (ctx->argc() < 2) ? SecFromTime(t) : ctx->args()[1].toNumber(); double ms = (ctx->argc() < 3) ? msFromTime(t) : ctx->args()[2].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setHours(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double hour = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double min = (ctx->argc() < 2) ? MinFromTime(t) : ctx->args()[1].toNumber(); double sec = (ctx->argc() < 3) ? SecFromTime(t) : ctx->args()[2].toNumber(); double ms = (ctx->argc() < 4) ? msFromTime(t) : ctx->args()[3].toNumber(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCHours(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double hour = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double min = (ctx->argc() < 2) ? MinFromTime(t) : ctx->args()[1].toNumber(); double sec = (ctx->argc() < 3) ? SecFromTime(t) : ctx->args()[2].toNumber(); double ms = (ctx->argc() < 4) ? msFromTime(t) : ctx->args()[3].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(hour, min, sec, ms))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setDate(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double date = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCDate(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double date = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setMonth(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); double month = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double date = (ctx->argc() < 2) ? DateFromTime(t) : ctx->args()[1].toNumber(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCMonth(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double month = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double date = (ctx->argc() < 2) ? DateFromTime(t) : ctx->args()[1].toNumber(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setYear(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); if (std::isnan(t)) t = 0; else @@ -1189,49 +1191,49 @@ ReturnedValue DatePrototype::method_setYear(CallContext *ctx) r = UTC(MakeDate(r, TimeWithinDay(t))); r = TimeClip(r); } - self->date().setDouble(r); - return self->date().asReturnedValue(); + self->setDate(r); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setUTCFullYear(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); double year = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double month = (ctx->argc() < 2) ? MonthFromTime(t) : ctx->args()[1].toNumber(); double date = (ctx->argc() < 3) ? DateFromTime(t) : ctx->args()[2].toNumber(); t = TimeClip(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_setFullYear(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = LocalTime(self->date().asDouble()); + double t = LocalTime(self->date()); if (std::isnan(t)) t = 0; double year = ctx->argc() ? ctx->args()[0].toNumber() : qSNaN(); double month = (ctx->argc() < 2) ? MonthFromTime(t) : ctx->args()[1].toNumber(); double date = (ctx->argc() < 3) ? DateFromTime(t) : ctx->args()[2].toNumber(); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); - self->date().setDouble(t); - return self->date().asReturnedValue(); + self->setDate(t); + return Encode(self->date()); } ReturnedValue DatePrototype::method_toUTCString(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); return ctx->d()->engine->newString(ToUTCString(t))->asReturnedValue(); } @@ -1250,11 +1252,11 @@ static void addZeroPrefixedInt(QString &str, int num, int nDigits) ReturnedValue DatePrototype::method_toISOString(CallContext *ctx) { - DateObject *self = ctx->thisObject().asDateObject(); + DateObject *self = ctx->thisObject().as<DateObject>(); if (!self) return ctx->engine()->throwTypeError(); - double t = self->date().asDouble(); + double t = self->date(); if (!std::isfinite(t)) return ctx->engine()->throwRangeError(ctx->thisObject()); @@ -1297,7 +1299,7 @@ ReturnedValue DatePrototype::method_toJSON(CallContext *ctx) ScopedString s(scope, ctx->d()->engine->newString(QStringLiteral("toISOString"))); ScopedValue v(scope, O->objectValue()->get(s)); - FunctionObject *toIso = v->asFunctionObject(); + FunctionObject *toIso = v->as<FunctionObject>(); if (!toIso) return ctx->engine()->throwTypeError(); diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index dad3689054..7a6413e820 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -49,18 +49,19 @@ struct DateObject : Object { DateObject(InternalClass *ic, QV4::Object *prototype) : Object(ic, prototype) { - value = Encode(qSNaN()); + date = qSNaN(); } DateObject(QV4::ExecutionEngine *engine, const Value &date) - : Object(engine->emptyClass, engine->datePrototype.asObject()) + : Object(engine->emptyClass, engine->datePrototype()) { - value = date; + this->date = date.toNumber(); } DateObject(QV4::ExecutionEngine *engine, const QDateTime &date); - Value value; + double date; }; + struct DateCtor : FunctionObject { DateCtor(QV4::ExecutionContext *scope); }; @@ -72,19 +73,23 @@ struct DateObject: Object { Q_MANAGED_TYPE(DateObject) - Value date() const { return d()->value; } - Value &date() { return d()->value; } - void setDate(const Value &date) { d()->value = date; } + double date() const { return d()->date; } + void setDate(double date) { d()->date = date; } QDateTime toQDateTime() const; }; +template<> +inline const DateObject *Value::as() const { + return isManaged() && m() && m()->vtable()->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : 0; +} + struct DateCtor: FunctionObject { V4_OBJECT2(DateCtor, FunctionObject) - static ReturnedValue construct(Managed *, CallData *callData); - static ReturnedValue call(Managed *that, CallData *); + static ReturnedValue construct(const Managed *, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *); }; struct DatePrototype: DateObject diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp index 36e7a3558c..ceeef80b9f 100644 --- a/src/qml/jsruntime/qv4debugging.cpp +++ b/src/qml/jsruntime/qv4debugging.cpp @@ -38,69 +38,65 @@ #include "qv4instr_moth_p.h" #include "qv4runtime_p.h" #include "qv4script_p.h" -#include "qv4objectiterator_p.h" #include "qv4identifier_p.h" -#include <iostream> +#include "qv4string_p.h" +#include "qv4objectiterator_p.h" +#include <iostream> #include <algorithm> +#include <QtCore/QJsonArray> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonValue> + +QT_BEGIN_NAMESPACE + using namespace QV4; using namespace QV4::Debugging; -namespace { -class JavaScriptJob: public Debugger::Job -{ - QV4::ExecutionEngine *engine; - int frameNr; - const QString &script; +Debugger::JavaScriptJob::JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, + const QString &script) + : engine(engine) + , frameNr(frameNr) + , script(script) +{} -public: - JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, const QString &script) - : engine(engine) - , frameNr(frameNr) - , script(script) - {} - - void run() - { - Scope scope(engine); +void Debugger::JavaScriptJob::run() +{ + Scope scope(engine); - ExecutionContextSaver saver(scope, engine->currentContext()); + ExecutionContextSaver saver(scope, engine->currentContext()); - if (frameNr > 0) { - Value *savedContexts = scope.alloc(frameNr); - for (int i = 0; i < frameNr; ++i) { - savedContexts[i] = engine->currentContext(); - engine->popContext(); - } + if (frameNr > 0) { + Value *savedContexts = scope.alloc(frameNr); + for (int i = 0; i < frameNr; ++i) { + savedContexts[i] = engine->currentContext(); + engine->popContext(); } - - ScopedContext ctx(scope, engine->currentContext()); - QV4::Script script(ctx, this->script); - script.strictMode = ctx->d()->strictMode; - // In order for property lookups in QML to work, we need to disable fast v4 lookups. That - // is a side-effect of inheritContext. - script.inheritContext = true; - script.parse(); - QV4::ScopedValue result(scope); - if (!scope.engine->hasException) - result = script.run(); - if (scope.engine->hasException) - result = scope.engine->catchException(); - handleResult(result); } -protected: - virtual void handleResult(QV4::ScopedValue &result) = 0; -}; + ScopedContext ctx(scope, engine->currentContext()); + QV4::Script script(ctx, this->script); + script.strictMode = ctx->d()->strictMode; + // In order for property lookups in QML to work, we need to disable fast v4 lookups. That + // is a side-effect of inheritContext. + script.inheritContext = true; + script.parse(); + QV4::ScopedValue result(scope); + if (!scope.engine->hasException) + result = script.run(); + if (scope.engine->hasException) + result = scope.engine->catchException(); + handleResult(result); +} -class EvalJob: public JavaScriptJob +class EvalJob: public Debugger::JavaScriptJob { bool result; public: EvalJob(QV4::ExecutionEngine *engine, const QString &script) - : JavaScriptJob(engine, /*frameNr*/-1, script) + : Debugger::JavaScriptJob(engine, /*frameNr*/-1, script) , result(false) {} @@ -115,58 +111,8 @@ public: } }; -class ExpressionEvalJob: public JavaScriptJob -{ - Debugger::Collector *collector; - -public: - ExpressionEvalJob(ExecutionEngine *engine, int frameNr, const QString &expression, Debugger::Collector *collector) - : JavaScriptJob(engine, frameNr, expression) - , collector(collector) - { - } - - virtual void handleResult(QV4::ScopedValue &result) - { - collector->collect(QStringLiteral("body"), result); - } -}; - -class GatherSourcesJob: public Debugger::Job -{ - QV4::ExecutionEngine *engine; - const int seq; - -public: - GatherSourcesJob(QV4::ExecutionEngine *engine, int seq) - : engine(engine) - , seq(seq) - {} - - ~GatherSourcesJob() {} - - void run() - { - QStringList sources; - - foreach (QV4::CompiledData::CompilationUnit *unit, engine->compilationUnits) { - QString fileName = unit->fileName(); - if (!fileName.isEmpty()) - sources.append(fileName); - } - - Debugger *debugger = engine->debugger; - QMetaObject::invokeMethod(debugger->agent(), "sourcesCollected", Qt::QueuedConnection, - Q_ARG(QV4::Debugging::Debugger*, debugger), - Q_ARG(QStringList, sources), - Q_ARG(int, seq)); - } -}; -} - Debugger::Debugger(QV4::ExecutionEngine *engine) : m_engine(engine) - , m_agent(0) , m_state(Running) , m_stepping(NotStepping) , m_pauseRequested(false) @@ -180,41 +126,6 @@ Debugger::Debugger(QV4::ExecutionEngine *engine) qMetaTypeId<PauseReason>(); } -Debugger::~Debugger() -{ - detachFromAgent(); -} - -void Debugger::attachToAgent(DebuggerAgent *agent) -{ - Q_ASSERT(!m_agent); - m_agent = agent; -} - -void Debugger::detachFromAgent() -{ - DebuggerAgent *agent = 0; - { - QMutexLocker locker(&m_lock); - agent = m_agent; - m_agent = 0; - } - if (agent) - agent->removeDebugger(this); -} - -void Debugger::gatherSources(int requestSequenceNr) -{ - QMutexLocker locker(&m_lock); - - m_gatherSources = new GatherSourcesJob(m_engine, requestSequenceNr); - if (m_state == Paused) { - runInEngine_havingLock(m_gatherSources); - delete m_gatherSources; - m_gatherSources = 0; - } -} - void Debugger::pause() { QMutexLocker locker(&m_lock); @@ -272,251 +183,6 @@ QVector<StackFrame> Debugger::stackTrace(int frameLimit) const return m_engine->stackTrace(frameLimit); } -static inline Heap::CallContext *findContext(Heap::ExecutionContext *ctxt, int frame) -{ - if (!ctxt) - return 0; - - Scope scope(ctxt->engine); - ScopedContext ctx(scope, ctxt); - while (ctx) { - CallContext *cCtxt = ctx->asCallContext(); - if (cCtxt && cCtxt->d()->function) { - if (frame < 1) - return cCtxt->d(); - --frame; - } - ctx = ctx->d()->parent; - } - - return 0; -} - -static inline Heap::CallContext *findScope(Heap::ExecutionContext *ctxt, int scope) -{ - if (!ctxt) - return 0; - - Scope s(ctxt->engine); - ScopedContext ctx(s, ctxt); - for (; scope > 0 && ctx; --scope) - ctx = ctx->d()->outer; - - return (ctx && ctx->d()) ? ctx->asCallContext()->d() : 0; -} - -void Debugger::collectArgumentsInContext(Collector *collector, int frameNr, int scopeNr) -{ - if (state() != Paused) - return; - - class ArgumentCollectJob: public Job - { - QV4::ExecutionEngine *engine; - Collector *collector; - int frameNr; - int scopeNr; - - public: - ArgumentCollectJob(QV4::ExecutionEngine *engine, Collector *collector, int frameNr, int scopeNr) - : engine(engine) - , collector(collector) - , frameNr(frameNr) - , scopeNr(scopeNr) - {} - - ~ArgumentCollectJob() {} - - void run() - { - if (frameNr < 0) - return; - - Scope scope(engine); - Scoped<CallContext> ctxt(scope, findScope(findContext(engine->currentContext(), frameNr), scopeNr)); - if (!ctxt) - return; - - ScopedValue v(scope); - int nFormals = ctxt->formalCount(); - for (unsigned i = 0, ei = nFormals; i != ei; ++i) { - QString qName; - if (Identifier *name = ctxt->formals()[nFormals - i - 1]) - qName = name->string; - v = ctxt->argument(i); - collector->collect(qName, v); - } - } - }; - - ArgumentCollectJob job(m_engine, collector, frameNr, scopeNr); - runInEngine(&job); -} - -/// Same as \c retrieveArgumentsFromContext, but now for locals. -void Debugger::collectLocalsInContext(Collector *collector, int frameNr, int scopeNr) -{ - if (state() != Paused) - return; - - class LocalCollectJob: public Job - { - QV4::ExecutionEngine *engine; - Collector *collector; - int frameNr; - int scopeNr; - - public: - LocalCollectJob(QV4::ExecutionEngine *engine, Collector *collector, int frameNr, int scopeNr) - : engine(engine) - , collector(collector) - , frameNr(frameNr) - , scopeNr(scopeNr) - {} - - void run() - { - if (frameNr < 0) - return; - - Scope scope(engine); - Scoped<CallContext> ctxt(scope, findScope(findContext(engine->currentContext(), frameNr), scopeNr)); - if (!ctxt) - return; - - ScopedValue v(scope); - for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) { - QString qName; - if (Identifier *name = ctxt->variables()[i]) - qName = name->string; - v = ctxt->d()->locals[i]; - collector->collect(qName, v); - } - } - }; - - LocalCollectJob job(m_engine, collector, frameNr, scopeNr); - runInEngine(&job); -} - -bool Debugger::collectThisInContext(Debugger::Collector *collector, int frame) -{ - if (state() != Paused) - return false; - - class ThisCollectJob: public Job - { - QV4::ExecutionEngine *engine; - Collector *collector; - int frameNr; - bool *foundThis; - - public: - ThisCollectJob(QV4::ExecutionEngine *engine, Collector *collector, int frameNr, bool *foundThis) - : engine(engine) - , collector(collector) - , frameNr(frameNr) - , foundThis(foundThis) - {} - - void run() - { - *foundThis = myRun(); - } - - bool myRun() - { - Scope scope(engine); - ScopedContext ctxt(scope, findContext(engine->currentContext(), frameNr)); - while (ctxt) { - if (CallContext *cCtxt = ctxt->asCallContext()) - if (cCtxt->d()->activation) - break; - ctxt = ctxt->d()->outer; - } - - if (!ctxt) - return false; - - ScopedObject o(scope, ctxt->asCallContext()->d()->activation); - collector->collect(o); - return true; - } - }; - - bool foundThis = false; - ThisCollectJob job(m_engine, collector, frame, &foundThis); - runInEngine(&job); - return foundThis; -} - -void Debugger::collectThrownValue(Collector *collector) -{ - if (state() != Paused || !m_engine->hasException) - return; - - class ThisCollectJob: public Job - { - QV4::ExecutionEngine *engine; - Collector *collector; - - public: - ThisCollectJob(QV4::ExecutionEngine *engine, Collector *collector) - : engine(engine) - , collector(collector) - {} - - void run() - { - Scope scope(engine); - ScopedValue v(scope, engine->exceptionValue); - collector->collect(QStringLiteral("exception"), v); - } - }; - - ThisCollectJob job(m_engine, collector); - runInEngine(&job); -} - -void Debugger::collectReturnedValue(Collector *collector) const -{ - if (state() != Paused) - return; - - Scope scope(m_engine); - ScopedObject o(scope, m_returnedValue.valueRef()); - collector->collect(o); -} - -QVector<Heap::ExecutionContext::ContextType> Debugger::getScopeTypes(int frame) const -{ - QVector<Heap::ExecutionContext::ContextType> types; - - if (state() != Paused) - return types; - - Scope scope(m_engine); - Scoped<CallContext> sctxt(scope, findContext(m_engine->currentContext(), frame)); - if (!sctxt || sctxt->d()->type < Heap::ExecutionContext::Type_SimpleCallContext) - return types; - - ScopedContext it(scope, sctxt->d()); - for (; it; it = it->d()->outer) - types.append(it->d()->type); - - return types; -} - - -void Debugger::evaluateExpression(int frameNr, const QString &expression, Debugger::Collector *resultsCollector) -{ - Q_ASSERT(state() == Paused); - - Q_ASSERT(m_runningJob == 0); - ExpressionEvalJob job(m_engine, frameNr, expression, resultsCollector); - runInEngine(&job); -} - void Debugger::maybeBreakAtInstruction() { if (m_runningJob) // do not re-enter when we're doing a job for the debugger. @@ -610,9 +276,7 @@ void Debugger::pauseAndWait(PauseReason reason) return; m_state = Paused; - QMetaObject::invokeMethod(m_agent, "debuggerPaused", Qt::QueuedConnection, - Q_ARG(QV4::Debugging::Debugger*, this), - Q_ARG(QV4::Debugging::PauseReason, reason)); + emit debuggerPaused(this, reason); while (true) { m_runningCondition.wait(&m_lock); @@ -662,174 +326,8 @@ void Debugger::runInEngine_havingLock(Debugger::Job *job) m_runningJob = 0; } -void DebuggerAgent::addDebugger(Debugger *debugger) -{ - Q_ASSERT(!m_debuggers.contains(debugger)); - m_debuggers << debugger; - debugger->attachToAgent(this); - - debugger->setBreakOnThrow(m_breakOnThrow); - - foreach (const BreakPoint &breakPoint, m_breakPoints.values()) - if (breakPoint.enabled) - debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition); -} - -void DebuggerAgent::removeDebugger(Debugger *debugger) -{ - m_debuggers.removeAll(debugger); - debugger->detachFromAgent(); -} - -void DebuggerAgent::pause(Debugger *debugger) const -{ - debugger->pause(); -} - -void DebuggerAgent::pauseAll() const -{ - foreach (Debugger *debugger, m_debuggers) - pause(debugger); -} - -void DebuggerAgent::resumeAll() const -{ - foreach (Debugger *debugger, m_debuggers) - if (debugger->state() == Debugger::Paused) - debugger->resume(Debugger::FullThrottle); -} - -int DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, bool enabled, const QString &condition) -{ - if (enabled) - foreach (Debugger *debugger, m_debuggers) - debugger->addBreakPoint(fileName, lineNumber, condition); - - int id = m_breakPoints.size(); - m_breakPoints.insert(id, BreakPoint(fileName, lineNumber, enabled, condition)); - return id; -} - -void DebuggerAgent::removeBreakPoint(int id) -{ - BreakPoint breakPoint = m_breakPoints.value(id); - if (!breakPoint.isValid()) - return; - - m_breakPoints.remove(id); - - if (breakPoint.enabled) - foreach (Debugger *debugger, m_debuggers) - debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr); -} - -void DebuggerAgent::removeAllBreakPoints() -{ - QList<int> ids = m_breakPoints.keys(); - foreach (int id, ids) - removeBreakPoint(id); -} - -void DebuggerAgent::enableBreakPoint(int id, bool onoff) -{ - BreakPoint &breakPoint = m_breakPoints[id]; - if (!breakPoint.isValid() || breakPoint.enabled == onoff) - return; - breakPoint.enabled = onoff; - - foreach (Debugger *debugger, m_debuggers) { - if (onoff) - debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition); - else - debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr); - } -} - -QList<int> DebuggerAgent::breakPointIds(const QString &fileName, int lineNumber) const -{ - QList<int> ids; - - for (QHash<int, BreakPoint>::const_iterator i = m_breakPoints.begin(), ei = m_breakPoints.end(); i != ei; ++i) - if (i->lineNr == lineNumber && fileName.endsWith(i->fileName)) - ids.push_back(i.key()); - - return ids; -} - -void DebuggerAgent::setBreakOnThrow(bool onoff) -{ - if (onoff != m_breakOnThrow) { - m_breakOnThrow = onoff; - foreach (Debugger *debugger, m_debuggers) - debugger->setBreakOnThrow(onoff); - } -} - -DebuggerAgent::~DebuggerAgent() -{ - foreach (Debugger *debugger, m_debuggers) - debugger->detachFromAgent(); - - Q_ASSERT(m_debuggers.isEmpty()); -} - -Debugger::Collector::~Collector() -{ -} - -void Debugger::Collector::collect(const QString &name, const ScopedValue &value) -{ - switch (value->type()) { - case Value::Empty_Type: - Q_ASSERT(!"empty Value encountered"); - break; - case Value::Undefined_Type: - addUndefined(name); - break; - case Value::Null_Type: - addNull(name); - break; - case Value::Boolean_Type: - addBoolean(name, value->booleanValue()); - break; - case Value::Managed_Type: - if (String *s = value->asString()) - addString(name, s->toQString()); - else - addObject(name, value); - break; - case Value::Integer_Type: - addInteger(name, value->int_32); - break; - default: // double - addDouble(name, value->doubleValue()); - break; - } -} - -void Debugger::Collector::collect(Object *object) -{ - bool property = true; - qSwap(property, m_isProperty); - - Scope scope(m_engine); - ObjectIterator it(scope, object, ObjectIterator::EnumerableOnly); - ScopedValue name(scope); - ScopedValue value(scope); - while (true) { - Value v; - name = it.nextPropertyNameAsString(&v); - if (name->isNull()) - break; - QString key = name->toQStringNoThrow(); - value = v; - collect(key, value); - } - - qSwap(property, m_isProperty); -} - - Debugger::Job::~Job() { } + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h index e6a9750351..ac0c934d42 100644 --- a/src/qml/jsruntime/qv4debugging_p.h +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -44,6 +44,8 @@ #include <QMutex> #include <QWaitCondition> +#include <QtCore/QJsonObject> + QT_BEGIN_NAMESPACE namespace QV4 { @@ -59,8 +61,6 @@ enum PauseReason { Step }; -class DebuggerAgent; - struct DebuggerBreakPoint { DebuggerBreakPoint(const QString &fileName, int line) : fileName(fileName), lineNumber(line) @@ -79,43 +79,29 @@ inline bool operator==(const DebuggerBreakPoint &a, const DebuggerBreakPoint &b) typedef QHash<DebuggerBreakPoint, QString> BreakPoints; - -class Q_QML_EXPORT Debugger +class Q_QML_EXPORT Debugger : public QObject { + Q_OBJECT public: - class Job + class Q_QML_EXPORT Job { public: virtual ~Job() = 0; virtual void run() = 0; }; - class Q_QML_EXPORT Collector + class Q_QML_EXPORT JavaScriptJob: public Job { - public: - Collector(ExecutionEngine *engine): m_engine(engine), m_isProperty(false) {} - virtual ~Collector(); + QV4::ExecutionEngine *engine; + int frameNr; + const QString &script; - void collect(const QString &name, const ScopedValue &value); - void collect(Object *object); + public: + JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, const QString &script); + void run(); protected: - virtual void addUndefined(const QString &name) = 0; - virtual void addNull(const QString &name) = 0; - virtual void addBoolean(const QString &name, bool value) = 0; - virtual void addString(const QString &name, const QString &value) = 0; - virtual void addObject(const QString &name, const Value &value) = 0; - virtual void addInteger(const QString &name, int value) = 0; - virtual void addDouble(const QString &name, double value) = 0; - - QV4::ExecutionEngine *engine() const { return m_engine; } - - bool isProperty() const { return m_isProperty; } - void setIsProperty(bool onoff) { m_isProperty = onoff; } - - private: - QV4::ExecutionEngine *m_engine; - bool m_isProperty; + virtual void handleResult(QV4::ScopedValue &result) = 0; }; enum State { @@ -133,16 +119,10 @@ public: }; Debugger(ExecutionEngine *engine); - ~Debugger(); ExecutionEngine *engine() const { return m_engine; } - void attachToAgent(DebuggerAgent *agent); - void detachFromAgent(); - DebuggerAgent *agent() const { return m_agent; } - - void gatherSources(int requestSequenceNr); void pause(); void resume(Speed speed); @@ -166,14 +146,10 @@ public: } QVector<StackFrame> stackTrace(int frameLimit = -1) const; - void collectArgumentsInContext(Collector *collector, int frameNr = 0, int scopeNr = 0); - void collectLocalsInContext(Collector *collector, int frameNr = 0, int scopeNr = 0); - bool collectThisInContext(Collector *collector, int frame = 0); - void collectThrownValue(Collector *collector); - void collectReturnedValue(Collector *collector) const; QVector<Heap::ExecutionContext::ContextType> getScopeTypes(int frame = 0) const; - void evaluateExpression(int frameNr, const QString &expression, Collector *resultsCollector); + Function *getFunction() const; + void runInEngine(Job *job); public: // compile-time interface void maybeBreakAtInstruction(); @@ -183,21 +159,19 @@ public: // execution hooks void leavingFunction(const ReturnedValue &retVal); void aboutToThrow(); -private: - Function *getFunction() const; +signals: + void sourcesCollected(QV4::Debugging::Debugger *self, const QStringList &sources, int seq); + void debuggerPaused(QV4::Debugging::Debugger *self, QV4::Debugging::PauseReason reason); +private: // requires lock to be held void pauseAndWait(PauseReason reason); - bool reallyHitTheBreakPoint(const QString &filename, int linenr); - - void runInEngine(Job *job); void runInEngine_havingLock(Debugger::Job *job); private: QV4::ExecutionEngine *m_engine; QV4::PersistentValue m_currentContext; - DebuggerAgent *m_agent; QMutex m_lock; QWaitCondition m_runningCondition; State m_state; @@ -214,54 +188,6 @@ private: QWaitCondition m_jobIsRunning; }; -class Q_QML_EXPORT DebuggerAgent : public QObject -{ - Q_OBJECT -public: - DebuggerAgent(): m_breakOnThrow(false) {} - ~DebuggerAgent(); - - void addDebugger(Debugger *debugger); - void removeDebugger(Debugger *debugger); - - void pause(Debugger *debugger) const; - void pauseAll() const; - void resumeAll() const; - int addBreakPoint(const QString &fileName, int lineNumber, bool enabled = true, const QString &condition = QString()); - void removeBreakPoint(int id); - void removeAllBreakPoints(); - void enableBreakPoint(int id, bool onoff); - QList<int> breakPointIds(const QString &fileName, int lineNumber) const; - - bool breakOnThrow() const { return m_breakOnThrow; } - void setBreakOnThrow(bool onoff); - - Q_INVOKABLE virtual void debuggerPaused(QV4::Debugging::Debugger *debugger, - QV4::Debugging::PauseReason reason) = 0; - Q_INVOKABLE virtual void sourcesCollected(QV4::Debugging::Debugger *debugger, - QStringList sources, int requestSequenceNr) = 0; - -protected: - QList<Debugger *> m_debuggers; - - struct BreakPoint { - QString fileName; - int lineNr; - bool enabled; - QString condition; - - BreakPoint(): lineNr(-1), enabled(false) {} - BreakPoint(const QString &fileName, int lineNr, bool enabled, const QString &condition) - : fileName(fileName), lineNr(lineNr), enabled(enabled), condition(condition) - {} - - bool isValid() const { return lineNr >= 0 && !fileName.isEmpty(); } - }; - - QHash<int, BreakPoint> m_breakPoints; - bool m_breakOnThrow; -}; - } // namespace Debugging } // namespace QV4 diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index e1282eeca9..7424dc4e4c 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -32,7 +32,7 @@ ****************************************************************************/ #include <qv4engine_p.h> #include <qv4context_p.h> -#include <qv4value_inl_p.h> +#include <qv4value_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <qv4objectiterator_p.h> @@ -48,7 +48,7 @@ #include <qv4regexp_p.h> #include <qv4variantobject_p.h> #include <qv4runtime_p.h> -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include <qv4argumentsobject_p.h> #include <qv4dateobject_p.h> #include <qv4jsonobject_p.h> @@ -59,7 +59,6 @@ #include "qv4executableallocator_p.h" #include "qv4sequenceobject_p.h" #include "qv4qobjectwrapper_p.h" -#include "qv4qmlextensions_p.h" #include "qv4memberdata_p.h" #include "qv4arraybuffer_p.h" #include "qv4dataview_p.h" @@ -197,6 +196,7 @@ QQmlEngine *ExecutionEngine::qmlEngine() const ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) : current(0) + , hasException(false) , memoryManager(new QV4::MemoryManager(this)) , executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) @@ -211,13 +211,9 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , m_engineId(engineSerial.fetchAndAddOrdered(1)) , regExpCache(0) , m_multiplyWrappedQObjects(0) - , m_qmlExtensions(0) { MemoryManager::GCBlocker gcBlocker(memoryManager); - exceptionValue = Encode::undefined(); - hasException = false; - if (!factory) { #ifdef V4_ENABLE_JIT @@ -241,6 +237,13 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) jsStackBase = (Value *)jsStack->base(); jsStackTop = jsStackBase; + exceptionValue = jsAlloca(1); + globalObject = static_cast<Object *>(jsAlloca(1)); + jsObjects = jsAlloca(NJSObjects); + typedArrayPrototype = static_cast<Object *>(jsAlloca(NTypedArrayTypes)); + typedArrayCtors = static_cast<FunctionObject *>(jsAlloca(NTypedArrayTypes)); + jsStrings = jsAlloca(NJSStrings); + #ifdef V4_USE_VALGRIND VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, 2*JSStackLimit); #endif @@ -251,208 +254,208 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) if (!recheckCStackLimits()) qFatal("Fatal: Not enough stack space available for QML. Please increase the process stack size to more than %d KBytes.", MinimumStackSize); - Scope scope(this); - identifierTable = new IdentifierTable(this); classPool = new InternalClassPool; emptyClass = new (classPool) InternalClass(this); - id_empty = newIdentifier(QString()); - id_undefined = newIdentifier(QStringLiteral("undefined")); - id_null = newIdentifier(QStringLiteral("null")); - id_true = newIdentifier(QStringLiteral("true")); - id_false = newIdentifier(QStringLiteral("false")); - id_boolean = newIdentifier(QStringLiteral("boolean")); - id_number = newIdentifier(QStringLiteral("number")); - id_string = newIdentifier(QStringLiteral("string")); - id_object = newIdentifier(QStringLiteral("object")); - id_function = newIdentifier(QStringLiteral("function")); - id_length = newIdentifier(QStringLiteral("length")); - id_prototype = newIdentifier(QStringLiteral("prototype")); - id_constructor = newIdentifier(QStringLiteral("constructor")); - id_arguments = newIdentifier(QStringLiteral("arguments")); - id_caller = newIdentifier(QStringLiteral("caller")); - id_callee = newIdentifier(QStringLiteral("callee")); - id_this = newIdentifier(QStringLiteral("this")); - id___proto__ = newIdentifier(QStringLiteral("__proto__")); - id_enumerable = newIdentifier(QStringLiteral("enumerable")); - id_configurable = newIdentifier(QStringLiteral("configurable")); - id_writable = newIdentifier(QStringLiteral("writable")); - id_value = newIdentifier(QStringLiteral("value")); - id_get = newIdentifier(QStringLiteral("get")); - id_set = newIdentifier(QStringLiteral("set")); - id_eval = newIdentifier(QStringLiteral("eval")); - id_uintMax = newIdentifier(QStringLiteral("4294967295")); - id_name = newIdentifier(QStringLiteral("name")); - id_index = newIdentifier(QStringLiteral("index")); - id_input = newIdentifier(QStringLiteral("input")); - id_toString = newIdentifier(QStringLiteral("toString")); - id_destroy = newIdentifier(QStringLiteral("destroy")); - id_valueOf = newIdentifier(QStringLiteral("valueOf")); - id_byteLength = newIdentifier(QStringLiteral("byteLength")); - id_byteOffset = newIdentifier(QStringLiteral("byteOffset")); - id_buffer = newIdentifier(QStringLiteral("buffer")); - id_lastIndex = newIdentifier(QStringLiteral("lastIndex")); - - objectPrototype = memoryManager->alloc<ObjectPrototype>(emptyClass, (QV4::Object *)0); - - arrayClass = emptyClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable); - arrayPrototype = memoryManager->alloc<ArrayPrototype>(arrayClass, objectPrototype.asObject()); - - InternalClass *argsClass = emptyClass->addMember(id_length, Attr_NotEnumerable); - argumentsObjectClass = argsClass->addMember(id_callee, Attr_Data|Attr_NotEnumerable); - strictArgumentsObjectClass = argsClass->addMember(id_callee, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - strictArgumentsObjectClass = strictArgumentsObjectClass->addMember(id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - - m_globalObject = newObject(); - Q_ASSERT(globalObject()->d()->vtable); + jsStrings[String_Empty] = newIdentifier(QString()); + jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); + jsStrings[String_null] = newIdentifier(QStringLiteral("null")); + jsStrings[String_true] = newIdentifier(QStringLiteral("true")); + jsStrings[String_false] = newIdentifier(QStringLiteral("false")); + jsStrings[String_boolean] = newIdentifier(QStringLiteral("boolean")); + jsStrings[String_number] = newIdentifier(QStringLiteral("number")); + jsStrings[String_string] = newIdentifier(QStringLiteral("string")); + jsStrings[String_object] = newIdentifier(QStringLiteral("object")); + jsStrings[String_function] = newIdentifier(QStringLiteral("function")); + jsStrings[String_length] = newIdentifier(QStringLiteral("length")); + jsStrings[String_prototype] = newIdentifier(QStringLiteral("prototype")); + jsStrings[String_constructor] = newIdentifier(QStringLiteral("constructor")); + jsStrings[String_arguments] = newIdentifier(QStringLiteral("arguments")); + jsStrings[String_caller] = newIdentifier(QStringLiteral("caller")); + jsStrings[String_callee] = newIdentifier(QStringLiteral("callee")); + jsStrings[String_this] = newIdentifier(QStringLiteral("this")); + jsStrings[String___proto__] = newIdentifier(QStringLiteral("__proto__")); + jsStrings[String_enumerable] = newIdentifier(QStringLiteral("enumerable")); + jsStrings[String_configurable] = newIdentifier(QStringLiteral("configurable")); + jsStrings[String_writable] = newIdentifier(QStringLiteral("writable")); + jsStrings[String_value] = newIdentifier(QStringLiteral("value")); + jsStrings[String_get] = newIdentifier(QStringLiteral("get")); + jsStrings[String_set] = newIdentifier(QStringLiteral("set")); + jsStrings[String_eval] = newIdentifier(QStringLiteral("eval")); + jsStrings[String_uintMax] = newIdentifier(QStringLiteral("4294967295")); + jsStrings[String_name] = newIdentifier(QStringLiteral("name")); + jsStrings[String_index] = newIdentifier(QStringLiteral("index")); + jsStrings[String_input] = newIdentifier(QStringLiteral("input")); + jsStrings[String_toString] = newIdentifier(QStringLiteral("toString")); + jsStrings[String_destroy] = newIdentifier(QStringLiteral("destroy")); + jsStrings[String_valueOf] = newIdentifier(QStringLiteral("valueOf")); + jsStrings[String_byteLength] = newIdentifier(QStringLiteral("byteLength")); + jsStrings[String_byteOffset] = newIdentifier(QStringLiteral("byteOffset")); + jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer")); + jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex")); + + jsObjects[ObjectProto] = memoryManager->alloc<ObjectPrototype>(emptyClass, (QV4::Object *)0); + + arrayClass = emptyClass->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); + jsObjects[ArrayProto] = memoryManager->alloc<ArrayPrototype>(arrayClass, objectPrototype()); + + InternalClass *argsClass = emptyClass->addMember(id_length(), Attr_NotEnumerable); + argumentsObjectClass = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); + strictArgumentsObjectClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + strictArgumentsObjectClass = strictArgumentsObjectClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + + *static_cast<Value *>(globalObject) = newObject(); + Q_ASSERT(globalObject->d()->vtable()); initRootContext(); - stringPrototype = memoryManager->alloc<StringPrototype>(emptyClass, objectPrototype.asObject()); - numberPrototype = memoryManager->alloc<NumberPrototype>(emptyClass, objectPrototype.asObject()); - booleanPrototype = memoryManager->alloc<BooleanPrototype>(emptyClass, objectPrototype.asObject()); - datePrototype = memoryManager->alloc<DatePrototype>(emptyClass, objectPrototype.asObject()); + jsObjects[StringProto] = memoryManager->alloc<StringPrototype>(emptyClass, objectPrototype()); + jsObjects[NumberProto] = memoryManager->alloc<NumberPrototype>(emptyClass, objectPrototype()); + jsObjects[BooleanProto] = memoryManager->alloc<BooleanPrototype>(emptyClass, objectPrototype()); + jsObjects[DateProto] = memoryManager->alloc<DatePrototype>(emptyClass, objectPrototype()); uint index; - InternalClass *functionProtoClass = emptyClass->addMember(id_prototype, Attr_NotEnumerable, &index); + InternalClass *functionProtoClass = emptyClass->addMember(id_prototype(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - functionPrototype = memoryManager->alloc<FunctionPrototype>(functionProtoClass, objectPrototype.asObject()); - functionClass = emptyClass->addMember(id_prototype, Attr_NotEnumerable|Attr_NotConfigurable, &index); + jsObjects[FunctionProto] = memoryManager->alloc<FunctionPrototype>(functionProtoClass, objectPrototype()); + functionClass = emptyClass->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - simpleScriptFunctionClass = functionClass->addMember(id_name, Attr_ReadOnly, &index); + simpleScriptFunctionClass = functionClass->addMember(id_name(), Attr_ReadOnly, &index); Q_ASSERT(index == Heap::SimpleScriptFunction::Index_Name); - simpleScriptFunctionClass = simpleScriptFunctionClass->addMember(id_length, Attr_ReadOnly, &index); + simpleScriptFunctionClass = simpleScriptFunctionClass->addMember(id_length(), Attr_ReadOnly, &index); Q_ASSERT(index == Heap::SimpleScriptFunction::Index_Length); - protoClass = emptyClass->addMember(id_constructor, Attr_NotEnumerable, &index); + protoClass = emptyClass->addMember(id_constructor(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor); - regExpPrototype = memoryManager->alloc<RegExpPrototype>(this); - regExpExecArrayClass = arrayClass->addMember(id_index, Attr_Data, &index); + jsObjects[RegExpProto] = memoryManager->alloc<RegExpPrototype>(this); + regExpExecArrayClass = arrayClass->addMember(id_index(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayIndex); - regExpExecArrayClass = regExpExecArrayClass->addMember(id_input, Attr_Data, &index); + regExpExecArrayClass = regExpExecArrayClass->addMember(id_input(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayInput); - errorPrototype = memoryManager->alloc<ErrorPrototype>(emptyClass, objectPrototype.asObject()); - evalErrorPrototype = memoryManager->alloc<EvalErrorPrototype>(emptyClass, errorPrototype.asObject()); - rangeErrorPrototype = memoryManager->alloc<RangeErrorPrototype>(emptyClass, errorPrototype.asObject()); - referenceErrorPrototype = memoryManager->alloc<ReferenceErrorPrototype>(emptyClass, errorPrototype.asObject()); - syntaxErrorPrototype = memoryManager->alloc<SyntaxErrorPrototype>(emptyClass, errorPrototype.asObject()); - typeErrorPrototype = memoryManager->alloc<TypeErrorPrototype>(emptyClass, errorPrototype.asObject()); - uRIErrorPrototype = memoryManager->alloc<URIErrorPrototype>(emptyClass, errorPrototype.asObject()); + jsObjects[ErrorProto] = memoryManager->alloc<ErrorPrototype>(emptyClass, objectPrototype()); + jsObjects[EvalErrorProto] = memoryManager->alloc<EvalErrorPrototype>(emptyClass, errorPrototype()); + jsObjects[RangeErrorProto] = memoryManager->alloc<RangeErrorPrototype>(emptyClass, errorPrototype()); + jsObjects[ReferenceErrorProto] = memoryManager->alloc<ReferenceErrorPrototype>(emptyClass, errorPrototype()); + jsObjects[SyntaxErrorProto] = memoryManager->alloc<SyntaxErrorPrototype>(emptyClass, errorPrototype()); + jsObjects[TypeErrorProto] = memoryManager->alloc<TypeErrorPrototype>(emptyClass, errorPrototype()); + jsObjects[URIErrorProto] = memoryManager->alloc<URIErrorPrototype>(emptyClass, errorPrototype()); - variantPrototype = memoryManager->alloc<VariantPrototype>(emptyClass, objectPrototype.asObject()); - Q_ASSERT(variantPrototype.asObject()->prototype() == objectPrototype.asObject()->d()); + jsObjects[VariantProto] = memoryManager->alloc<VariantPrototype>(emptyClass, objectPrototype()); + Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); - sequencePrototype = ScopedValue(scope, memoryManager->alloc<SequencePrototype>(arrayClass, arrayPrototype.asObject())); + Scope scope(this); + jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->alloc<SequencePrototype>(arrayClass, arrayPrototype())); ScopedContext global(scope, rootContext()); - objectCtor = memoryManager->alloc<ObjectCtor>(global); - stringCtor = memoryManager->alloc<StringCtor>(global); - numberCtor = memoryManager->alloc<NumberCtor>(global); - booleanCtor = memoryManager->alloc<BooleanCtor>(global); - arrayCtor = memoryManager->alloc<ArrayCtor>(global); - functionCtor = memoryManager->alloc<FunctionCtor>(global); - dateCtor = memoryManager->alloc<DateCtor>(global); - regExpCtor = memoryManager->alloc<RegExpCtor>(global); - errorCtor = memoryManager->alloc<ErrorCtor>(global); - evalErrorCtor = memoryManager->alloc<EvalErrorCtor>(global); - rangeErrorCtor = memoryManager->alloc<RangeErrorCtor>(global); - referenceErrorCtor = memoryManager->alloc<ReferenceErrorCtor>(global); - syntaxErrorCtor = memoryManager->alloc<SyntaxErrorCtor>(global); - typeErrorCtor = memoryManager->alloc<TypeErrorCtor>(global); - uRIErrorCtor = memoryManager->alloc<URIErrorCtor>(global); - - static_cast<ObjectPrototype *>(objectPrototype.asObject())->init(this, objectCtor.asObject()); - static_cast<StringPrototype *>(stringPrototype.asObject())->init(this, stringCtor.asObject()); - static_cast<NumberPrototype *>(numberPrototype.asObject())->init(this, numberCtor.asObject()); - static_cast<BooleanPrototype *>(booleanPrototype.asObject())->init(this, booleanCtor.asObject()); - static_cast<ArrayPrototype *>(arrayPrototype.asObject())->init(this, arrayCtor.asObject()); - static_cast<DatePrototype *>(datePrototype.asObject())->init(this, dateCtor.asObject()); - static_cast<FunctionPrototype *>(functionPrototype.asObject())->init(this, functionCtor.asObject()); - static_cast<RegExpPrototype *>(regExpPrototype.asObject())->init(this, regExpCtor.asObject()); - static_cast<ErrorPrototype *>(errorPrototype.asObject())->init(this, errorCtor.asObject()); - static_cast<EvalErrorPrototype *>(evalErrorPrototype.asObject())->init(this, evalErrorCtor.asObject()); - static_cast<RangeErrorPrototype *>(rangeErrorPrototype.asObject())->init(this, rangeErrorCtor.asObject()); - static_cast<ReferenceErrorPrototype *>(referenceErrorPrototype.asObject())->init(this, referenceErrorCtor.asObject()); - static_cast<SyntaxErrorPrototype *>(syntaxErrorPrototype.asObject())->init(this, syntaxErrorCtor.asObject()); - static_cast<TypeErrorPrototype *>(typeErrorPrototype.asObject())->init(this, typeErrorCtor.asObject()); - static_cast<URIErrorPrototype *>(uRIErrorPrototype.asObject())->init(this, uRIErrorCtor.asObject()); - - static_cast<VariantPrototype *>(variantPrototype.asObject())->init(); - sequencePrototype.cast<SequencePrototype>()->init(); + jsObjects[Object_Ctor] = memoryManager->alloc<ObjectCtor>(global); + jsObjects[String_Ctor] = memoryManager->alloc<StringCtor>(global); + jsObjects[Number_Ctor] = memoryManager->alloc<NumberCtor>(global); + jsObjects[Boolean_Ctor] = memoryManager->alloc<BooleanCtor>(global); + jsObjects[Array_Ctor] = memoryManager->alloc<ArrayCtor>(global); + jsObjects[Function_Ctor] = memoryManager->alloc<FunctionCtor>(global); + jsObjects[Date_Ctor] = memoryManager->alloc<DateCtor>(global); + jsObjects[RegExp_Ctor] = memoryManager->alloc<RegExpCtor>(global); + jsObjects[Error_Ctor] = memoryManager->alloc<ErrorCtor>(global); + jsObjects[EvalError_Ctor] = memoryManager->alloc<EvalErrorCtor>(global); + jsObjects[RangeError_Ctor] = memoryManager->alloc<RangeErrorCtor>(global); + jsObjects[ReferenceError_Ctor] = memoryManager->alloc<ReferenceErrorCtor>(global); + jsObjects[SyntaxError_Ctor] = memoryManager->alloc<SyntaxErrorCtor>(global); + jsObjects[TypeError_Ctor] = memoryManager->alloc<TypeErrorCtor>(global); + jsObjects[URIError_Ctor] = memoryManager->alloc<URIErrorCtor>(global); + + static_cast<ObjectPrototype *>(objectPrototype())->init(this, objectCtor()); + static_cast<StringPrototype *>(stringPrototype())->init(this, stringCtor()); + static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor()); + static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor()); + static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor()); + static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor()); + static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor()); + static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor()); + static_cast<ErrorPrototype *>(errorPrototype())->init(this, errorCtor()); + static_cast<EvalErrorPrototype *>(evalErrorPrototype())->init(this, evalErrorCtor()); + static_cast<RangeErrorPrototype *>(rangeErrorPrototype())->init(this, rangeErrorCtor()); + static_cast<ReferenceErrorPrototype *>(referenceErrorPrototype())->init(this, referenceErrorCtor()); + static_cast<SyntaxErrorPrototype *>(syntaxErrorPrototype())->init(this, syntaxErrorCtor()); + static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor()); + static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor()); + + static_cast<VariantPrototype *>(variantPrototype())->init(); + sequencePrototype()->cast<SequencePrototype>()->init(); // typed arrays - arrayBufferCtor = memoryManager->alloc<ArrayBufferCtor>(global); - arrayBufferPrototype = memoryManager->alloc<ArrayBufferPrototype>(emptyClass, objectPrototype.asObject()); - static_cast<ArrayBufferPrototype *>(arrayBufferPrototype.asObject())->init(this, arrayBufferCtor.asObject()); + jsObjects[ArrayBuffer_Ctor] = memoryManager->alloc<ArrayBufferCtor>(global); + jsObjects[ArrayBufferProto] = memoryManager->alloc<ArrayBufferPrototype>(emptyClass, objectPrototype()); + static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor()); - dataViewCtor = memoryManager->alloc<DataViewCtor>(global); - dataViewPrototype = memoryManager->alloc<DataViewPrototype>(emptyClass, objectPrototype.asObject()); - static_cast<DataViewPrototype *>(dataViewPrototype.asObject())->init(this, dataViewCtor.asObject()); + jsObjects[DataView_Ctor] = memoryManager->alloc<DataViewCtor>(global); + jsObjects[DataViewProto] = memoryManager->alloc<DataViewPrototype>(emptyClass, objectPrototype()); + static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor()); + jsObjects[ValueTypeProto] = (Heap::Base *) 0; for (int i = 0; i < Heap::TypedArray::NTypes; ++i) { - typedArrayCtors[i] = memoryManager->alloc<TypedArrayCtor>(global, Heap::TypedArray::Type(i)); - typedArrayPrototype[i] = memoryManager->alloc<TypedArrayPrototype>(this, Heap::TypedArray::Type(i)); - typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].asObject())); + static_cast<Value &>(typedArrayCtors[i]) = memoryManager->alloc<TypedArrayCtor>(global, Heap::TypedArray::Type(i)); + static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->alloc<TypedArrayPrototype>(this, Heap::TypedArray::Type(i)); + typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>())); } // // set up the global object // - rootContext()->global = globalObject()->d(); - rootContext()->callData->thisObject = globalObject(); - Q_ASSERT(globalObject()->d()->vtable); - - globalObject()->defineDefaultProperty(QStringLiteral("Object"), objectCtor); - globalObject()->defineDefaultProperty(QStringLiteral("String"), stringCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Number"), numberCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Boolean"), booleanCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Array"), arrayCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Function"), functionCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Date"), dateCtor); - globalObject()->defineDefaultProperty(QStringLiteral("RegExp"), regExpCtor); - globalObject()->defineDefaultProperty(QStringLiteral("Error"), errorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("EvalError"), evalErrorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("RangeError"), rangeErrorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("ReferenceError"), referenceErrorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("SyntaxError"), syntaxErrorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("TypeError"), typeErrorCtor); - globalObject()->defineDefaultProperty(QStringLiteral("URIError"), uRIErrorCtor); - - globalObject()->defineDefaultProperty(QStringLiteral("ArrayBuffer"), arrayBufferCtor); - globalObject()->defineDefaultProperty(QStringLiteral("DataView"), dataViewCtor); + rootContext()->d()->global = globalObject->d(); + rootContext()->d()->callData->thisObject = globalObject; + Q_ASSERT(globalObject->d()->vtable()); + + globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor()); + globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Array"), *arrayCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Function"), *functionCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Date"), *dateCtor()); + globalObject->defineDefaultProperty(QStringLiteral("RegExp"), *regExpCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Error"), *errorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("EvalError"), *evalErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("RangeError"), *rangeErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("ReferenceError"), *referenceErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("SyntaxError"), *syntaxErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("TypeError"), *typeErrorCtor()); + globalObject->defineDefaultProperty(QStringLiteral("URIError"), *uRIErrorCtor()); + + globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor()); + globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor()); ScopedString str(scope); for (int i = 0; i < Heap::TypedArray::NTypes; ++i) - globalObject()->defineDefaultProperty((str = typedArrayCtors[i].asFunctionObject()->name())->toQString(), typedArrayCtors[i]); + globalObject->defineDefaultProperty((str = typedArrayCtors[i].as<FunctionObject>()->name())->toQString(), typedArrayCtors[i]); ScopedObject o(scope); - globalObject()->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->alloc<MathObject>(this))); - globalObject()->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->alloc<JsonObject>(this))); + globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->alloc<MathObject>(this))); + globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->alloc<JsonObject>(this))); - globalObject()->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue()); - globalObject()->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN())); - globalObject()->defineReadonlyProperty(QStringLiteral("Infinity"), Primitive::fromDouble(Q_INFINITY)); + globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue()); + globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN())); + globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Primitive::fromDouble(Q_INFINITY)); - evalFunction = memoryManager->alloc<EvalFunction>(global); - globalObject()->defineDefaultProperty(QStringLiteral("eval"), (o = evalFunction)); + jsObjects[Eval_Function] = memoryManager->alloc<EvalFunction>(global); + globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction()); - globalObject()->defineDefaultProperty(QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); - globalObject()->defineDefaultProperty(QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); - globalObject()->defineDefaultProperty(QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); - globalObject()->defineDefaultProperty(QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); - globalObject()->defineDefaultProperty(QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); - globalObject()->defineDefaultProperty(QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); - globalObject()->defineDefaultProperty(QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); - globalObject()->defineDefaultProperty(QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); - globalObject()->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1); - globalObject()->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); + globalObject->defineDefaultProperty(QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); + globalObject->defineDefaultProperty(QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); + globalObject->defineDefaultProperty(QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); + globalObject->defineDefaultProperty(QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); + globalObject->defineDefaultProperty(QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); + globalObject->defineDefaultProperty(QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); + globalObject->defineDefaultProperty(QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); + globalObject->defineDefaultProperty(QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); + globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1); + globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); ScopedString name(scope, newString(QStringLiteral("thrower"))); - thrower = BuiltinFunction::create(global, name, ::throwTypeError); + jsObjects[ThrowerObject] = BuiltinFunction::create(global, name, ::throwTypeError); } ExecutionEngine::~ExecutionEngine() @@ -471,7 +474,6 @@ ExecutionEngine::~ExecutionEngine() foreach (QV4::CompiledData::CompilationUnit *unit, remainingUnits) unit->unlink(); - delete m_qmlExtensions; emptyClass->destroy(); delete classPool; delete bumperPointerAllocator; @@ -504,10 +506,10 @@ void ExecutionEngine::initRootContext() r->d()->callData = reinterpret_cast<CallData *>(r->d() + 1); r->d()->callData->tag = QV4::Value::_Integer_Type; r->d()->callData->argc = 0; - r->d()->callData->thisObject = globalObject(); + r->d()->callData->thisObject = globalObject; r->d()->callData->args[0] = Encode::undefined(); - m_rootContext = r->d(); + jsObjects[RootContect] = r; } InternalClass *ExecutionEngine::newClass(const InternalClass &other) @@ -519,7 +521,7 @@ Heap::ExecutionContext *ExecutionEngine::pushGlobalContext() { Scope scope(this); Scoped<GlobalContext> g(scope, memoryManager->alloc<GlobalContext>(this)); - g->d()->callData = rootContext()->callData; + g->d()->callData = rootContext()->d()->callData; Q_ASSERT(currentContext() == g->d()); return g->d(); @@ -551,10 +553,10 @@ Heap::String *ExecutionEngine::newIdentifier(const QString &text) return identifierTable->insertString(text); } -Heap::Object *ExecutionEngine::newStringObject(const Value &value) +Heap::Object *ExecutionEngine::newStringObject(const String *string) { Scope scope(this); - Scoped<StringObject> object(scope, memoryManager->alloc<StringObject>(this, value)); + Scoped<StringObject> object(scope, memoryManager->alloc<StringObject>(this, string)); return object->d(); } @@ -653,7 +655,7 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) Heap::Object *ExecutionEngine::newErrorObject(const Value &value) { Scope scope(this); - ScopedObject object(scope, memoryManager->alloc<ErrorObject>(emptyClass, errorPrototype.asObject(), value)); + ScopedObject object(scope, memoryManager->alloc<ErrorObject>(emptyClass, errorPrototype(), value)); return object->d(); } @@ -723,7 +725,7 @@ Heap::Object *ExecutionEngine::newForEachIteratorObject(Object *o) return obj->d(); } -Heap::Object *ExecutionEngine::qmlContextObject() const +Heap::QmlContext *ExecutionEngine::qmlContext() const { Heap::ExecutionContext *ctx = currentContext(); @@ -740,8 +742,28 @@ Heap::Object *ExecutionEngine::qmlContextObject() const if (ctx->type != Heap::ExecutionContext::Type_QmlContext) return 0; - Q_ASSERT(static_cast<Heap::CallContext *>(ctx)->activation); - return static_cast<Heap::CallContext *>(ctx)->activation; + return static_cast<Heap::QmlContext *>(ctx); +} + +Heap::QmlContextWrapper *ExecutionEngine::qmlContextObject() const +{ + Heap::QmlContext *ctx = qmlContext(); + if (!ctx) + return 0; + Q_ASSERT(ctx->qml); + return ctx->qml; +} + +QObject *ExecutionEngine::qmlScopeObject() const +{ + return qmlContextObject()->scopeObject; +} + +QQmlContextData *ExecutionEngine::callingQmlContext() const +{ + Heap::QmlContextWrapper *w = qmlContextObject(); + + return w ? w->context.contextData() : 0; } QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const @@ -777,7 +799,7 @@ QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const StackFrame frame; frame.source = globalCode->sourceFile(); frame.function = globalCode->name()->toQString(); - frame.line = rootContext()->lineNumber; + frame.line = rootContext()->d()->lineNumber; frame.column = -1; stack.append(frame); @@ -889,8 +911,6 @@ void ExecutionEngine::markObjects() { identifierTable->mark(this); - globalObject()->mark(this); - for (int i = 0; i < nArgumentsAccessors; ++i) { const Property &pd = argumentsAccessors[i]; if (Heap::FunctionObject *getter = pd.getter()) @@ -904,98 +924,11 @@ void ExecutionEngine::markObjects() Q_ASSERT(c->inUse()); if (!c->isMarked()) { c->setMarkBit(); - c->gcGetVtable()->markObjects(c, this); + c->vtable()->markObjects(c, this); } c = c->parent; } - id_empty->mark(this); - id_undefined->mark(this); - id_null->mark(this); - id_true->mark(this); - id_false->mark(this); - id_boolean->mark(this); - id_number->mark(this); - id_string->mark(this); - id_object->mark(this); - id_function->mark(this); - id_length->mark(this); - id_prototype->mark(this); - id_constructor->mark(this); - id_arguments->mark(this); - id_caller->mark(this); - id_callee->mark(this); - id_this->mark(this); - id___proto__->mark(this); - id_enumerable->mark(this); - id_configurable->mark(this); - id_writable->mark(this); - id_value->mark(this); - id_get->mark(this); - id_set->mark(this); - id_eval->mark(this); - id_uintMax->mark(this); - id_name->mark(this); - id_index->mark(this); - id_input->mark(this); - id_toString->mark(this); - id_destroy->mark(this); - id_valueOf->mark(this); - id_byteLength->mark(this); - id_byteOffset->mark(this); - id_buffer->mark(this); - id_lastIndex->mark(this); - - objectCtor.mark(this); - stringCtor.mark(this); - numberCtor.mark(this); - booleanCtor.mark(this); - arrayCtor.mark(this); - functionCtor.mark(this); - dateCtor.mark(this); - regExpCtor.mark(this); - errorCtor.mark(this); - evalErrorCtor.mark(this); - rangeErrorCtor.mark(this); - referenceErrorCtor.mark(this); - syntaxErrorCtor.mark(this); - typeErrorCtor.mark(this); - uRIErrorCtor.mark(this); - arrayBufferCtor.mark(this); - dataViewCtor.mark(this); - for (int i = 0; i < Heap::TypedArray::NTypes; ++i) - typedArrayCtors[i].mark(this); - - objectPrototype.mark(this); - arrayPrototype.mark(this); - stringPrototype.mark(this); - numberPrototype.mark(this); - booleanPrototype.mark(this); - datePrototype.mark(this); - functionPrototype.mark(this); - regExpPrototype.mark(this); - errorPrototype.mark(this); - evalErrorPrototype.mark(this); - rangeErrorPrototype.mark(this); - referenceErrorPrototype.mark(this); - syntaxErrorPrototype.mark(this); - typeErrorPrototype.mark(this); - uRIErrorPrototype.mark(this); - variantPrototype.mark(this); - sequencePrototype.mark(this); - - arrayBufferPrototype.mark(this); - dataViewPrototype.mark(this); - for (int i = 0; i < Heap::TypedArray::NTypes; ++i) - typedArrayPrototype[i].mark(this); - - exceptionValue.mark(this); - - thrower->mark(this); - - if (m_qmlExtensions) - m_qmlExtensions->markObjects(this); - classPool->markObjects(this); for (QSet<CompiledData::CompilationUnit*>::ConstIterator it = compilationUnits.constBegin(), end = compilationUnits.constEnd(); @@ -1003,13 +936,6 @@ void ExecutionEngine::markObjects() (*it)->markObjects(this); } -QmlExtensions *ExecutionEngine::qmlExtensions() -{ - if (!m_qmlExtensions) - m_qmlExtensions = new QmlExtensions; - return m_qmlExtensions; -} - ReturnedValue ExecutionEngine::throwError(const Value &value) { // we can get in here with an exception already set, as the runtime @@ -1020,7 +946,7 @@ ReturnedValue ExecutionEngine::throwError(const Value &value) return Encode::undefined(); hasException = true; - exceptionValue = value; + *exceptionValue = value; QV4::Scope scope(this); QV4::Scoped<ErrorObject> error(scope, value); if (!!error) @@ -1041,8 +967,8 @@ ReturnedValue ExecutionEngine::catchException(StackTrace *trace) *trace = exceptionStackTrace; exceptionStackTrace.clear(); hasException = false; - ReturnedValue res = exceptionValue.asReturnedValue(); - exceptionValue = Primitive::emptyValue(); + ReturnedValue res = exceptionValue->asReturnedValue(); + *exceptionValue = Primitive::emptyValue(); return res; } @@ -1175,7 +1101,7 @@ bool ExecutionEngine::recheckCStackLimits() typedef QSet<QV4::Heap::Object *> V4ObjectSet; static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects); static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value); -static QVariant objectToVariant(QV4::ExecutionEngine *e, QV4::Object *o, V4ObjectSet *visitedObjects = 0); +static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects = 0); static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value, const QByteArray &targetType, void **result); @@ -1198,7 +1124,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int Q_ASSERT (!value.isEmpty()); QV4::Scope scope(e); - if (QV4::VariantObject *v = value.as<QV4::VariantObject>()) + if (const QV4::VariantObject *v = value.as<QV4::VariantObject>()) return v->d()->data; if (typeHint == QVariant::Bool) @@ -1210,10 +1136,10 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (typeHint == qMetaTypeId<QJSValue>()) return QVariant::fromValue(QJSValue(e, value.asReturnedValue())); - if (value.asObject()) { + if (value.as<Object>()) { QV4::ScopedObject object(scope, value); if (typeHint == QMetaType::QJsonObject - && !value.asArrayObject() && !value.asFunctionObject()) { + && !value.as<ArrayObject>() && !value.as<FunctionObject>()) { return QVariant::fromValue(QV4::JsonObject::toJsonObject(object)); } else if (QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>()) { return qVariantFromValue<QObject *>(wrapper->object()); @@ -1229,7 +1155,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return QV4::SequencePrototype::toVariant(object); } - if (value.asArrayObject()) { + if (value.as<ArrayObject>()) { QV4::ScopedArrayObject a(scope, value); if (typeHint == qMetaTypeId<QList<QObject *> >()) { QList<QObject *> list; @@ -1267,11 +1193,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return value.asDouble(); if (value.isString()) return value.stringValue()->toQString(); - if (QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>()) + if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>()) return ld->d()->locale; - if (QV4::DateObject *d = value.asDateObject()) + if (const QV4::DateObject *d = value.as<DateObject>()) return d->toQDateTime(); - if (QV4::ArrayBuffer *d = value.as<ArrayBuffer>()) + if (const QV4::ArrayBuffer *d = value.as<ArrayBuffer>()) return d->asByteArray(); // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)! @@ -1287,7 +1213,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return objectToVariant(e, o, visitedObjects); } -static QVariant objectToVariant(QV4::ExecutionEngine *e, QV4::Object *o, V4ObjectSet *visitedObjects) +static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects) { Q_ASSERT(o); @@ -1298,7 +1224,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, QV4::Object *o, V4Objec // Avoid recursion. // For compatibility with QVariant{List,Map} conversion, we return an // empty object (and no error is thrown). - if (o->asArrayObject()) + if (o->as<ArrayObject>()) return QVariantList(); return QVariantMap(); } @@ -1306,7 +1232,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, QV4::Object *o, V4Objec QVariant result; - if (o->asArrayObject()) { + if (o->as<ArrayObject>()) { QV4::Scope scope(e); QV4::ScopedArrayObject a(scope, o->asReturnedValue()); QV4::ScopedValue v(scope); @@ -1319,7 +1245,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, QV4::Object *o, V4Objec } result = list; - } else if (!o->asFunctionObject()) { + } else if (!o->as<FunctionObject>()) { QVariantMap map; QV4::Scope scope(e); QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); @@ -1497,7 +1423,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode(newVariantObject(variant)); } -QVariantMap ExecutionEngine::variantMapFromJS(Object *o) +QVariantMap ExecutionEngine::variantMapFromJS(const Object *o) { return objectToVariant(this, o).toMap(); } @@ -1630,93 +1556,90 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return 0; } -void ExecutionEngine::assertObjectBelongsToEngine(const Value &v) +void ExecutionEngine::assertObjectBelongsToEngine(const Heap::Base &baseObject) { - Q_UNUSED(v); - Q_ASSERT(!v.isObject() || v.objectValue()->engine() == this); - Q_UNUSED(v); + Q_ASSERT(!baseObject.vtable()->isObject || static_cast<const Heap::Object&>(baseObject).internalClass->engine == this); + Q_UNUSED(baseObject); } // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. -bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *data) +bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) { - QV4::Scope scope(this); - // check if it's one of the types we know switch (QMetaType::Type(type)) { case QMetaType::Bool: - *reinterpret_cast<bool*>(data) = value.toBoolean(); + *reinterpret_cast<bool*>(data) = value->toBoolean(); return true; case QMetaType::Int: - *reinterpret_cast<int*>(data) = value.toInt32(); + *reinterpret_cast<int*>(data) = value->toInt32(); return true; case QMetaType::UInt: - *reinterpret_cast<uint*>(data) = value.toUInt32(); + *reinterpret_cast<uint*>(data) = value->toUInt32(); return true; case QMetaType::LongLong: - *reinterpret_cast<qlonglong*>(data) = qlonglong(value.toInteger()); + *reinterpret_cast<qlonglong*>(data) = qlonglong(value->toInteger()); return true; case QMetaType::ULongLong: - *reinterpret_cast<qulonglong*>(data) = qulonglong(value.toInteger()); + *reinterpret_cast<qulonglong*>(data) = qulonglong(value->toInteger()); return true; case QMetaType::Double: - *reinterpret_cast<double*>(data) = value.toNumber(); + *reinterpret_cast<double*>(data) = value->toNumber(); return true; case QMetaType::QString: - if (value.isUndefined() || value.isNull()) + if (value->isUndefined() || value->isNull()) *reinterpret_cast<QString*>(data) = QString(); else - *reinterpret_cast<QString*>(data) = value.toQString(); + *reinterpret_cast<QString*>(data) = value->toQString(); return true; case QMetaType::Float: - *reinterpret_cast<float*>(data) = value.toNumber(); + *reinterpret_cast<float*>(data) = value->toNumber(); return true; case QMetaType::Short: - *reinterpret_cast<short*>(data) = short(value.toInt32()); + *reinterpret_cast<short*>(data) = short(value->toInt32()); return true; case QMetaType::UShort: - *reinterpret_cast<unsigned short*>(data) = value.toUInt16(); + *reinterpret_cast<unsigned short*>(data) = value->toUInt16(); return true; case QMetaType::Char: - *reinterpret_cast<char*>(data) = char(value.toInt32()); + *reinterpret_cast<char*>(data) = char(value->toInt32()); return true; case QMetaType::UChar: - *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value.toInt32()); + *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->toInt32()); return true; case QMetaType::QChar: - if (value.isString()) { - QString str = value.stringValue()->toQString(); + if (value->isString()) { + QString str = value->stringValue()->toQString(); *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0); } else { - *reinterpret_cast<QChar*>(data) = QChar(ushort(value.toUInt16())); + *reinterpret_cast<QChar*>(data) = QChar(ushort(value->toUInt16())); } return true; case QMetaType::QDateTime: - if (QV4::DateObject *d = value.asDateObject()) { + if (const QV4::DateObject *d = value->as<DateObject>()) { *reinterpret_cast<QDateTime *>(data) = d->toQDateTime(); return true; } break; case QMetaType::QDate: - if (QV4::DateObject *d = value.asDateObject()) { + if (const QV4::DateObject *d = value->as<DateObject>()) { *reinterpret_cast<QDate *>(data) = d->toQDateTime().date(); return true; } break; case QMetaType::QRegExp: - if (QV4::RegExpObject *r = value.as<QV4::RegExpObject>()) { + if (const QV4::RegExpObject *r = value->as<QV4::RegExpObject>()) { *reinterpret_cast<QRegExp *>(data) = r->toQRegExp(); return true; } break; case QMetaType::QObjectStar: { - QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>(); - if (qobjectWrapper || value.isNull()) { - *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(scope.engine, value); + const QV4::QObjectWrapper *qobjectWrapper = value->as<QV4::QObjectWrapper>(); + if (qobjectWrapper || value->isNull()) { + *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(this, *value); return true; } break; } case QMetaType::QStringList: { - QV4::ScopedArrayObject a(scope, value); + const QV4::ArrayObject *a = value->as<QV4::ArrayObject>(); if (a) { *reinterpret_cast<QStringList *>(data) = a->toQStringList(); return true; @@ -1724,15 +1647,15 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da break; } case QMetaType::QVariantList: { - QV4::ScopedArrayObject a(scope, value); + const QV4::ArrayObject *a = value->as<QV4::ArrayObject>(); if (a) { - *reinterpret_cast<QVariantList *>(data) = scope.engine->toVariant(a, /*typeHint*/-1, /*createJSValueForObjects*/false).toList(); + *reinterpret_cast<QVariantList *>(data) = toVariant(*a, /*typeHint*/-1, /*createJSValueForObjects*/false).toList(); return true; } break; } case QMetaType::QVariantMap: { - QV4::ScopedObject o(scope, value); + const QV4::Object *o = value->as<QV4::Object>(); if (o) { *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(o); return true; @@ -1740,20 +1663,19 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da break; } case QMetaType::QVariant: - *reinterpret_cast<QVariant*>(data) = scope.engine->toVariant(value, /*typeHint*/-1, /*createJSValueForObjects*/false); + *reinterpret_cast<QVariant*>(data) = toVariant(*value, /*typeHint*/-1, /*createJSValueForObjects*/false); return true; case QMetaType::QJsonValue: - *reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(value); + *reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(*value); return true; case QMetaType::QJsonObject: { - QV4::ScopedObject o(scope, value); - *reinterpret_cast<QJsonObject *>(data) = QV4::JsonObject::toJsonObject(o); + *reinterpret_cast<QJsonObject *>(data) = QV4::JsonObject::toJsonObject(value->as<Object>()); return true; } case QMetaType::QJsonArray: { - QV4::ScopedArrayObject a(scope, value); + const QV4::ArrayObject *a = value->as<ArrayObject>(); if (a) { - *reinterpret_cast<QJsonArray *>(data) = QV4::JsonObject::toJsonArray(a); + *reinterpret_cast<QJsonArray *>(data) = JsonObject::toJsonArray(a); return true; } break; @@ -1763,7 +1685,7 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da } { - QV4::Scoped<QV4::QQmlValueTypeWrapper> vtw(scope, value); + const QQmlValueTypeWrapper *vtw = value->as<QQmlValueTypeWrapper>(); if (vtw && vtw->typeId() == type) { return vtw->toGadget(data); } @@ -1788,21 +1710,22 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da } #endif - // Try to use magic; for compatibility with qscriptvalue_cast. + // Try to use magic; for compatibility with qjsvalue_cast. QByteArray name = QMetaType::typeName(type); - if (convertToNativeQObject(this, value, name, reinterpret_cast<void* *>(data))) + if (convertToNativeQObject(this, *value, name, reinterpret_cast<void* *>(data))) return true; - if (value.as<QV4::VariantObject>() && name.endsWith('*')) { + if (value->as<QV4::VariantObject>() && name.endsWith('*')) { int valueType = QMetaType::type(name.left(name.size()-1)); - QVariant &var = value.as<QV4::VariantObject>()->d()->data; + QVariant &var = value->as<QV4::VariantObject>()->d()->data; if (valueType == var.userType()) { // We have T t, T* is requested, so return &t. *reinterpret_cast<void* *>(data) = var.data(); return true; - } else if (value.isObject()) { + } else if (value->isObject()) { // Look in the prototype chain. - QV4::ScopedObject proto(scope, value.objectValue()->prototype()); + QV4::Scope scope(this); + QV4::ScopedObject proto(scope, value->objectValue()->prototype()); while (proto) { bool canCast = false; if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) { @@ -1812,7 +1735,7 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da else if (proto->as<QV4::QObjectWrapper>()) { QByteArray className = name.left(name.size()-1); QV4::ScopedObject p(scope, proto.getPointer()); - if (QObject *qobject = qtObjectFromJS(scope.engine, p)) + if (QObject *qobject = qtObjectFromJS(this, p)) canCast = qobject->qt_metacast(className) != 0; } if (canCast) { @@ -1826,11 +1749,11 @@ bool ExecutionEngine::metaTypeFromJS(const QV4::Value &value, int type, void *da proto = proto->prototype(); } } - } else if (value.isNull() && name.endsWith('*')) { + } else if (value->isNull() && name.endsWith('*')) { *reinterpret_cast<void* *>(data) = 0; return true; } else if (type == qMetaTypeId<QJSValue>()) { - *reinterpret_cast<QJSValue*>(data) = QJSValue(this, value.asReturnedValue()); + *reinterpret_cast<QJSValue*>(data) = QJSValue(this, value->asReturnedValue()); return true; } diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index c0c88abaa5..9f44745e4c 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -37,6 +37,7 @@ #include "private/qv4isel_p.h" #include "qv4managed_p.h" #include "qv4context_p.h" +#include "qv4internalclass_p.h" #include <private/qintrusivelist_p.h> namespace WTF { @@ -50,6 +51,7 @@ class QV8Engine; class QQmlError; class QJSEngine; class QQmlEngine; +class QQmlContextData; namespace QV4 { namespace Debugging { @@ -80,8 +82,6 @@ public: Value *jsStackTop; quint32 hasException; - Heap::GlobalContext *m_rootContext; - Heap::GlobalContext *rootContext() const { return m_rootContext; } MemoryManager *memoryManager; ExecutableAllocator *executableAllocator; @@ -106,14 +106,19 @@ public: --jsStackTop; return jsStackTop->heapObject(); } + Value *jsAlloca(int nValues) { + Value *ptr = jsStackTop; + jsStackTop = ptr + nValues; + memset(ptr, 0, nValues*sizeof(Value)); + return ptr; + } IdentifierTable *identifierTable; QV4::Debugging::Debugger *debugger; QV4::Profiling::Profiler *profiler; - Value m_globalObject; - Object *globalObject() { return reinterpret_cast<Object *>(&m_globalObject); } + Object *globalObject; Function *globalCode; @@ -121,47 +126,97 @@ public: QQmlEngine *qmlEngine() const; QV8Engine *v8Engine; - Value objectCtor; - Value stringCtor; - Value numberCtor; - Value booleanCtor; - Value arrayCtor; - Value functionCtor; - Value dateCtor; - Value regExpCtor; - Value errorCtor; - Value evalErrorCtor; - Value rangeErrorCtor; - Value referenceErrorCtor; - Value syntaxErrorCtor; - Value typeErrorCtor; - Value uRIErrorCtor; - Value arrayBufferCtor; - Value dataViewCtor; - enum { NTypedArrayTypes = 9 }; // avoid header dependency - Value typedArrayCtors[NTypedArrayTypes]; - - Value objectPrototype; - Value arrayPrototype; - Value stringPrototype; - Value numberPrototype; - Value booleanPrototype; - Value datePrototype; - Value functionPrototype; - Value regExpPrototype; - Value errorPrototype; - Value evalErrorPrototype; - Value rangeErrorPrototype; - Value referenceErrorPrototype; - Value syntaxErrorPrototype; - Value typeErrorPrototype; - Value uRIErrorPrototype; - Value variantPrototype; - Value sequencePrototype; - - Value arrayBufferPrototype; - Value dataViewPrototype; - Value typedArrayPrototype[NTypedArrayTypes]; // TypedArray::NValues, avoid including the header here + enum JSObjects { + RootContect, + ObjectProto, + ArrayProto, + StringProto, + NumberProto, + BooleanProto, + DateProto, + FunctionProto, + RegExpProto, + ErrorProto, + EvalErrorProto, + RangeErrorProto, + ReferenceErrorProto, + SyntaxErrorProto, + TypeErrorProto, + URIErrorProto, + VariantProto, + SequenceProto, + ArrayBufferProto, + DataViewProto, + ValueTypeProto, + + Object_Ctor, + String_Ctor, + Number_Ctor, + Boolean_Ctor, + Array_Ctor, + Function_Ctor, + Date_Ctor, + RegExp_Ctor, + Error_Ctor, + EvalError_Ctor, + RangeError_Ctor, + ReferenceError_Ctor, + SyntaxError_Ctor, + TypeError_Ctor, + URIError_Ctor, + ArrayBuffer_Ctor, + DataView_Ctor, + + Eval_Function, + ThrowerObject, + NJSObjects + }; + Value *jsObjects; + enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency + + GlobalContext *rootContext() const { return reinterpret_cast<GlobalContext *>(jsObjects + RootContect); } + FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); } + FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); } + FunctionObject *numberCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Number_Ctor); } + FunctionObject *booleanCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Boolean_Ctor); } + FunctionObject *arrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Array_Ctor); } + FunctionObject *functionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Function_Ctor); } + FunctionObject *dateCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Date_Ctor); } + FunctionObject *regExpCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + RegExp_Ctor); } + FunctionObject *errorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Error_Ctor); } + FunctionObject *evalErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + EvalError_Ctor); } + FunctionObject *rangeErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + RangeError_Ctor); } + FunctionObject *referenceErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ReferenceError_Ctor); } + FunctionObject *syntaxErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + SyntaxError_Ctor); } + FunctionObject *typeErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + TypeError_Ctor); } + FunctionObject *uRIErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + URIError_Ctor); } + FunctionObject *arrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ArrayBuffer_Ctor); } + FunctionObject *dataViewCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + DataView_Ctor); } + FunctionObject *typedArrayCtors; + + Object *objectPrototype() const { return reinterpret_cast<Object *>(jsObjects + ObjectProto); } + Object *arrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayProto); } + 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); } + Object *datePrototype() const { return reinterpret_cast<Object *>(jsObjects + DateProto); } + Object *functionPrototype() const { return reinterpret_cast<Object *>(jsObjects + FunctionProto); } + Object *regExpPrototype() const { return reinterpret_cast<Object *>(jsObjects + RegExpProto); } + Object *errorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ErrorProto); } + Object *evalErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + EvalErrorProto); } + Object *rangeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + RangeErrorProto); } + Object *referenceErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ReferenceErrorProto); } + Object *syntaxErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + SyntaxErrorProto); } + Object *typeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeErrorProto); } + Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); } + Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); } + Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); } + + Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); } + Object *dataViewPrototype() const { return reinterpret_cast<Object *>(jsObjects + DataViewProto); } + Object *typedArrayPrototype; + + Object *valueTypeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + ValueTypeProto); } InternalClassPool *classPool; InternalClass *emptyClass; @@ -177,48 +232,89 @@ public: InternalClass *argumentsObjectClass; InternalClass *strictArgumentsObjectClass; - Heap::EvalFunction *evalFunction; - Heap::FunctionObject *thrower; + EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); } + FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); } Property *argumentsAccessors; int nArgumentsAccessors; - StringValue id_empty; - StringValue id_undefined; - StringValue id_null; - StringValue id_true; - StringValue id_false; - StringValue id_boolean; - StringValue id_number; - StringValue id_string; - StringValue id_object; - StringValue id_function; - StringValue id_length; - StringValue id_prototype; - StringValue id_constructor; - StringValue id_arguments; - StringValue id_caller; - StringValue id_callee; - StringValue id_this; - StringValue id___proto__; - StringValue id_enumerable; - StringValue id_configurable; - StringValue id_writable; - StringValue id_value; - StringValue id_get; - StringValue id_set; - StringValue id_eval; - StringValue id_uintMax; - StringValue id_name; - StringValue id_index; - StringValue id_input; - StringValue id_toString; - StringValue id_destroy; - StringValue id_valueOf; - StringValue id_byteLength; - StringValue id_byteOffset; - StringValue id_buffer; - StringValue id_lastIndex; + enum JSStrings { + String_Empty, + String_undefined, + String_null, + String_true, + String_false, + String_boolean, + String_number, + String_string, + String_object, + String_function, + String_length, + String_prototype, + String_constructor, + String_arguments, + String_caller, + String_callee, + String_this, + String___proto__, + String_enumerable, + String_configurable, + String_writable, + String_value, + String_get, + String_set, + String_eval, + String_uintMax, + String_name, + String_index, + String_input, + String_toString, + String_destroy, + String_valueOf, + String_byteLength, + String_byteOffset, + String_buffer, + String_lastIndex, + NJSStrings + }; + Value *jsStrings; + + String *id_empty() const { return reinterpret_cast<String *>(jsStrings + String_Empty); } + String *id_undefined() const { return reinterpret_cast<String *>(jsStrings + String_undefined); } + String *id_null() const { return reinterpret_cast<String *>(jsStrings + String_null); } + String *id_true() const { return reinterpret_cast<String *>(jsStrings + String_true); } + String *id_false() const { return reinterpret_cast<String *>(jsStrings + String_false); } + String *id_boolean() const { return reinterpret_cast<String *>(jsStrings + String_boolean); } + String *id_number() const { return reinterpret_cast<String *>(jsStrings + String_number); } + String *id_string() const { return reinterpret_cast<String *>(jsStrings + String_string); } + String *id_object() const { return reinterpret_cast<String *>(jsStrings + String_object); } + String *id_function() const { return reinterpret_cast<String *>(jsStrings + String_function); } + String *id_length() const { return reinterpret_cast<String *>(jsStrings + String_length); } + String *id_prototype() const { return reinterpret_cast<String *>(jsStrings + String_prototype); } + String *id_constructor() const { return reinterpret_cast<String *>(jsStrings + String_constructor); } + String *id_arguments() const { return reinterpret_cast<String *>(jsStrings + String_arguments); } + String *id_caller() const { return reinterpret_cast<String *>(jsStrings + String_caller); } + String *id_callee() const { return reinterpret_cast<String *>(jsStrings + String_callee); } + String *id_this() const { return reinterpret_cast<String *>(jsStrings + String_this); } + String *id___proto__() const { return reinterpret_cast<String *>(jsStrings + String___proto__); } + String *id_enumerable() const { return reinterpret_cast<String *>(jsStrings + String_enumerable); } + String *id_configurable() const { return reinterpret_cast<String *>(jsStrings + String_configurable); } + String *id_writable() const { return reinterpret_cast<String *>(jsStrings + String_writable); } + String *id_value() const { return reinterpret_cast<String *>(jsStrings + String_value); } + String *id_get() const { return reinterpret_cast<String *>(jsStrings + String_get); } + String *id_set() const { return reinterpret_cast<String *>(jsStrings + String_set); } + String *id_eval() const { return reinterpret_cast<String *>(jsStrings + String_eval); } + String *id_uintMax() const { return reinterpret_cast<String *>(jsStrings + String_uintMax); } + String *id_name() const { return reinterpret_cast<String *>(jsStrings + String_name); } + String *id_index() const { return reinterpret_cast<String *>(jsStrings + String_index); } + String *id_input() const { return reinterpret_cast<String *>(jsStrings + String_input); } + String *id_toString() const { return reinterpret_cast<String *>(jsStrings + String_toString); } + String *id_destroy() const { return reinterpret_cast<String *>(jsStrings + String_destroy); } + String *id_valueOf() const { return reinterpret_cast<String *>(jsStrings + String_valueOf); } + String *id_byteLength() const { return reinterpret_cast<String *>(jsStrings + String_byteLength); } + String *id_byteOffset() const { return reinterpret_cast<String *>(jsStrings + String_byteOffset); } + String *id_buffer() const { return reinterpret_cast<String *>(jsStrings + String_buffer); } + String *id_lastIndex() const { return reinterpret_cast<String *>(jsStrings + String_lastIndex); } QSet<CompiledData::CompilationUnit*> compilationUnits; @@ -261,7 +357,7 @@ public: Heap::String *newString(const QString &s = QString()); Heap::String *newIdentifier(const QString &text); - Heap::Object *newStringObject(const Value &value); + Heap::Object *newStringObject(const String *string); Heap::Object *newNumberObject(double value); Heap::Object *newBooleanObject(bool b); @@ -291,7 +387,11 @@ public: Heap::Object *newForEachIteratorObject(Object *o); - Heap::Object *qmlContextObject() const; + Heap::QmlContext *qmlContext() const; + QV4::Heap::QmlContextWrapper *qmlContextObject() const; + QObject *qmlScopeObject() const; + QQmlContextData *callingQmlContext() const; + StackTrace stackTrace(int frameLimit = -1) const; StackFrame currentStackFrame() const; @@ -305,12 +405,10 @@ public: InternalClass *newClass(const InternalClass &other); - QmlExtensions *qmlExtensions(); - bool recheckCStackLimits(); // Exception handling - Value exceptionValue; + Value *exceptionValue; StackTrace exceptionStackTrace; ReturnedValue throwError(const Value &value); @@ -335,15 +433,12 @@ public: QVariant toVariant(const QV4::Value &value, int typeHint, bool createJSValueForObjects = true); QV4::ReturnedValue fromVariant(const QVariant &); - QVariantMap variantMapFromJS(QV4::Object *o); + QVariantMap variantMapFromJS(const QV4::Object *o); - bool metaTypeFromJS(const Value &value, int type, void *data); + bool metaTypeFromJS(const Value *value, int type, void *data); QV4::ReturnedValue metaTypeToJS(int type, const void *data); - void assertObjectBelongsToEngine(const Value &v); - -private: - QmlExtensions *m_qmlExtensions; + void assertObjectBelongsToEngine(const Heap::Base &baseObject); }; inline void ExecutionEngine::pushContext(CallContext *context) @@ -376,29 +471,26 @@ Heap::ExecutionContext::ExecutionContext(ExecutionEngine *engine, ContextType t) } -// ### Remove me inline -void Managed::mark(QV4::ExecutionEngine *engine) +void Heap::Base::mark(QV4::ExecutionEngine *engine) { Q_ASSERT(inUse()); - if (markBit()) + if (isMarked()) return; #ifndef QT_NO_DEBUG engine->assertObjectBelongsToEngine(*this); #endif - d()->setMarkBit(); - engine->pushForGC(d()); + setMarkBit(); + engine->pushForGC(this); } - -inline -void Heap::Base::mark(QV4::ExecutionEngine *engine) +inline void Value::mark(ExecutionEngine *e) { - Q_ASSERT(inUse()); - if (isMarked()) + if (!_val) return; - setMarkBit(); - engine->pushForGC(this); + Managed *m = as<Managed>(); + if (m) + m->d()->mark(e); } diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 9034dee6a0..a6c2a25b91 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -33,11 +33,14 @@ #include "qv4errorobject_p.h" -#include "qv4mm_p.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> #include <QtCore/QDateTime> #include <QtCore/QStringList> #include <QtCore/QDebug> +#include "qv4string_p.h" +#include <private/qv4mm_p.h> #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> @@ -179,57 +182,57 @@ DEFINE_OBJECT_VTABLE(ErrorObject); DEFINE_OBJECT_VTABLE(SyntaxErrorObject); Heap::SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const Value &msg) - : Heap::ErrorObject(engine->emptyClass, engine->syntaxErrorPrototype.asObject(), msg, SyntaxError) + : Heap::ErrorObject(engine->emptyClass, engine->syntaxErrorPrototype(), msg, SyntaxError) { } Heap::SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber, int columnNumber) - : Heap::ErrorObject(engine->emptyClass, engine->syntaxErrorPrototype.asObject(), msg, fileName, lineNumber, columnNumber, SyntaxError) + : Heap::ErrorObject(engine->emptyClass, engine->syntaxErrorPrototype(), msg, fileName, lineNumber, columnNumber, SyntaxError) { } Heap::EvalErrorObject::EvalErrorObject(ExecutionEngine *engine, const Value &message) - : Heap::ErrorObject(engine->emptyClass, engine->evalErrorPrototype.asObject(), message, EvalError) + : Heap::ErrorObject(engine->emptyClass, engine->evalErrorPrototype(), message, EvalError) { } Heap::RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const Value &message) - : Heap::ErrorObject(engine->emptyClass, engine->rangeErrorPrototype.asObject(), message, RangeError) + : Heap::ErrorObject(engine->emptyClass, engine->rangeErrorPrototype(), message, RangeError) { } Heap::RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const QString &message) - : Heap::ErrorObject(engine->emptyClass, engine->rangeErrorPrototype.asObject(), message, RangeError) + : Heap::ErrorObject(engine->emptyClass, engine->rangeErrorPrototype(), message, RangeError) { } Heap::ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const Value &message) - : Heap::ErrorObject(engine->emptyClass, engine->referenceErrorPrototype.asObject(), message, ReferenceError) + : Heap::ErrorObject(engine->emptyClass, engine->referenceErrorPrototype(), message, ReferenceError) { } Heap::ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &message) - : Heap::ErrorObject(engine->emptyClass, engine->referenceErrorPrototype.asObject(), message, ReferenceError) + : Heap::ErrorObject(engine->emptyClass, engine->referenceErrorPrototype(), message, ReferenceError) { } Heap::ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber, int columnNumber) - : Heap::ErrorObject(engine->emptyClass, engine->referenceErrorPrototype.asObject(), msg, fileName, lineNumber, columnNumber, ReferenceError) + : Heap::ErrorObject(engine->emptyClass, engine->referenceErrorPrototype(), msg, fileName, lineNumber, columnNumber, ReferenceError) { } Heap::TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const Value &message) - : Heap::ErrorObject(engine->emptyClass, engine->typeErrorPrototype.asObject(), message, TypeError) + : Heap::ErrorObject(engine->emptyClass, engine->typeErrorPrototype(), message, TypeError) { } Heap::TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const QString &message) - : Heap::ErrorObject(engine->emptyClass, engine->typeErrorPrototype.asObject(), message, TypeError) + : Heap::ErrorObject(engine->emptyClass, engine->typeErrorPrototype(), message, TypeError) { } Heap::URIErrorObject::URIErrorObject(ExecutionEngine *engine, const Value &message) - : Heap::ErrorObject(engine->emptyClass, engine->uRIErrorPrototype.asObject(), message, URIError) + : Heap::ErrorObject(engine->emptyClass, engine->uRIErrorPrototype(), message, URIError) { } @@ -251,16 +254,16 @@ Heap::ErrorCtor::ErrorCtor(QV4::ExecutionContext *scope, const QString &name) { } -ReturnedValue ErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue ErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<ErrorCtor *>(m)->engine()); + Scope scope(static_cast<const ErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); return Encode(scope.engine->newErrorObject(v)); } -ReturnedValue ErrorCtor::call(Managed *that, CallData *callData) +ReturnedValue ErrorCtor::call(const Managed *that, CallData *callData) { - return static_cast<Object *>(that)->construct(callData); + return static_cast<const Object *>(that)->construct(callData); } Heap::EvalErrorCtor::EvalErrorCtor(QV4::ExecutionContext *scope) @@ -268,9 +271,9 @@ Heap::EvalErrorCtor::EvalErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue EvalErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue EvalErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<EvalErrorCtor *>(m)->engine()); + Scope scope(static_cast<const EvalErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); return (scope.engine->memoryManager->alloc<EvalErrorObject>(scope.engine, v))->asReturnedValue(); } @@ -280,9 +283,9 @@ Heap::RangeErrorCtor::RangeErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue RangeErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue RangeErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<RangeErrorCtor *>(m)->engine()); + Scope scope(static_cast<const RangeErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); return (scope.engine->memoryManager->alloc<RangeErrorObject>(scope.engine, v))->asReturnedValue(); } @@ -292,9 +295,9 @@ Heap::ReferenceErrorCtor::ReferenceErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ReferenceErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue ReferenceErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<ReferenceErrorCtor *>(m)->engine()); + Scope scope(static_cast<const ReferenceErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); return (scope.engine->memoryManager->alloc<ReferenceErrorObject>(scope.engine, v))->asReturnedValue(); } @@ -304,9 +307,9 @@ Heap::SyntaxErrorCtor::SyntaxErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue SyntaxErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue SyntaxErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<SyntaxErrorCtor *>(m)->engine()); + Scope scope(static_cast<const SyntaxErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); return (scope.engine->memoryManager->alloc<SyntaxErrorObject>(scope.engine, v))->asReturnedValue(); } @@ -316,9 +319,9 @@ Heap::TypeErrorCtor::TypeErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue TypeErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue TypeErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<TypeErrorCtor *>(m)->engine()); + Scope scope(static_cast<const TypeErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); return (scope.engine->memoryManager->alloc<TypeErrorObject>(scope.engine, v))->asReturnedValue(); } @@ -328,9 +331,9 @@ Heap::URIErrorCtor::URIErrorCtor(QV4::ExecutionContext *scope) { } -ReturnedValue URIErrorCtor::construct(Managed *m, CallData *callData) +ReturnedValue URIErrorCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<URIErrorCtor *>(m)->engine()); + Scope scope(static_cast<const URIErrorCtor *>(m)->engine()); ScopedValue v(scope, callData->argument(0)); return (scope.engine->memoryManager->alloc<URIErrorObject>(scope.engine, v))->asReturnedValue(); } @@ -340,10 +343,10 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj) Scope scope(engine); ScopedString s(scope); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_prototype, (o = obj)); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = obj)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); obj->defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - obj->defineDefaultProperty(engine->id_toString, method_toString, 0); + obj->defineDefaultProperty(engine->id_toString(), method_toString, 0); obj->defineDefaultProperty(QStringLiteral("message"), (s = engine->newString())); } @@ -351,18 +354,18 @@ ReturnedValue ErrorPrototype::method_toString(CallContext *ctx) { Scope scope(ctx); - Object *o = ctx->thisObject().asObject(); + Object *o = ctx->thisObject().as<Object>(); if (!o) return ctx->engine()->throwTypeError(); - ScopedValue name(scope, o->get(ctx->d()->engine->id_name)); + ScopedValue name(scope, o->get(ctx->d()->engine->id_name())); QString qname; if (name->isUndefined()) - qname = QString::fromLatin1("Error"); + qname = QStringLiteral("Error"); else qname = name->toQString(); - ScopedString s(scope, ctx->d()->engine->newString(QString::fromLatin1("message"))); + ScopedString s(scope, ctx->d()->engine->newString(QStringLiteral("message"))); ScopedValue message(scope, o->get(s)); QString qmessage; if (!message->isUndefined()) diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 071f5b8c9a..e0fbcb4d8d 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -62,7 +62,7 @@ struct ErrorObject : Object { ErrorType errorType; StackTrace stackTrace; - String *stack; + Pointer<String> stack; }; struct EvalErrorObject : ErrorObject { @@ -141,8 +141,8 @@ struct ErrorObject: Object { }; template<> -inline ErrorObject *value_cast(const Value &v) { - return v.asErrorObject(); +inline const ErrorObject *Value::as() const { + return isManaged() && m() && m()->vtable()->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : 0; } struct EvalErrorObject: ErrorObject { @@ -183,50 +183,50 @@ struct ErrorCtor: FunctionObject { V4_OBJECT2(ErrorCtor, FunctionObject) - static ReturnedValue construct(Managed *, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct EvalErrorCtor: ErrorCtor { V4_OBJECT2(EvalErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct RangeErrorCtor: ErrorCtor { V4_OBJECT2(RangeErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct ReferenceErrorCtor: ErrorCtor { V4_OBJECT2(ReferenceErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct SyntaxErrorCtor: ErrorCtor { V4_OBJECT2(SyntaxErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct TypeErrorCtor: ErrorCtor { V4_OBJECT2(TypeErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; struct URIErrorCtor: ErrorCtor { V4_OBJECT2(URIErrorCtor, ErrorCtor) - static ReturnedValue construct(Managed *m, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index be63b31e1e..98a4490211 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -34,10 +34,10 @@ #include "qv4function_p.h" #include "qv4managed_p.h" #include "qv4string_p.h" -#include "qv4value_inl_p.h" +#include "qv4value_p.h" #include "qv4engine_p.h" #include "qv4lookup_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 0a12d013ac..da01592c0d 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -37,7 +37,7 @@ #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4function_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4arrayobject_p.h" #include "qv4scopedvalue_p.h" @@ -63,7 +63,7 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(FunctionObject); Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, QV4::String *name, bool createProto) - : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype.asObject()) + : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype()) , scope(scope->d()) , function(Q_NULLPTR) { @@ -73,7 +73,7 @@ Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, QV4::String * } Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, Function *function, bool createProto) - : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype.asObject()) + : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype()) , scope(scope->d()) , function(Q_NULLPTR) { @@ -84,7 +84,7 @@ Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, Function *fun } Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, const QString &name, bool createProto) - : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype.asObject()) + : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype()) , scope(scope->d()) , function(Q_NULLPTR) { @@ -95,7 +95,7 @@ Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, const QString } Heap::FunctionObject::FunctionObject(ExecutionContext *scope, const QString &name, bool createProto) - : Heap::Object(scope->engine->functionClass, scope->engine->functionPrototype.asObject()) + : Heap::Object(scope->engine->functionClass, scope->engine->functionPrototype()) , scope(scope) , function(Q_NULLPTR) { @@ -106,7 +106,7 @@ Heap::FunctionObject::FunctionObject(ExecutionContext *scope, const QString &nam } Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, const ReturnedValue name) - : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype.asObject()) + : Heap::Object(scope->d()->engine->functionClass, scope->d()->engine->functionPrototype()) , scope(scope->d()) , function(Q_NULLPTR) { @@ -117,7 +117,7 @@ Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, const Returne } Heap::FunctionObject::FunctionObject(ExecutionContext *scope, const ReturnedValue name) - : Heap::Object(scope->engine->functionClass, scope->engine->functionPrototype.asObject()) + : Heap::Object(scope->engine->functionClass, scope->engine->functionPrototype()) , scope(scope) , function(Q_NULLPTR) { @@ -129,7 +129,7 @@ Heap::FunctionObject::FunctionObject(ExecutionContext *scope, const ReturnedValu Heap::FunctionObject::FunctionObject(InternalClass *ic, QV4::Object *prototype) : Heap::Object(ic, prototype) - , scope(ic->engine->rootContext()) + , scope(ic->engine->rootContext()->d()) , function(Q_NULLPTR) { Scope scope(ic->engine); @@ -152,7 +152,7 @@ void FunctionObject::init(String *n, bool createProto) ensureMemberIndex(s.engine, Heap::FunctionObject::Index_Prototype); if (createProto) { - ScopedObject proto(s, scope()->engine->newObject(s.engine->protoClass, s.engine->objectPrototype.asObject())); + ScopedObject proto(s, scope()->engine->newObject(s.engine->protoClass, s.engine->objectPrototype())); proto->ensureMemberIndex(s.engine, Heap::FunctionObject::Index_ProtoConstructor); proto->memberData()->data[Heap::FunctionObject::Index_ProtoConstructor] = this->asReturnedValue(); memberData()->data[Heap::FunctionObject::Index_Prototype] = proto.asReturnedValue(); @@ -161,12 +161,12 @@ void FunctionObject::init(String *n, bool createProto) } ScopedValue v(s, n); - defineReadonlyProperty(s.engine->id_name, v); + defineReadonlyProperty(s.engine->id_name(), v); } -ReturnedValue FunctionObject::name() +ReturnedValue FunctionObject::name() const { - return get(scope()->engine->id_name); + return get(scope()->engine->id_name()); } @@ -177,13 +177,12 @@ ReturnedValue FunctionObject::newInstance() return construct(callData); } -ReturnedValue FunctionObject::construct(Managed *that, CallData *) +ReturnedValue FunctionObject::construct(const Managed *that, CallData *) { - static_cast<FunctionObject *>(that)->internalClass()->engine->throwTypeError(); - return Encode::undefined(); + return static_cast<const FunctionObject *>(that)->engine()->throwTypeError(); } -ReturnedValue FunctionObject::call(Managed *, CallData *) +ReturnedValue FunctionObject::call(const Managed *, CallData *) { return Encode::undefined(); } @@ -209,12 +208,12 @@ Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *sco bool FunctionObject::isBinding() const { - return d()->vtable == QQmlBindingFunction::staticVTable(); + return d()->vtable() == QQmlBindingFunction::staticVTable(); } bool FunctionObject::isBoundFunction() const { - return d()->vtable == BoundFunction::staticVTable(); + return d()->vtable() == BoundFunction::staticVTable(); } QQmlSourceLocation FunctionObject::sourceLocation() const @@ -237,10 +236,10 @@ Heap::FunctionCtor::FunctionCtor(QV4::ExecutionContext *scope) } // 15.3.2 -ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData) +ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData) { - Scope scope(static_cast<Object *>(that)->engine()); - Scoped<FunctionCtor> f(scope, static_cast<FunctionCtor *>(that)); + Scope scope(static_cast<const Object *>(that)->engine()); + Scoped<FunctionCtor> f(scope, static_cast<const FunctionCtor *>(that)); ScopedContext ctx(scope, scope.engine->currentContext()); QString arguments; QString body; @@ -287,7 +286,7 @@ ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData) } // 15.3.1: This is equivalent to new Function(...) -ReturnedValue FunctionCtor::call(Managed *that, CallData *callData) +ReturnedValue FunctionCtor::call(const Managed *that, CallData *callData) { return construct(that, callData); } @@ -304,12 +303,12 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(1)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - defineReadonlyProperty(engine->id_length, Primitive::fromInt32(0)); + defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(0)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString, 0); + defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("apply"), method_apply, 2); defineDefaultProperty(QStringLiteral("call"), method_call, 1); defineDefaultProperty(QStringLiteral("bind"), method_bind, 1); @@ -318,7 +317,7 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) ReturnedValue FunctionPrototype::method_toString(CallContext *ctx) { - FunctionObject *fun = ctx->thisObject().asFunctionObject(); + FunctionObject *fun = ctx->thisObject().as<FunctionObject>(); if (!fun) return ctx->engine()->throwTypeError(); @@ -328,7 +327,7 @@ ReturnedValue FunctionPrototype::method_toString(CallContext *ctx) ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) { Scope scope(ctx); - ScopedFunctionObject o(scope, ctx->thisObject().asFunctionObject()); + ScopedFunctionObject o(scope, ctx->thisObject().as<FunctionObject>()); if (!o) return ctx->engine()->throwTypeError(); @@ -370,7 +369,7 @@ ReturnedValue FunctionPrototype::method_call(CallContext *ctx) { Scope scope(ctx); - ScopedFunctionObject o(scope, ctx->thisObject().asFunctionObject()); + ScopedFunctionObject o(scope, ctx->thisObject().as<FunctionObject>()); if (!o) return ctx->engine()->throwTypeError(); @@ -409,15 +408,15 @@ Heap::ScriptFunction::ScriptFunction(QV4::ExecutionContext *scope, Function *fun { } -ReturnedValue ScriptFunction::construct(Managed *that, CallData *callData) +ReturnedValue ScriptFunction::construct(const Managed *that, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(that)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); if (v4->hasException) return Encode::undefined(); CHECK_STACK_LIMITS(v4); Scope scope(v4); - Scoped<ScriptFunction> f(scope, static_cast<ScriptFunction *>(that)); + Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); InternalClass *ic = scope.engine->emptyClass; ScopedObject proto(scope, f->protoForConstructor()); @@ -441,15 +440,15 @@ ReturnedValue ScriptFunction::construct(Managed *that, CallData *callData) return obj.asReturnedValue(); } -ReturnedValue ScriptFunction::call(Managed *that, CallData *callData) +ReturnedValue ScriptFunction::call(const Managed *that, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(that)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); if (v4->hasException) return Encode::undefined(); CHECK_STACK_LIMITS(v4); Scope scope(v4); - Scoped<ScriptFunction> f(scope, static_cast<ScriptFunction *>(that)); + Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); ScopedContext context(scope, v4->currentContext()); Scoped<CallContext> ctx(scope, context->newCallContext(f, callData)); @@ -458,7 +457,7 @@ ReturnedValue ScriptFunction::call(Managed *that, CallData *callData) ScopedValue result(scope, Q_V4_PROFILE(v4, f->function())); if (f->function()->compiledFunction->hasQmlDependencies()) - QmlContextWrapper::registerQmlDependencies(ctx->d()->engine, f->function()->compiledFunction); + QmlContextWrapper::registerQmlDependencies(scope.engine, f->function()->compiledFunction); return result->asReturnedValue(); } @@ -466,7 +465,7 @@ ReturnedValue ScriptFunction::call(Managed *that, CallData *callData) DEFINE_OBJECT_VTABLE(SimpleScriptFunction); Heap::SimpleScriptFunction::SimpleScriptFunction(QV4::ExecutionContext *scope, Function *function, bool createProto) - : Heap::FunctionObject(function->compilationUnit->engine->simpleScriptFunctionClass, function->compilationUnit->engine->functionPrototype.asObject()) + : Heap::FunctionObject(function->compilationUnit->engine->simpleScriptFunctionClass, function->compilationUnit->engine->functionPrototype()) { this->scope = scope->d(); @@ -481,7 +480,7 @@ Heap::SimpleScriptFunction::SimpleScriptFunction(QV4::ExecutionContext *scope, F if (createProto) { ScopedString name(s, function->name()); f->init(name, createProto); - f->defineReadonlyProperty(scope->d()->engine->id_length, Primitive::fromInt32(f->formalParameterCount())); + f->defineReadonlyProperty(scope->d()->engine->id_length(), Primitive::fromInt32(f->formalParameterCount())); } else { f->ensureMemberIndex(s.engine, Index_Length); memberData->data[Index_Name] = function->name(); @@ -490,22 +489,22 @@ Heap::SimpleScriptFunction::SimpleScriptFunction(QV4::ExecutionContext *scope, F if (scope->d()->strictMode) { ScopedProperty pd(s); - pd->value = s.engine->thrower; - pd->set = s.engine->thrower; - f->insertMember(scope->d()->engine->id_caller, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - f->insertMember(scope->d()->engine->id_arguments, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + pd->value = s.engine->thrower(); + pd->set = s.engine->thrower(); + f->insertMember(scope->d()->engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + f->insertMember(scope->d()->engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } } -ReturnedValue SimpleScriptFunction::construct(Managed *that, CallData *callData) +ReturnedValue SimpleScriptFunction::construct(const Managed *that, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(that)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); if (v4->hasException) return Encode::undefined(); CHECK_STACK_LIMITS(v4); Scope scope(v4); - Scoped<SimpleScriptFunction> f(scope, static_cast<SimpleScriptFunction *>(that)); + Scoped<SimpleScriptFunction> f(scope, static_cast<const SimpleScriptFunction *>(that)); InternalClass *ic = scope.engine->emptyClass; ScopedObject proto(scope, f->protoForConstructor()); @@ -514,7 +513,10 @@ ReturnedValue SimpleScriptFunction::construct(Managed *that, CallData *callData) ExecutionContextSaver ctxSaver(scope, v4->currentContext()); CallContext::Data ctx(v4); - ctx.vtable = CallContext::staticVTable(); +#ifndef QT_NO_DEBUG + ctx.mm_data = 0; // make sure we don't run into the assertion in setVTable when allocating a context on the stack +#endif + ctx.setVtable(CallContext::staticVTable()); ctx.strictMode = f->strictMode(); ctx.callData = callData; ctx.function = f->d(); @@ -536,20 +538,23 @@ ReturnedValue SimpleScriptFunction::construct(Managed *that, CallData *callData) return result.asReturnedValue(); } -ReturnedValue SimpleScriptFunction::call(Managed *that, CallData *callData) +ReturnedValue SimpleScriptFunction::call(const Managed *that, CallData *callData) { - ExecutionEngine *v4 = static_cast<SimpleScriptFunction *>(that)->internalClass()->engine; + ExecutionEngine *v4 = static_cast<const SimpleScriptFunction *>(that)->internalClass()->engine; if (v4->hasException) return Encode::undefined(); CHECK_STACK_LIMITS(v4); Scope scope(v4); - Scoped<SimpleScriptFunction> f(scope, static_cast<SimpleScriptFunction *>(that)); + Scoped<SimpleScriptFunction> f(scope, static_cast<const SimpleScriptFunction *>(that)); ExecutionContextSaver ctxSaver(scope, v4->currentContext()); CallContext::Data ctx(v4); - ctx.vtable = CallContext::staticVTable(); +#ifndef QT_NO_DEBUG + ctx.mm_data = 0; // make sure we don't run into the assertion in setVTable when allocating a context on the stack +#endif + ctx.setVtable(CallContext::staticVTable()); ctx.strictMode = f->strictMode(); ctx.callData = callData; ctx.function = f->d(); @@ -575,7 +580,7 @@ Heap::Object *SimpleScriptFunction::protoForConstructor() ScopedObject p(scope, protoProperty()); if (p) return p->d(); - return scope.engine->objectPrototype.asObject()->d(); + return scope.engine->objectPrototype()->d(); } @@ -588,14 +593,14 @@ Heap::BuiltinFunction::BuiltinFunction(QV4::ExecutionContext *scope, QV4::String { } -ReturnedValue BuiltinFunction::construct(Managed *f, CallData *) +ReturnedValue BuiltinFunction::construct(const Managed *f, CallData *) { - return static_cast<BuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); + return static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); } -ReturnedValue BuiltinFunction::call(Managed *that, CallData *callData) +ReturnedValue BuiltinFunction::call(const Managed *that, CallData *callData) { - BuiltinFunction *f = static_cast<BuiltinFunction *>(that); + const BuiltinFunction *f = static_cast<const BuiltinFunction *>(that); ExecutionEngine *v4 = f->internalClass()->engine; if (v4->hasException) return Encode::undefined(); @@ -605,7 +610,10 @@ ReturnedValue BuiltinFunction::call(Managed *that, CallData *callData) ExecutionContextSaver ctxSaver(scope, v4->currentContext()); CallContext::Data ctx(v4); - ctx.vtable = CallContext::staticVTable(); +#ifndef QT_NO_DEBUG + ctx.mm_data = 0; // make sure we don't run into the assertion in setVTable when allocating a context on the stack +#endif + ctx.setVtable(CallContext::staticVTable()); ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context? ctx.callData = callData; Q_ASSERT(v4->currentContext() == &ctx); @@ -614,9 +622,9 @@ ReturnedValue BuiltinFunction::call(Managed *that, CallData *callData) return f->d()->code(sctx); } -ReturnedValue IndexedBuiltinFunction::call(Managed *that, CallData *callData) +ReturnedValue IndexedBuiltinFunction::call(const Managed *that, CallData *callData) { - IndexedBuiltinFunction *f = static_cast<IndexedBuiltinFunction *>(that); + const IndexedBuiltinFunction *f = static_cast<const IndexedBuiltinFunction *>(that); ExecutionEngine *v4 = f->internalClass()->engine; if (v4->hasException) return Encode::undefined(); @@ -626,7 +634,10 @@ ReturnedValue IndexedBuiltinFunction::call(Managed *that, CallData *callData) ExecutionContextSaver ctxSaver(scope, v4->currentContext()); CallContext::Data ctx(v4); - ctx.vtable = CallContext::staticVTable(); +#ifndef QT_NO_DEBUG + ctx.mm_data = 0; // make sure we don't run into the assertion in setVTable when allocating a context on the stack +#endif + ctx.setVtable(CallContext::staticVTable()); ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context? ctx.callData = callData; Q_ASSERT(v4->currentContext() == &ctx); @@ -650,24 +661,24 @@ Heap::BoundFunction::BoundFunction(QV4::ExecutionContext *scope, QV4::FunctionOb Scope s(scope); ScopedObject f(s, this); - ScopedValue l(s, target->get(s.engine->id_length)); + ScopedValue l(s, target->get(s.engine->id_length())); int len = l->toUInt32(); if (boundArgs) len -= boundArgs->size(); if (len < 0) len = 0; - f->defineReadonlyProperty(s.engine->id_length, Primitive::fromInt32(len)); + f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(len)); ScopedProperty pd(s); - pd->value = s.engine->thrower; - pd->set = s.engine->thrower; - f->insertMember(s.engine->id_arguments, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - f->insertMember(s.engine->id_caller, pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + pd->value = s.engine->thrower(); + pd->set = s.engine->thrower(); + f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } -ReturnedValue BoundFunction::call(Managed *that, CallData *dd) +ReturnedValue BoundFunction::call(const Managed *that, CallData *dd) { - BoundFunction *f = static_cast<BoundFunction *>(that); + const BoundFunction *f = static_cast<const BoundFunction *>(that); Scope scope(f->engine()); if (scope.hasException()) return Encode::undefined(); @@ -685,9 +696,9 @@ ReturnedValue BoundFunction::call(Managed *that, CallData *dd) return t->call(callData); } -ReturnedValue BoundFunction::construct(Managed *that, CallData *dd) +ReturnedValue BoundFunction::construct(const Managed *that, CallData *dd) { - BoundFunction *f = static_cast<BoundFunction *>(that); + const BoundFunction *f = static_cast<const BoundFunction *>(that); Scope scope(f->engine()); if (scope.hasException()) return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 252ff40a1a..2c6a195746 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -36,7 +36,7 @@ #include "qv4object_p.h" #include "qv4function_p.h" #include "qv4context_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> QT_BEGIN_NAMESPACE @@ -65,7 +65,7 @@ struct Q_QML_PRIVATE_EXPORT FunctionObject : Object { unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; } bool needsActivation() const { return function ? function->needsActivation() : false; } - ExecutionContext *scope; + Pointer<ExecutionContext> scope; Function *function; }; @@ -102,9 +102,9 @@ struct ScriptFunction : SimpleScriptFunction { struct BoundFunction : FunctionObject { BoundFunction(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); - FunctionObject *target; + Pointer<FunctionObject> target; Value boundThis; - MemberData *boundArgs; + Pointer<MemberData> boundArgs; }; } @@ -117,12 +117,12 @@ struct Q_QML_EXPORT FunctionObject: Object { Q_MANAGED_TYPE(FunctionObject) V4_NEEDS_DESTROY - Heap::ExecutionContext *scope() { return d()->scope; } - Function *function() { return d()->function; } + Heap::ExecutionContext *scope() const { return d()->scope; } + Function *function() const { return d()->function; } - ReturnedValue name(); - unsigned int formalParameterCount() { return d()->formalParameterCount(); } - unsigned int varCount() { return d()->varCount(); } + ReturnedValue name() const; + unsigned int formalParameterCount() const { return d()->formalParameterCount(); } + unsigned int varCount() const { return d()->varCount(); } void init(String *name, bool createProto); @@ -130,12 +130,8 @@ struct Q_QML_EXPORT FunctionObject: Object { using Object::construct; using Object::call; - static ReturnedValue construct(Managed *that, CallData *); - static ReturnedValue call(Managed *that, CallData *d); - - static FunctionObject *cast(const Value &v) { - return v.asFunctionObject(); - } + static ReturnedValue construct(const Managed *that, CallData *); + static ReturnedValue call(const Managed *that, CallData *d); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function, bool createProto = true); @@ -152,16 +148,17 @@ struct Q_QML_EXPORT FunctionObject: Object { }; template<> -inline FunctionObject *value_cast(const Value &v) { - return v.asFunctionObject(); +inline const FunctionObject *Value::as() const { + return isManaged() && m() && m()->vtable()->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : 0; } + struct FunctionCtor: FunctionObject { V4_OBJECT2(FunctionCtor, FunctionObject) - static ReturnedValue construct(Managed *that, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct FunctionPrototype: FunctionObject @@ -184,20 +181,20 @@ struct Q_QML_EXPORT BuiltinFunction: FunctionObject { return scope->engine()->memoryManager->alloc<BuiltinFunction>(scope, name, code); } - static ReturnedValue construct(Managed *, CallData *); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *, CallData *); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct IndexedBuiltinFunction: FunctionObject { V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *) + static ReturnedValue construct(const Managed *m, CallData *) { - return static_cast<IndexedBuiltinFunction *>(m)->engine()->throwTypeError(); + return static_cast<const IndexedBuiltinFunction *>(m)->engine()->throwTypeError(); } - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; Heap::IndexedBuiltinFunction::IndexedBuiltinFunction(QV4::ExecutionContext *scope, uint index, @@ -212,8 +209,8 @@ Heap::IndexedBuiltinFunction::IndexedBuiltinFunction(QV4::ExecutionContext *scop struct SimpleScriptFunction: FunctionObject { V4_OBJECT2(SimpleScriptFunction, FunctionObject) - static ReturnedValue construct(Managed *, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); Heap::Object *protoForConstructor(); }; @@ -221,8 +218,8 @@ struct SimpleScriptFunction: FunctionObject { struct ScriptFunction: SimpleScriptFunction { V4_OBJECT2(ScriptFunction, FunctionObject) - static ReturnedValue construct(Managed *, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; @@ -234,12 +231,12 @@ struct BoundFunction: FunctionObject { return scope->engine()->memoryManager->alloc<BoundFunction>(scope, target, boundThis, boundArgs); } - Heap::FunctionObject *target() { return d()->target; } + Heap::FunctionObject *target() const { return d()->target; } Value boundThis() const { return d()->boundThis; } Heap::MemberData *boundArgs() const { return d()->boundArgs; } - static ReturnedValue construct(Managed *, CallData *d); - static ReturnedValue call(Managed *that, CallData *dd); + static ReturnedValue construct(const Managed *, CallData *d); + static ReturnedValue call(const Managed *that, CallData *dd); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 4b08194b60..759c6795e2 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -88,6 +88,8 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } #define V4_ENABLE_JIT #endif +#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) +#define V4_ENABLE_JIT #endif // Black list some platforms @@ -168,7 +170,7 @@ struct Property; struct Value; struct Lookup; struct ArrayData; -struct ManagedVTable; +struct VTable; struct BooleanObject; struct NumberObject; @@ -211,7 +213,6 @@ class WeakValue; struct IdentifierTable; class RegExpCache; class MultiplyWrappedQObjectMap; -struct QmlExtensions; namespace Global { enum { @@ -277,8 +278,6 @@ struct PropertyAttributes setConfigurable(!(f & Attr_NotConfigurable)); } } - PropertyAttributes(const PropertyAttributes &other) : m_all(other.m_all) {} - PropertyAttributes & operator=(const PropertyAttributes &other) { m_all = other.m_all; return *this; } void setType(Type t) { m_type = t; type_set = true; } Type type() const { return type_set ? (Type)m_type : Generic; } diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 8e33cec57f..ec7b9b3f7d 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -32,13 +32,15 @@ ****************************************************************************/ #include "qv4globalobject_p.h" -#include "qv4mm_p.h" -#include "qv4value_inl_p.h" +#include <private/qv4mm_p.h> +#include "qv4value_p.h" #include "qv4context_p.h" #include "qv4function_p.h" #include "qv4debugging_p.h" +#include "qv4profiling_p.h" #include "qv4script_p.h" #include "qv4scopedvalue_p.h" +#include "qv4string_p.h" #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> @@ -47,6 +49,7 @@ #include <qv4jsir_p.h> #include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" +#include "private/qtools_p.h" #include <QtCore/QDebug> #include <QtCore/QString> @@ -56,25 +59,8 @@ #include <wtf/MathExtras.h> using namespace QV4; - - -static inline char toHex(char c) -{ - static const char hexnumbers[] = "0123456789ABCDEF"; - return hexnumbers[c & 0xf]; -} - -static int fromHex(QChar ch) -{ - ushort c = ch.unicode(); - if ((c >= '0') && (c <= '9')) - return c - '0'; - if ((c >= 'A') && (c <= 'F')) - return c - 'A' + 10; - if ((c >= 'a') && (c <= 'f')) - return c - 'a' + 10; - return -1; -} +using QtMiscUtils::toHexUpper; +using QtMiscUtils::fromHex; static QString escape(const QString &input) { @@ -93,16 +79,16 @@ static QString escape(const QString &input) output.append(QChar(uc)); } else { output.append('%'); - output.append(QLatin1Char(toHex(uc >> 4))); - output.append(QLatin1Char(toHex(uc))); + output.append(QLatin1Char(toHexUpper(uc >> 4))); + output.append(QLatin1Char(toHexUpper(uc))); } } else { output.append('%'); output.append('u'); - output.append(QLatin1Char(toHex(uc >> 12))); - output.append(QLatin1Char(toHex(uc >> 8))); - output.append(QLatin1Char(toHex(uc >> 4))); - output.append(QLatin1Char(toHex(uc))); + output.append(QLatin1Char(toHexUpper(uc >> 12))); + output.append(QLatin1Char(toHexUpper(uc >> 8))); + output.append(QLatin1Char(toHexUpper(uc >> 4))); + output.append(QLatin1Char(toHexUpper(uc))); } } return output; @@ -119,10 +105,10 @@ static QString unescape(const QString &input) if ((c == '%') && (i + 1 < length)) { QChar a = input.at(i); if ((a == 'u') && (i + 4 < length)) { - int d3 = fromHex(input.at(i+1)); - int d2 = fromHex(input.at(i+2)); - int d1 = fromHex(input.at(i+3)); - int d0 = fromHex(input.at(i+4)); + int d3 = fromHex(input.at(i+1).unicode()); + int d2 = fromHex(input.at(i+2).unicode()); + int d1 = fromHex(input.at(i+3).unicode()); + int d0 = fromHex(input.at(i+4).unicode()); if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) { ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0); result.append(QChar(uc)); @@ -131,8 +117,8 @@ static QString unescape(const QString &input) result.append(c); } } else { - int d1 = fromHex(a); - int d0 = fromHex(input.at(i+1)); + int d1 = fromHex(a.unicode()); + int d0 = fromHex(input.at(i+1).unicode()); if ((d1 != -1) && (d0 != -1)) { c = (d1 << 4) | d0; i += 2; @@ -153,8 +139,8 @@ static const char uriUnescapedReserved[] = "-_.!~*'();/?:@&=+$,#"; static void addEscapeSequence(QString &output, uchar ch) { output.append(QLatin1Char('%')); - output.append(QLatin1Char(toHex(ch >> 4))); - output.append(QLatin1Char(toHex(ch & 0xf))); + output.append(QLatin1Char(toHexUpper(ch >> 4))); + output.append(QLatin1Char(toHexUpper(ch & 0xf))); } static QString encode(const QString &input, const char *unescapedSet, bool *ok) @@ -246,8 +232,8 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) if (i + 2 >= length) goto error; - int d1 = fromHex(input.at(i+1)); - int d0 = fromHex(input.at(i+2)); + int d1 = fromHex(input.at(i+1).unicode()); + int d0 = fromHex(input.at(i+2).unicode()); if ((d1 == -1) || (d0 == -1)) goto error; @@ -281,8 +267,8 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) if (input.at(i) != percent) goto error; - d1 = fromHex(input.at(i+1)); - d0 = fromHex(input.at(i+2)); + d1 = fromHex(input.at(i+1).unicode()); + d0 = fromHex(input.at(i+2).unicode()); if ((d1 == -1) || (d0 == -1)) goto error; @@ -339,14 +325,14 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) DEFINE_OBJECT_VTABLE(EvalFunction); Heap::EvalFunction::EvalFunction(QV4::ExecutionContext *scope) - : Heap::FunctionObject(scope, scope->d()->engine->id_eval) + : Heap::FunctionObject(scope, scope->d()->engine->id_eval()) { Scope s(scope); ScopedFunctionObject f(s, this); - f->defineReadonlyProperty(s.engine->id_length, Primitive::fromInt32(1)); + f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); } -ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) +ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const { if (callData->argc < 1) return Encode::undefined(); @@ -395,14 +381,14 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) ctx->d()->strictMode = false; ctx->d()->compilationUnit = function->compilationUnit; - return function->code(ctx->engine(), function->codeData); + return Q_V4_PROFILE(ctx->engine(), function); } -ReturnedValue EvalFunction::call(Managed *that, CallData *callData) +ReturnedValue EvalFunction::call(const Managed *that, CallData *callData) { // indirect call - return static_cast<EvalFunction *>(that)->evalCall(callData, false); + return static_cast<const EvalFunction *>(that)->evalCall(callData, false); } diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h index 74de233b47..ba1d5d2e0b 100644 --- a/src/qml/jsruntime/qv4globalobject_p.h +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -52,10 +52,10 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject { V4_OBJECT2(EvalFunction, FunctionObject) - ReturnedValue evalCall(CallData *callData, bool directCall); + ReturnedValue evalCall(CallData *callData, bool directCall) const; using Object::construct; - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct GlobalFunctions diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index ff374225f4..58f808b4d5 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -80,8 +80,8 @@ public: if (!entry || entry->isMarked()) continue; entry->setMarkBit(); - Q_ASSERT(entry->gcGetVtable()->markObjects); - entry->gcGetVtable()->markObjects(entry, e); + Q_ASSERT(entry->vtable()->markObjects); + entry->vtable()->markObjects(entry, e); } } }; diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index e4bd460966..2eb61081c7 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -54,7 +54,7 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, QQmlContex : v4(engine), m_network(0), m_reply(0), m_url(url), m_redirectCount(0), m_context(context) { m_qmlglobal.set(engine, qmlglobal); - if (callback.asFunctionObject()) + if (callback.as<QV4::FunctionObject>()) m_callbackFunction.set(engine, callback); m_resultObject.set(v4, resultValue(v4)); @@ -94,14 +94,14 @@ void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status) { if (!callback.isObject()) return; - QV4::ExecutionEngine *v4 = callback.asObject()->engine(); + QV4::ExecutionEngine *v4 = callback.as<QV4::Object>()->engine(); QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, callback); if (!f) return; QV4::ScopedCallData callData(scope, 1); - callData->thisObject = v4->globalObject()->asReturnedValue(); + callData->thisObject = v4->globalObject->asReturnedValue(); callData->args[0] = status; f->call(callData); if (scope.hasException()) @@ -176,7 +176,7 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) return QV4::Encode::undefined(); QV4::Scope scope(ctx->engine()); - QQmlContextData *context = QV4::QmlContextWrapper::callingContext(scope.engine); + QQmlContextData *context = scope.engine->callingQmlContext(); if (!context || !context->isJSContext) V4THROW_ERROR("Qt.include(): Can only be called from JavaScript files"); @@ -184,7 +184,7 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) QUrl url(scope.engine->resolvedUrl(ctx->args()[0].toQStringNoThrow())); QV4::ScopedValue callbackFunction(scope, QV4::Primitive::undefinedValue()); - if (ctx->argc() >= 2 && ctx->args()[1].asFunctionObject()) + if (ctx->argc() >= 2 && ctx->args()[1].as<QV4::FunctionObject>()) callbackFunction = ctx->args()[1]; QString localFile = QQmlFile::urlToLocalFileOrQrc(url); @@ -224,7 +224,7 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) QV4::ScopedValue ex(scope, scope.engine->catchException()); result = resultValue(scope.engine, Exception); QV4::ScopedString exception(scope, scope.engine->newString(QStringLiteral("exception"))); - result->asObject()->put(exception, ex); + result->as<QV4::Object>()->put(exception, ex); } else { result = resultValue(scope.engine, Ok); } diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index 5dc94e8555..5ef4442b03 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -50,7 +50,7 @@ #include <private/qqmlcontext_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4context_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 3289058cb7..80590fe72e 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -46,7 +46,7 @@ struct String; struct ExecutionEngine; struct Object; struct Identifier; -struct ManagedVTable; +struct VTable; struct PropertyHashData; struct PropertyHash diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index e7905974df..f703e85399 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -38,8 +38,8 @@ #include <qv4objectiterator_p.h> #include <qv4scopedvalue_p.h> #include <qv4runtime_p.h> +#include "qv4string_p.h" -#include <qjsondocument.h> #include <qstack.h> #include <qstringlist.h> @@ -62,33 +62,6 @@ static int indent = 0; DEFINE_OBJECT_VTABLE(JsonObject); -class JsonParser -{ -public: - JsonParser(ExecutionEngine *engine, const QChar *json, int length); - - ReturnedValue parse(QJsonParseError *error); - -private: - inline bool eatSpace(); - inline QChar nextToken(); - - ReturnedValue parseObject(); - ReturnedValue parseArray(); - bool parseMember(Object *o); - bool parseString(QString *string); - bool parseValue(Value *val); - bool parseNumber(Value *val); - - ExecutionEngine *engine; - const QChar *head; - const QChar *json; - const QChar *end; - - int nestingLevel; - QJsonParseError::ParseError lastError; -}; - static const int nestingLimit = 1024; @@ -639,17 +612,22 @@ bool JsonParser::parseString(QString *string) struct Stringify { - ExecutionContext *ctx; + ExecutionEngine *v4; FunctionObject *replacerFunction; - // ### GC - QVector<Heap::String *> propertyList; + QV4::String *propertyList; + int propertyListSize; QString gap; QString indent; + QStack<Object *> stack; - // ### GC - QStack<Heap::Object *> stack; + bool stackContains(Object *o) { + for (int i = 0; i < stack.size(); ++i) + if (stack.at(i)->d() == o->d()) + return true; + return false; + } - Stringify(ExecutionContext *ctx) : ctx(ctx), replacerFunction(0) {} + Stringify(ExecutionEngine *e) : v4(e), replacerFunction(0), propertyList(0), propertyListSize(0) {} QString Str(const QString &key, const Value &v); QString JA(ArrayObject *a); @@ -701,26 +679,26 @@ static QString quote(const QString &str) QString Stringify::Str(const QString &key, const Value &v) { - Scope scope(ctx); + Scope scope(v4); ScopedValue value(scope, v); ScopedObject o(scope, value); if (o) { - ScopedString s(scope, ctx->d()->engine->newString(QStringLiteral("toJSON"))); + ScopedString s(scope, v4->newString(QStringLiteral("toJSON"))); ScopedFunctionObject toJSON(scope, o->get(s)); if (!!toJSON) { ScopedCallData callData(scope, 1); callData->thisObject = value; - callData->args[0] = ctx->d()->engine->newString(key); + callData->args[0] = v4->newString(key); value = toJSON->call(callData); } } if (replacerFunction) { - ScopedObject holder(scope, ctx->d()->engine->newObject()); + ScopedObject holder(scope, v4->newObject()); holder->put(scope.engine, QString(), value); ScopedCallData callData(scope, 2); - callData->args[0] = ctx->d()->engine->newString(key); + callData->args[0] = v4->newString(key); callData->args[1] = value; callData->thisObject = holder; value = replacerFunction->call(callData); @@ -728,11 +706,11 @@ QString Stringify::Str(const QString &key, const Value &v) o = value->asReturnedValue(); if (o) { - if (NumberObject *n = o->asNumberObject()) + if (NumberObject *n = o->as<NumberObject>()) value = Encode(n->value()); - else if (StringObject *so = o->asStringObject()) - value = so->d()->value; - else if (BooleanObject *b =o->asBooleanObject()) + else if (StringObject *so = o->as<StringObject>()) + value = so->d()->string; + else if (BooleanObject *b = o->as<BooleanObject>()) value = Encode(b->value()); } @@ -750,8 +728,8 @@ QString Stringify::Str(const QString &key, const Value &v) o = value->asReturnedValue(); if (o) { - if (!o->asFunctionObject()) { - if (o->asArrayObject()) { + if (!o->as<FunctionObject>()) { + if (o->as<ArrayObject>()) { return JA(static_cast<ArrayObject *>(o.getPointer())); } else { return JO(o); @@ -777,20 +755,20 @@ QString Stringify::makeMember(const QString &key, const Value &v) QString Stringify::JO(Object *o) { - if (stack.contains(o->d())) { - ctx->engine()->throwTypeError(); + if (stackContains(o)) { + v4->throwTypeError(); return QString(); } - Scope scope(ctx); + Scope scope(v4); QString result; - stack.push(o->d()); + stack.push(o); QString stepback = indent; indent += gap; QStringList partial; - if (propertyList.isEmpty()) { + if (!propertyListSize) { ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); ScopedValue name(scope); @@ -805,11 +783,13 @@ QString Stringify::JO(Object *o) partial += member; } } else { - ScopedString s(scope); - for (int i = 0; i < propertyList.size(); ++i) { + ScopedValue v(scope); + for (int i = 0; i < propertyListSize; ++i) { bool exists; - s = propertyList.at(i); - ScopedValue v(scope, o->get(s, &exists)); + String *s = propertyList + i; + if (!s) + continue; + v = o->get(s, &exists); if (!exists) continue; QString member = makeMember(s->toQString(), v); @@ -834,15 +814,15 @@ QString Stringify::JO(Object *o) QString Stringify::JA(ArrayObject *a) { - if (stack.contains(a->d())) { - ctx->engine()->throwTypeError(); + if (stackContains(a)) { + v4->throwTypeError(); return QString(); } Scope scope(a->engine()); QString result; - stack.push(a->d()); + stack.push(a); QString stepback = indent; indent += gap; @@ -879,7 +859,7 @@ QString Stringify::JA(ArrayObject *a) Heap::JsonObject::JsonObject(ExecutionEngine *e) - : Heap::Object(e->emptyClass, e->objectPrototype.asObject()) + : Heap::Object(e->emptyClass, e->objectPrototype()) { Scope scope(e); ScopedObject o(scope, this); @@ -911,32 +891,38 @@ ReturnedValue JsonObject::method_stringify(CallContext *ctx) { Scope scope(ctx); - Stringify stringify(ctx); + Stringify stringify(scope.engine); ScopedObject o(scope, ctx->argument(1)); if (o) { - stringify.replacerFunction = o->asFunctionObject(); + stringify.replacerFunction = o->as<FunctionObject>(); if (o->isArrayObject()) { uint arrayLen = o->getLength(); - ScopedValue v(scope); + stringify.propertyList = static_cast<QV4::String *>(scope.alloc(arrayLen)); for (uint i = 0; i < arrayLen; ++i) { - v = o->getIndexed(i); - if (v->asNumberObject() || v->asStringObject() || v->isNumber()) - v = RuntimeHelpers::toString(scope.engine, v); - if (v->isString()) { - String *s = v->stringValue(); - if (!stringify.propertyList.contains(s->d())) - stringify.propertyList.append(s->d()); + Value *v = stringify.propertyList + i; + *v = o->getIndexed(i); + if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber()) + *v = RuntimeHelpers::toString(scope.engine, *v); + if (!v->isString()) { + v->setM(0); + } else { + for (uint j = 0; j <i; ++j) { + if (stringify.propertyList[j].m() == v->m()) { + v->setM(0); + break; + } + } } } } } ScopedValue s(scope, ctx->argument(2)); - if (NumberObject *n = s->asNumberObject()) + if (NumberObject *n = s->as<NumberObject>()) s = Encode(n->value()); - else if (StringObject *so = s->asStringObject()) - s = so->d()->value; + else if (StringObject *so = s->as<StringObject>()) + s = so->d()->string; if (s->isNumber()) { stringify.gap = QString(qMin(10, (int)s->toInteger()), ' '); @@ -986,7 +972,7 @@ QJsonValue JsonObject::toJsonValue(const Value &value, V4ObjectSet &visitedObjec return QJsonValue(value.toQString()); Q_ASSERT(value.isObject()); - Scope scope(value.asObject()->engine()); + Scope scope(value.as<Object>()->engine()); ScopedArrayObject a(scope, value); if (a) return toJsonArray(a, visitedObjects); @@ -1009,22 +995,22 @@ QV4::ReturnedValue JsonObject::fromJsonObject(ExecutionEngine *engine, const QJs return o.asReturnedValue(); } -QJsonObject JsonObject::toJsonObject(Object *o, V4ObjectSet &visitedObjects) +QJsonObject JsonObject::toJsonObject(const Object *o, V4ObjectSet &visitedObjects) { QJsonObject result; - if (!o || o->asFunctionObject()) + if (!o || o->as<FunctionObject>()) return result; Scope scope(o->engine()); - if (visitedObjects.contains(o->d())) { + if (visitedObjects.contains(ObjectItem(o))) { // Avoid recursion. // For compatibility with QVariant{List,Map} conversion, we return an // empty object (and no error is thrown). return result; } - visitedObjects.insert(o->d()); + visitedObjects.insert(ObjectItem(o)); ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); ScopedValue name(scope); @@ -1035,11 +1021,11 @@ QJsonObject JsonObject::toJsonObject(Object *o, V4ObjectSet &visitedObjects) break; QString key = name->toQStringNoThrow(); - if (!val->asFunctionObject()) + if (!val->as<FunctionObject>()) result.insert(key, toJsonValue(val, visitedObjects)); } - visitedObjects.remove(o->d()); + visitedObjects.remove(ObjectItem(o)); return result; } @@ -1057,7 +1043,7 @@ QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJso return a.asReturnedValue(); } -QJsonArray JsonObject::toJsonArray(ArrayObject *a, V4ObjectSet &visitedObjects) +QJsonArray JsonObject::toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects) { QJsonArray result; if (!a) @@ -1065,25 +1051,25 @@ QJsonArray JsonObject::toJsonArray(ArrayObject *a, V4ObjectSet &visitedObjects) Scope scope(a->engine()); - if (visitedObjects.contains(a->d())) { + if (visitedObjects.contains(ObjectItem(a))) { // Avoid recursion. // For compatibility with QVariant{List,Map} conversion, we return an // empty array (and no error is thrown). return result; } - visitedObjects.insert(a->d()); + visitedObjects.insert(ObjectItem(a)); ScopedValue v(scope); quint32 length = a->getLength(); for (quint32 i = 0; i < length; ++i) { v = a->getIndexed(i); - if (v->asFunctionObject()) + if (v->as<FunctionObject>()) v = Encode::null(); result.append(toJsonValue(v, visitedObjects)); } - visitedObjects.remove(a->d()); + visitedObjects.remove(ObjectItem(a)); return result; } diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h index 81a783ee92..1ad0e2c5de 100644 --- a/src/qml/jsruntime/qv4jsonobject_p.h +++ b/src/qml/jsruntime/qv4jsonobject_p.h @@ -37,6 +37,8 @@ #include <qjsonarray.h> #include <qjsonobject.h> #include <qjsonvalue.h> +#include <qjsondocument.h> +#include <qhash.h> QT_BEGIN_NAMESPACE @@ -50,12 +52,23 @@ struct JsonObject : Object { } +struct ObjectItem { + const QV4::Object *o; + ObjectItem(const QV4::Object *o) : o(o) {} +}; + +inline bool operator ==(const ObjectItem &a, const ObjectItem &b) +{ return a.o->d() == b.o->d(); } + +inline int qHash(const ObjectItem &i, uint seed = 0) +{ return ::qHash((void *)i.o->d(), seed); } + struct JsonObject : Object { Q_MANAGED_TYPE(JsonObject) V4_OBJECT2(JsonObject, Object) private: - // ### GC - typedef QSet<QV4::Heap::Base *> V4ObjectSet; + + typedef QSet<ObjectItem> V4ObjectSet; public: static ReturnedValue method_parse(CallContext *ctx); @@ -67,18 +80,45 @@ public: static inline QJsonValue toJsonValue(const QV4::Value &value) { V4ObjectSet visitedObjects; return toJsonValue(value, visitedObjects); } - static inline QJsonObject toJsonObject(QV4::Object *o) + static inline QJsonObject toJsonObject(const QV4::Object *o) { V4ObjectSet visitedObjects; return toJsonObject(o, visitedObjects); } - static inline QJsonArray toJsonArray(QV4::ArrayObject *a) + static inline QJsonArray toJsonArray(const QV4::ArrayObject *a) { V4ObjectSet visitedObjects; return toJsonArray(a, visitedObjects); } private: static QJsonValue toJsonValue(const QV4::Value &value, V4ObjectSet &visitedObjects); - static QJsonObject toJsonObject(Object *o, V4ObjectSet &visitedObjects); - static QJsonArray toJsonArray(ArrayObject *a, V4ObjectSet &visitedObjects); + static QJsonObject toJsonObject(const Object *o, V4ObjectSet &visitedObjects); + static QJsonArray toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects); }; +class JsonParser +{ +public: + JsonParser(ExecutionEngine *engine, const QChar *json, int length); + + ReturnedValue parse(QJsonParseError *error); + +private: + inline bool eatSpace(); + inline QChar nextToken(); + + ReturnedValue parseObject(); + ReturnedValue parseArray(); + bool parseMember(Object *o); + bool parseString(QString *string); + bool parseValue(Value *val); + bool parseNumber(Value *val); + + ExecutionEngine *engine; + const QChar *head; + const QChar *json; + const QChar *end; + + int nestingLevel; + QJsonParseError::ParseError lastError; +}; + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 82b20337cb..443427b024 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -33,6 +33,7 @@ #include "qv4lookup_p.h" #include "qv4functionobject_p.h" #include "qv4scopedvalue_p.h" +#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -72,7 +73,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu return Primitive::emptyValue().asReturnedValue(); } -ReturnedValue Lookup::lookup(Object *thisObject, PropertyAttributes *attrs) +ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs) { Heap::Object *obj = thisObject->d(); ExecutionEngine *engine = thisObject->engine(); @@ -123,7 +124,7 @@ ReturnedValue Lookup::indexedGetterFallback(Lookup *l, const Value &object, cons ScopedObject o(scope, object); if (!o) { if (idx < UINT_MAX) { - if (String *str = object.asString()) { + if (const String *str = object.as<String>()) { if (idx >= (uint)str->toQString().length()) { return Encode::undefined(); } @@ -168,7 +169,7 @@ ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const Value &object, con Object *o = object.objectValue(); if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->len) if (!s->data(idx).isEmpty()) return s->data(idx).asReturnedValue(); @@ -200,7 +201,7 @@ void Lookup::indexedSetterFallback(Lookup *l, const Value &object, const Value & uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->len) { s->data(idx) = value; return; @@ -224,7 +225,7 @@ void Lookup::indexedSetterObjectInt(Lookup *l, const Value &object, const Value Object *o = object.objectValue(); if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->len) { s->data(idx) = v; return; @@ -235,7 +236,7 @@ void Lookup::indexedSetterObjectInt(Lookup *l, const Value &object, const Value ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object) { - if (Object *o = object.asObject()) + if (const Object *o = object.as<Object>()) return o->getLookup(l); Object *proto; @@ -244,14 +245,14 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va case Value::Null_Type: return engine->throwTypeError(); case Value::Boolean_Type: - proto = engine->booleanPrototype.asObject(); + proto = engine->booleanPrototype(); break; case Value::Managed_Type: { Q_ASSERT(object.isString()); - proto = engine->stringPrototype.asObject(); + proto = engine->stringPrototype(); Scope scope(engine); ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); - if (name->equals(engine->id_length)) { + if (name->equals(engine->id_length())) { // special case, as the property is on the object itself l->getter = stringLengthGetter; return stringLengthGetter(l, engine, object); @@ -260,7 +261,7 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va } case Value::Integer_Type: default: // Number - proto = engine->numberPrototype.asObject(); + proto = engine->numberPrototype(); } PropertyAttributes attrs; @@ -291,7 +292,7 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Lookup l1 = *l; if (l1.getter == Lookup::getter0 || l1.getter == Lookup::getter1) { - if (Object *o = object.asObject()) { + if (const Object *o = object.as<Object>()) { ReturnedValue v = o->getLookup(l); Lookup l2 = *l; @@ -560,7 +561,7 @@ ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engin ReturnedValue Lookup::stringLengthGetter(Lookup *l, ExecutionEngine *engine, const Value &object) { - if (String *s = object.asString()) + if (const String *s = object.as<String>()) return Encode(s->d()->length()); l->getter = getterGeneric; @@ -569,7 +570,7 @@ ReturnedValue Lookup::stringLengthGetter(Lookup *l, ExecutionEngine *engine, con ReturnedValue Lookup::arrayLengthGetter(Lookup *l, ExecutionEngine *engine, const Value &object) { - if (ArrayObject *a = object.asArrayObject()) + if (const ArrayObject *a = object.as<ArrayObject>()) return a->memberData()->data[Heap::ArrayObject::LengthPropertyIndex].asReturnedValue(); l->getter = getterGeneric; @@ -579,7 +580,7 @@ ReturnedValue Lookup::arrayLengthGetter(Lookup *l, ExecutionEngine *engine, cons ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) { - Object *o = engine->globalObject(); + Object *o = engine->globalObject; PropertyAttributes attrs; ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { @@ -608,7 +609,7 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetter0(Lookup *l, ExecutionEngine *engine) { - Object *o = engine->globalObject(); + Object *o = engine->globalObject; if (l->classList[0] == o->internalClass()) return o->memberData()->data[l->index].asReturnedValue(); @@ -618,7 +619,7 @@ ReturnedValue Lookup::globalGetter0(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetter1(Lookup *l, ExecutionEngine *engine) { - Object *o = engine->globalObject(); + Object *o = engine->globalObject; if (l->classList[0] == o->internalClass() && l->classList[1] == o->prototype()->internalClass) return o->prototype()->memberData->data[l->index].asReturnedValue(); @@ -629,7 +630,7 @@ ReturnedValue Lookup::globalGetter1(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetter2(Lookup *l, ExecutionEngine *engine) { - Heap::Object *o = engine->globalObject()->d(); + Heap::Object *o = engine->globalObject->d(); if (l->classList[0] == o->internalClass) { o = o->prototype; if (l->classList[1] == o->internalClass) { @@ -645,7 +646,7 @@ ReturnedValue Lookup::globalGetter2(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterAccessor0(Lookup *l, ExecutionEngine *engine) { - Object *o = engine->globalObject(); + Object *o = engine->globalObject; if (l->classList[0] == o->internalClass()) { Scope scope(o->engine()); ScopedFunctionObject getter(scope, o->propertyAt(l->index)->getter()); @@ -662,7 +663,7 @@ ReturnedValue Lookup::globalGetterAccessor0(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterAccessor1(Lookup *l, ExecutionEngine *engine) { - Object *o = engine->globalObject(); + Object *o = engine->globalObject; if (l->classList[0] == o->internalClass() && l->classList[1] == o->prototype()->internalClass) { Scope scope(o->engine()); @@ -680,7 +681,7 @@ ReturnedValue Lookup::globalGetterAccessor1(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) { - Heap::Object *o = engine->globalObject()->d(); + Heap::Object *o = engine->globalObject->d(); if (l->classList[0] == o->internalClass) { o = o->prototype; if (l->classList[1] == o->internalClass) { @@ -701,7 +702,7 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) return globalGetterGeneric(l, engine); } -void Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Scope scope(engine); ScopedObject o(scope, object); @@ -716,11 +717,11 @@ void Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, const Value &obje o->setLookup(l, value); } -void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Lookup l1 = *l; - if (Object *o = object.asObject()) { + if (Object *o = object.as<Object>()) { o->setLookup(l, value); if (l->setter == Lookup::setter0) { @@ -735,7 +736,7 @@ void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &o setterFallback(l, engine, object, value); } -void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { QV4::Scope scope(engine); QV4::ScopedObject o(scope, object.toObject(scope.engine)); @@ -745,9 +746,9 @@ void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, const Value &obj } } -void Lookup::setter0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = static_cast<Object *>(object.asManaged()); + Object *o = object.as<Object>(); if (o && o->internalClass() == l->classList[0]) { o->memberData()->data[l->index] = value; return; @@ -756,9 +757,9 @@ void Lookup::setter0(Lookup *l, ExecutionEngine *engine, const Value &object, co setterTwoClasses(l, engine, object, value); } -void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = static_cast<Object *>(object.asManaged()); + Object *o = object.as<Object>(); if (o && o->internalClass() == l->classList[0]) { if (!o->prototype()) { if (!o->memberData() || l->index >= o->memberData()->size) @@ -773,9 +774,9 @@ void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, const Value &obje setterFallback(l, engine, object, value); } -void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = static_cast<Object *>(object.asManaged()); + Object *o = object.as<Object>(); if (o && o->internalClass() == l->classList[0]) { Heap::Object *p = o->prototype(); if (p && p->internalClass == l->classList[1]) { @@ -791,9 +792,9 @@ void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, const Value &obje setterFallback(l, engine, object, value); } -void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = static_cast<Object *>(object.asManaged()); + Object *o = object.as<Object>(); if (o && o->internalClass() == l->classList[0]) { Heap::Object *p = o->prototype(); if (p && p->internalClass == l->classList[1]) { @@ -812,9 +813,9 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, const Value &obje setterFallback(l, engine, object, value); } -void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value) +void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Object *o = static_cast<Object *>(object.asManaged()); + Object *o = object.as<Object>(); if (o) { if (o->internalClass() == l->classList[0]) { o->memberData()->data[l->index] = value; diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 88397dc36c..232e909c48 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -51,7 +51,7 @@ struct Lookup { void (*indexedSetter)(Lookup *l, const Value &object, const Value &index, const Value &v); ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); - void (*setter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &v); + void (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); }; union { ExecutionEngine *engine; @@ -107,17 +107,17 @@ struct Lookup { static ReturnedValue globalGetterAccessor1(Lookup *l, ExecutionEngine *engine); static ReturnedValue globalGetterAccessor2(Lookup *l, ExecutionEngine *engine); - static void setterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setter0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setterInsert0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setterInsert1(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setterInsert2(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); - static void setter0setter0(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &value); + static void setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static void setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); ReturnedValue lookup(const Value &thisObject, Object *obj, PropertyAttributes *attrs); - ReturnedValue lookup(Object *obj, PropertyAttributes *attrs); + ReturnedValue lookup(const Object *obj, PropertyAttributes *attrs); }; diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index c4b9fc597e..bb7ee43b4e 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -32,13 +32,13 @@ ****************************************************************************/ #include "qv4managed_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4errorobject_p.h" using namespace QV4; -const ManagedVTable Managed::static_vtbl = +const VTable Managed::static_vtbl = { 0, Managed::IsExecutionContext, @@ -59,7 +59,7 @@ const ManagedVTable Managed::static_vtbl = QString Managed::className() const { const char *s = 0; - switch (Type(d()->vtable->type)) { + switch (Type(d()->vtable()->type)) { case Type_Invalid: case Type_String: return QString(); diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 0fe5c7ee49..6f5564300f 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -35,7 +35,7 @@ #include "qv4global_p.h" #include "qv4value_p.h" -#include "qv4internalclass_p.h" +#include <private/qv4heap_p.h> QT_BEGIN_NAMESPACE @@ -65,10 +65,10 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} Q_MANAGED_CHECK \ typedef QV4::Heap::DataClass Data; \ typedef superClass SuperClass; \ - static const QV4::ManagedVTable static_vtbl; \ - static inline const QV4::ManagedVTable *staticVTable() { return &static_vtbl; } \ + static const QV4::VTable static_vtbl; \ + static inline const QV4::VTable *staticVTable() { return &static_vtbl; } \ V4_MANAGED_SIZE_TEST \ - QV4::Heap::DataClass *d() const { return static_cast<QV4::Heap::DataClass *>(m); } + QV4::Heap::DataClass *d() const { return static_cast<QV4::Heap::DataClass *>(m()); } #define Q_MANAGED_TYPE(type) \ public: \ @@ -78,31 +78,6 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} (classname::func == QV4::Managed::func ? 0 : classname::func) -struct GCDeletable -{ - GCDeletable() : next(0), lastCall(false) {} - virtual ~GCDeletable() {} - GCDeletable *next; - bool lastCall; -}; - -struct ManagedVTable -{ - const ManagedVTable * const parent; - uint isExecutionContext : 1; - uint isString : 1; - uint isObject : 1; - uint isFunctionObject : 1; - uint isErrorObject : 1; - uint isArrayData : 1; - uint unused : 18; - uint type : 8; - const char *className; - void (*destroy)(Heap::Base *); - void (*markObjects)(Heap::Base *, ExecutionEngine *e); - bool (*isEqualTo)(Managed *m, Managed *other); -}; - #define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ { \ parentVTable, \ @@ -121,7 +96,7 @@ struct ManagedVTable } #define DEFINE_MANAGED_VTABLE(classname) \ -const QV4::ManagedVTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) +const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) struct Q_QML_PRIVATE_EXPORT Managed : Value { @@ -141,8 +116,6 @@ private: public: - inline void mark(QV4::ExecutionEngine *engine); - enum Type { Type_Invalid, Type_String, @@ -167,55 +140,15 @@ public: }; Q_MANAGED_TYPE(Invalid) - template <typename T> - T *as() { - Q_ASSERT(d()->vtable); -#if !defined(QT_NO_QOBJECT_CHECK) - static_cast<T *>(this)->qt_check_for_QMANAGED_macro(static_cast<T *>(this)); -#endif - const ManagedVTable *vt = d()->vtable; - while (vt) { - if (vt == T::staticVTable()) - return static_cast<T *>(this); - vt = vt->parent; - } - return 0; - } - template <typename T> - const T *as() const { - Q_ASSERT(d()->vtable); -#if !defined(QT_NO_QOBJECT_CHECK) - static_cast<T *>(this)->qt_check_for_QMANAGED_macro(static_cast<T *>(const_cast<Managed *>(this))); -#endif - const ManagedVTable *vt = d()->vtable; - while (vt) { - if (vt == T::staticVTable()) - return static_cast<T *>(this); - vt = vt->parent; - } - return 0; - } - - String *asString() { return d()->vtable->isString ? reinterpret_cast<String *>(this) : 0; } - Object *asObject() { return d()->vtable->isObject ? reinterpret_cast<Object *>(this) : 0; } - ArrayObject *asArrayObject() { return d()->vtable->type == Type_ArrayObject ? reinterpret_cast<ArrayObject *>(this) : 0; } - FunctionObject *asFunctionObject() { return d()->vtable->isFunctionObject ? reinterpret_cast<FunctionObject *>(this) : 0; } - BooleanObject *asBooleanObject() { return d()->vtable->type == Type_BooleanObject ? reinterpret_cast<BooleanObject *>(this) : 0; } - NumberObject *asNumberObject() { return d()->vtable->type == Type_NumberObject ? reinterpret_cast<NumberObject *>(this) : 0; } - StringObject *asStringObject() { return d()->vtable->type == Type_StringObject ? reinterpret_cast<StringObject *>(this) : 0; } - DateObject *asDateObject() { return d()->vtable->type == Type_DateObject ? reinterpret_cast<DateObject *>(this) : 0; } - ErrorObject *asErrorObject() { return d()->vtable->isErrorObject ? reinterpret_cast<ErrorObject *>(this) : 0; } - ArgumentsObject *asArgumentsObject() { return d()->vtable->type == Type_ArgumentsObject ? reinterpret_cast<ArgumentsObject *>(this) : 0; } - - bool isListType() const { return d()->vtable->type == Type_QmlSequence; } - - bool isArrayObject() const { return d()->vtable->type == Type_ArrayObject; } - bool isStringObject() const { return d()->vtable->type == Type_StringObject; } + bool isListType() const { return d()->vtable()->type == Type_QmlSequence; } + + bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; } + bool isStringObject() const { return d()->vtable()->type == Type_StringObject; } QString className() const; bool isEqualTo(const Managed *other) const - { return d()->vtable->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); } + { return d()->vtable()->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); } static bool isEqualTo(Managed *m, Managed *other); @@ -231,37 +164,15 @@ private: template<> -inline Managed *value_cast(const Value &v) { - return v.asManaged(); +inline const Managed *Value::as() const { + if (isManaged()) + return managed(); + return 0; } -template<typename T> -inline T *managed_cast(Managed *m) -{ - return m ? m->as<T>() : 0; -} - -template<> -inline String *managed_cast(Managed *m) -{ - return m ? m->asString() : 0; -} template<> -inline Object *managed_cast(Managed *m) -{ - return m ? m->asObject() : 0; -} -template<> -inline FunctionObject *managed_cast(Managed *m) -{ - return m ? m->asFunctionObject() : 0; -} - -inline Value Value::fromManaged(Managed *m) -{ - if (!m) - return QV4::Primitive::undefinedValue(); - return *m; +inline const Object *Value::as() const { + return isManaged() && m() && m()->vtable()->isObject ? objectValue() : 0; } } diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 473e05bf88..c498160c36 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -48,7 +48,7 @@ DEFINE_OBJECT_VTABLE(MathObject); static const double qt_PI = 2.0 * ::asin(1.0); Heap::MathObject::MathObject(ExecutionEngine *e) - : Heap::Object(e->emptyClass, e->objectPrototype.asObject()) + : Heap::Object(e->emptyClass, e->objectPrototype()) { Scope scope(e); ScopedObject m(scope, this); diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp index 03dfee3dcf..5ec5cb1f58 100644 --- a/src/qml/jsruntime/qv4memberdata.cpp +++ b/src/qml/jsruntime/qv4memberdata.cpp @@ -32,7 +32,8 @@ ****************************************************************************/ #include "qv4memberdata_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> +#include "qv4value_p.h" using namespace QV4; diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 4a1a94e872..4ae30a7f35 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -33,6 +33,7 @@ #include "qv4numberobject_p.h" #include "qv4runtime_p.h" +#include "qv4string_p.h" #include <QtCore/qnumeric.h> #include <QtCore/qmath.h> @@ -50,14 +51,14 @@ Heap::NumberCtor::NumberCtor(QV4::ExecutionContext *scope) { } -ReturnedValue NumberCtor::construct(Managed *m, CallData *callData) +ReturnedValue NumberCtor::construct(const Managed *m, CallData *callData) { Scope scope(m->cast<NumberCtor>()->engine()); double dbl = callData->argc ? callData->args[0].toNumber() : 0.; return Encode(scope.engine->newNumberObject(dbl)); } -ReturnedValue NumberCtor::call(Managed *, CallData *callData) +ReturnedValue NumberCtor::call(const Managed *, CallData *callData) { double dbl = callData->argc ? callData->args[0].toNumber() : 0.; return Encode(dbl); @@ -67,8 +68,8 @@ void NumberPrototype::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(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(qSNaN())); ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Primitive::fromDouble(-qInf())); @@ -81,9 +82,9 @@ QT_WARNING_DISABLE_INTEL(239) QT_WARNING_POP defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString); + defineDefaultProperty(engine->id_toString(), method_toString); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); - defineDefaultProperty(engine->id_valueOf, method_valueOf); + defineDefaultProperty(engine->id_valueOf(), method_valueOf); defineDefaultProperty(QStringLiteral("toFixed"), method_toFixed, 1); defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential); defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision); @@ -93,7 +94,7 @@ inline ReturnedValue thisNumberValue(ExecutionContext *ctx) { if (ctx->thisObject().isNumber()) return ctx->thisObject().asReturnedValue(); - NumberObject *n = ctx->thisObject().asNumberObject(); + NumberObject *n = ctx->thisObject().as<NumberObject>(); if (!n) return ctx->engine()->throwTypeError(); return Encode(n->value()); @@ -103,7 +104,7 @@ inline double thisNumber(ExecutionContext *ctx) { if (ctx->thisObject().isNumber()) return ctx->thisObject().asDouble(); - NumberObject *n = ctx->thisObject().asNumberObject(); + NumberObject *n = ctx->thisObject().as<NumberObject>(); if (!n) return ctx->engine()->throwTypeError(); return n->value(); @@ -119,7 +120,7 @@ ReturnedValue NumberPrototype::method_toString(CallContext *ctx) if (ctx->argc() && !ctx->args()[0].isUndefined()) { int radix = ctx->args()[0].toInt32(); if (radix < 2 || radix > 36) - return ctx->engine()->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") + return ctx->engine()->throwError(QStringLiteral("Number.prototype.toString: %0 is not a valid radix") .arg(radix)); if (std::isnan(num)) { @@ -197,7 +198,7 @@ ReturnedValue NumberPrototype::method_toFixed(CallContext *ctx) QString str; if (std::isnan(v)) - str = QString::fromLatin1("NaN"); + str = QStringLiteral("NaN"); else if (qIsInf(v)) str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); else if (v < 1.e21) { diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 205995701b..04798d31fc 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -53,8 +53,8 @@ struct NumberCtor: FunctionObject { V4_OBJECT2(NumberCtor, FunctionObject) - static ReturnedValue construct(Managed *that, CallData *callData); - static ReturnedValue call(Managed *, CallData *callData); + static ReturnedValue construct(const Managed *that, CallData *callData); + static ReturnedValue call(const Managed *, CallData *callData); }; struct NumberPrototype: NumberObject diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 211fd1812e..b3c0863e3e 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -37,12 +37,13 @@ #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4argumentsobject_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4lookup_p.h" #include "qv4scopedvalue_p.h" #include "qv4memberdata_p.h" #include "qv4objectiterator_p.h" #include "qv4identifier_p.h" +#include "qv4string_p.h" #include <stdint.h> @@ -138,7 +139,7 @@ void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(Ca ScopedString s(scope, e->newIdentifier(name)); ScopedContext global(scope, e->rootContext()); ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); - function->defineReadonlyProperty(e->id_length, Primitive::fromInt32(argumentCount)); + function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(s, function); } @@ -148,7 +149,7 @@ void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallConte Scope scope(e); ScopedContext global(scope, e->rootContext()); ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); - function->defineReadonlyProperty(e->id_length, Primitive::fromInt32(argumentCount)); + function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(name, function); } @@ -219,50 +220,50 @@ void Object::insertMember(String *s, const Property *p, PropertyAttributes attri } // Section 8.12.1 -Property *Object::__getOwnProperty__(String *name, PropertyAttributes *attrs) +void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) - return __getOwnProperty__(idx, attrs); + return getOwnProperty(idx, attrs, p); uint member = internalClass()->find(name); if (member < UINT_MAX) { - if (attrs) - *attrs = internalClass()->propertyData[member]; - return propertyAt(member); + *attrs = internalClass()->propertyData[member]; + if (p) + p->copy(propertyAt(member), *attrs); + return; } if (attrs) *attrs = Attr_Invalid; - return 0; + return; } -Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs) +void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p) { - Property *p = arrayData() ? arrayData()->getProperty(index) : 0; - if (p) { - if (attrs) - *attrs = arrayData()->attributes(index); - return p; + Property *pd = arrayData() ? arrayData()->getProperty(index) : 0; + if (pd) { + *attrs = arrayData()->attributes(index); + if (p) + p->copy(pd, *attrs); + return; } if (isStringObject()) { - if (attrs) - *attrs = Attr_NotConfigurable|Attr_NotWritable; - return static_cast<StringObject *>(this)->getIndex(index); + *attrs = Attr_NotConfigurable|Attr_NotWritable; + if (p) + p->value = static_cast<StringObject *>(this)->getIndex(index); + return; } if (attrs) *attrs = Attr_Invalid; - return 0; + return; } // Section 8.12.2 Property *Object::__getPropertyDescriptor__(String *name, PropertyAttributes *attrs) const { - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return __getPropertyDescriptor__(idx, attrs); - + Q_ASSERT(name->asArrayIndex() == UINT_MAX); const Heap::Object *o = d(); while (o) { @@ -286,16 +287,15 @@ Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attr while (o) { Property *p = o->arrayData ? o->arrayData->getProperty(index) : 0; if (p) { - if (attrs) - *attrs = o->arrayData->attributes(index); + *attrs = o->arrayData->attributes(index); return p; } - if (o->vtable->type == Type_StringObject) { - Property *p = static_cast<const Heap::StringObject *>(o)->getIndex(index); - if (p) { - if (attrs) - *attrs = (Attr_NotWritable|Attr_NotConfigurable); - return p; + if (o->vtable()->type == Type_StringObject) { + if (index < static_cast<const Heap::StringObject *>(o)->length()) { + // this is an evil hack, but it works, as the method is only ever called from putIndexed, + // where we don't use the returned pointer there for non writable attributes + *attrs = (Attr_NotWritable|Attr_NotConfigurable); + return reinterpret_cast<Property *>(0x1); } } o = o->prototype; @@ -356,8 +356,7 @@ bool Object::hasOwnProperty(uint index) const return true; if (isStringObject()) { - String *s = static_cast<const StringObject *>(this)->d()->value.asString(); - if (index < (uint)s->d()->length()) + if (index < static_cast<const StringObject *>(this)->length()) return true; } if (!queryIndexed(index).isEmpty()) @@ -365,24 +364,24 @@ bool Object::hasOwnProperty(uint index) const return false; } -ReturnedValue Object::construct(Managed *m, CallData *) +ReturnedValue Object::construct(const Managed *m, CallData *) { - return static_cast<Object *>(m)->engine()->throwTypeError(); + return static_cast<const Object *>(m)->engine()->throwTypeError(); } -ReturnedValue Object::call(Managed *m, CallData *) +ReturnedValue Object::call(const Managed *m, CallData *) { - return static_cast<Object *>(m)->engine()->throwTypeError(); + return static_cast<const Object *>(m)->engine()->throwTypeError(); } -ReturnedValue Object::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty) { - return static_cast<Object *>(m)->internalGet(name, hasProperty); + return static_cast<const Object *>(m)->internalGet(name, hasProperty); } -ReturnedValue Object::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty) { - return static_cast<Object *>(m)->internalGetIndexed(index, hasProperty); + return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty); } void Object::put(Managed *m, String *name, const Value &value) @@ -416,8 +415,7 @@ PropertyAttributes Object::queryIndexed(const Managed *m, uint index) return o->arrayData()->attributes(index); if (o->isStringObject()) { - String *s = static_cast<const StringObject *>(o)->d()->value.asString(); - if (index < (uint)s->d()->length()) + if (index < static_cast<const StringObject *>(o)->length()) return (Attr_NotWritable|Attr_NotConfigurable); } return Attr_Invalid; @@ -433,9 +431,9 @@ bool Object::deleteIndexedProperty(Managed *m, uint index) return static_cast<Object *>(m)->internalDeleteIndexedProperty(index); } -ReturnedValue Object::getLookup(Managed *m, Lookup *l) +ReturnedValue Object::getLookup(const Managed *m, Lookup *l) { - Object *o = static_cast<Object *>(m); + const Object *o = static_cast<const Object *>(m); PropertyAttributes attrs; ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { @@ -516,10 +514,10 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) l->setter = Lookup::setterGeneric; } -void Object::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *pd, PropertyAttributes *attrs) +void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs) { Object *o = static_cast<Object *>(m); - *name = 0; + name->setM(0); *index = UINT_MAX; if (o->arrayData()) { @@ -531,7 +529,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name while (it->arrayNode != o->sparseEnd()) { int k = it->arrayNode->key(); uint pidx = it->arrayNode->value; - Heap::SparseArrayData *sa = static_cast<Heap::SparseArrayData *>(o->d()->arrayData); + Heap::SparseArrayData *sa = o->d()->arrayData.cast<Heap::SparseArrayData>(); Property *p = reinterpret_cast<Property *>(sa->arrayData + pidx); it->arrayNode = it->arrayNode->nextNode(); PropertyAttributes a = sa->attrs ? sa->attrs[pidx] : Attr_Data; @@ -548,7 +546,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name } // dense arrays while (it->arrayIndex < o->d()->arrayData->len) { - Heap::SimpleArrayData *sa = static_cast<Heap::SimpleArrayData *>(o->d()->arrayData); + Heap::SimpleArrayData *sa = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Value &val = sa->data(it->arrayIndex); PropertyAttributes a = o->arrayData()->attributes(it->arrayIndex); ++it->arrayIndex; @@ -574,7 +572,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name PropertyAttributes a = o->internalClass()->propertyData[it->memberIndex]; ++it->memberIndex; if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { - *name = o->engine()->newString(n->string); + name->setM(o->engine()->newString(n->string)); *attrs = a; pd->copy(p, a); return; @@ -585,7 +583,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name } // Section 8.12.3 -ReturnedValue Object::internalGet(String *name, bool *hasProperty) +ReturnedValue Object::internalGet(String *name, bool *hasProperty) const { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) @@ -611,7 +609,7 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) return Encode::undefined(); } -ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) +ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const { Property *pd = 0; PropertyAttributes attrs; @@ -625,10 +623,12 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) break; } if (o->isStringObject()) { - pd = static_cast<StringObject *>(o.getPointer())->getIndex(index); - if (pd) { + ScopedString str(scope, static_cast<StringObject *>(o.getPointer())->getIndex(index)); + if (str) { attrs = (Attr_NotWritable|Attr_NotConfigurable); - break; + if (hasProperty) + *hasProperty = true; + return str.asReturnedValue(); } } o = o->prototype(); @@ -674,7 +674,7 @@ void Object::internalPut(String *name, const Value &value) goto reject; } else if (!attrs.isWritable()) goto reject; - else if (isArrayObject() && name->equals(engine()->id_length)) { + else if (isArrayObject() && name->equals(engine()->id_length())) { bool ok; uint l = value.asArrayLength(&ok); if (!ok) { @@ -710,7 +710,7 @@ void Object::internalPut(String *name, const Value &value) // Clause 5 if (pd && attrs.isAccessor()) { - assert(pd->setter() != 0); + Q_ASSERT(pd->setter() != 0); Scope scope(engine()); ScopedFunctionObject setter(scope, pd->setter()); @@ -745,8 +745,7 @@ void Object::internalPutIndexed(uint index, const Value &value) attrs = arrayData()->attributes(index); if (!pd && isStringObject()) { - pd = static_cast<StringObject *>(this)->getIndex(index); - if (pd) + if (index < static_cast<StringObject *>(this)->length()) // not writable goto reject; } @@ -784,7 +783,7 @@ void Object::internalPutIndexed(uint index, const Value &value) // Clause 5 if (pd && attrs.isAccessor()) { - assert(pd->setter() != 0); + Q_ASSERT(pd->setter() != 0); Scope scope(engine()); ScopedFunctionObject setter(scope, pd->setter()); @@ -858,8 +857,8 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const PropertyAttributes *cattrs; uint memberIndex; - if (isArrayObject() && name->equals(engine->id_length)) { - Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length)); + if (isArrayObject() && name->equals(engine->id_length())) { + Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length())); Property *lp = propertyAt(Heap::ArrayObject::LengthPropertyIndex); cattrs = internalClass()->propertyData.constData() + Heap::ArrayObject::LengthPropertyIndex; if (attrs.isEmpty() || p->isSubset(attrs, lp, *cattrs)) @@ -926,16 +925,16 @@ reject: bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs) { - Property *current = 0; + bool hasProperty = 0; // Clause 1 if (arrayData()) { - current = arrayData()->getProperty(index); - if (!current && isStringObject()) - current = static_cast<StringObject *>(this)->getIndex(index); + hasProperty = arrayData()->getProperty(index); + if (!hasProperty && isStringObject()) + hasProperty = (index < static_cast<StringObject *>(this)->length()); } - if (!current) { + if (!hasProperty) { // clause 3 if (!isExtensible()) goto reject; @@ -1029,9 +1028,9 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * } else { // clause 10 Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor()); if (!cattrs.isConfigurable()) { - if (!p->value.isEmpty() && current->value.val != p->value.val) + if (!p->value.isEmpty() && current->value.rawValue() != p->value.rawValue()) goto reject; - if (!p->set.isEmpty() && current->set.val != p->set.val) + if (!p->set.isEmpty() && current->set.rawValue() != p->set.rawValue()) goto reject; } } @@ -1097,7 +1096,7 @@ void Object::copyArrayData(Object *other) uint Object::getLength(const Managed *m) { Scope scope(static_cast<const Object *>(m)->engine()); - ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(scope.engine->id_length)); + ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(scope.engine->id_length())); return v->toUInt32(); } @@ -1137,7 +1136,7 @@ void Object::initSparseArray() DEFINE_OBJECT_VTABLE(ArrayObject); Heap::ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list) - : Heap::Object(engine->arrayClass, engine->arrayPrototype.asObject()) + : Heap::Object(engine->arrayClass, engine->arrayPrototype()) { init(); Scope scope(engine); @@ -1155,14 +1154,14 @@ Heap::ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list) a->setArrayLengthUnchecked(len); } -ReturnedValue ArrayObject::getLookup(Managed *m, Lookup *l) +ReturnedValue ArrayObject::getLookup(const Managed *m, Lookup *l) { - Scope scope(static_cast<Object *>(m)->engine()); + Scope scope(static_cast<const Object *>(m)->engine()); ScopedString name(scope, scope.engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); - if (name->equals(scope.engine->id_length)) { + if (name->equals(scope.engine->id_length())) { // special case, as the property is on the object itself l->getter = Lookup::arrayLengthGetter; - ArrayObject *a = static_cast<ArrayObject *>(m); + const ArrayObject *a = static_cast<const ArrayObject *>(m); return a->memberData()->data[Heap::ArrayObject::LengthPropertyIndex].asReturnedValue(); } return Object::getLookup(m, l); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 71a997e133..f129312819 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -36,6 +36,9 @@ #include "qv4managed_p.h" #include "qv4memberdata_p.h" #include "qv4arraydata_p.h" +#include "qv4engine_p.h" +#include "qv4scopedvalue_p.h" +#include "qv4value_p.h" QT_BEGIN_NAMESPACE @@ -44,20 +47,16 @@ namespace QV4 { namespace Heap { struct Object : Base { - Object(ExecutionEngine *engine) - : internalClass(engine->emptyClass), - prototype(static_cast<Object *>(engine->objectPrototype.m)) - { - } + inline Object(ExecutionEngine *engine); Object(InternalClass *internal, QV4::Object *prototype); const Property *propertyAt(uint index) const { return reinterpret_cast<const Property *>(memberData->data + index); } Property *propertyAt(uint index) { return reinterpret_cast<Property *>(memberData->data + index); } InternalClass *internalClass; - Heap::Object *prototype; - MemberData *memberData; - ArrayData *arrayData; + Pointer<Object> prototype; + Pointer<MemberData> memberData; + Pointer<ArrayData> arrayData; }; } @@ -67,9 +66,9 @@ struct Object : Base { Q_MANAGED_CHECK \ typedef superClass SuperClass; \ static const QV4::ObjectVTable static_vtbl; \ - static inline const QV4::ManagedVTable *staticVTable() { return &static_vtbl.managedVTable; } \ + static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \ V4_MANAGED_SIZE_TEST \ - Data *d() const { return static_cast<Data *>(m); } + Data *d() const { return static_cast<Data *>(m()); } #define V4_OBJECT2(DataClass, superClass) \ public: \ @@ -77,33 +76,33 @@ struct Object : Base { typedef QV4::Heap::DataClass Data; \ typedef superClass SuperClass; \ static const QV4::ObjectVTable static_vtbl; \ - static inline const QV4::ManagedVTable *staticVTable() { return &static_vtbl.managedVTable; } \ + static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \ V4_MANAGED_SIZE_TEST \ - QV4::Heap::DataClass *d() const { return static_cast<QV4::Heap::DataClass *>(m); } + QV4::Heap::DataClass *d() const { return static_cast<QV4::Heap::DataClass *>(m()); } struct ObjectVTable { - ManagedVTable managedVTable; - ReturnedValue (*call)(Managed *, CallData *data); - ReturnedValue (*construct)(Managed *, CallData *data); - ReturnedValue (*get)(Managed *, String *name, bool *hasProperty); - ReturnedValue (*getIndexed)(Managed *, uint index, bool *hasProperty); + VTable vTable; + ReturnedValue (*call)(const Managed *, CallData *data); + ReturnedValue (*construct)(const Managed *, 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); PropertyAttributes (*query)(const Managed *, String *name); PropertyAttributes (*queryIndexed)(const Managed *, uint index); bool (*deleteProperty)(Managed *m, String *name); bool (*deleteIndexedProperty)(Managed *m, uint index); - ReturnedValue (*getLookup)(Managed *m, Lookup *l); + ReturnedValue (*getLookup)(const Managed *m, Lookup *l); void (*setLookup)(Managed *m, Lookup *l, const Value &v); uint (*getLength)(const Managed *m); - void (*advanceIterator)(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes); + void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); }; #define DEFINE_OBJECT_VTABLE(classname) \ const QV4::ObjectVTable classname::static_vtbl = \ { \ - DEFINE_MANAGED_VTABLE_INT(classname, &classname::SuperClass::static_vtbl == &Object::static_vtbl ? 0 : &classname::SuperClass::static_vtbl.managedVTable), \ + DEFINE_MANAGED_VTABLE_INT(classname, &classname::SuperClass::static_vtbl == &Object::static_vtbl ? 0 : &classname::SuperClass::static_vtbl.vTable), \ call, \ construct, \ get, \ @@ -141,15 +140,15 @@ struct Q_QML_EXPORT Object: Managed { const Property *propertyAt(uint index) const { return d()->propertyAt(index); } Property *propertyAt(uint index) { return d()->propertyAt(index); } - const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable); } + const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); } Heap::Object *prototype() const { return d()->prototype; } bool setPrototype(Object *proto); - Property *__getOwnProperty__(String *name, PropertyAttributes *attrs = 0); - Property *__getOwnProperty__(uint index, PropertyAttributes *attrs = 0); + void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = 0); + void getOwnProperty(uint index, PropertyAttributes *attrs, Property *p = 0); - Property *__getPropertyDescriptor__(String *name, PropertyAttributes *attrs = 0) const; - Property *__getPropertyDescriptor__(uint index, PropertyAttributes *attrs = 0) const; + Property *__getPropertyDescriptor__(String *name, PropertyAttributes *attrs) const; + Property *__getPropertyDescriptor__(uint index, PropertyAttributes *attrs) const; bool hasProperty(String *name) const; bool hasProperty(uint index) const; @@ -272,9 +271,9 @@ public: } void ensureMemberIndex(uint idx); - inline ReturnedValue get(String *name, bool *hasProperty = 0) + inline ReturnedValue get(String *name, bool *hasProperty = 0) const { return vtable()->get(this, name, hasProperty); } - inline ReturnedValue getIndexed(uint idx, bool *hasProperty = 0) + 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); } @@ -288,38 +287,38 @@ public: { return vtable()->deleteProperty(this, name); } bool deleteIndexedProperty(uint index) { return vtable()->deleteIndexedProperty(this, index); } - ReturnedValue getLookup(Lookup *l) + ReturnedValue getLookup(Lookup *l) const { return vtable()->getLookup(this, l); } void setLookup(Lookup *l, const Value &v) { vtable()->setLookup(this, l, v); } - void advanceIterator(ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes) + 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); } - inline ReturnedValue construct(CallData *d) + inline ReturnedValue construct(CallData *d) const { return vtable()->construct(this, d); } - inline ReturnedValue call(CallData *d) + inline ReturnedValue call(CallData *d) const { return vtable()->call(this, d); } protected: static void markObjects(Heap::Base *that, ExecutionEngine *e); - static ReturnedValue construct(Managed *m, CallData *); - static ReturnedValue call(Managed *m, CallData *); - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue construct(const Managed *m, CallData *); + static ReturnedValue call(const Managed *m, 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 PropertyAttributes query(const Managed *m, String *name); static PropertyAttributes queryIndexed(const Managed *m, uint index); static bool deleteProperty(Managed *m, String *name); static bool deleteIndexedProperty(Managed *m, uint index); - static ReturnedValue getLookup(Managed *m, Lookup *l); + static ReturnedValue getLookup(const Managed *m, Lookup *l); static void setLookup(Managed *m, Lookup *l, const Value &v); - static void advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes); + static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static uint getLength(const Managed *m); private: - ReturnedValue internalGet(String *name, bool *hasProperty); - ReturnedValue internalGetIndexed(uint index, bool *hasProperty); + 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 internalDeleteProperty(String *name); @@ -331,6 +330,12 @@ private: namespace Heap { +inline Object::Object(ExecutionEngine *engine) + : internalClass(engine->emptyClass), + prototype(static_cast<Object *>(engine->objectPrototype()->m())) +{ +} + struct BooleanObject : Object { BooleanObject(InternalClass *ic, QV4::Object *prototype) : Object(ic, prototype), @@ -339,7 +344,7 @@ struct BooleanObject : Object { } BooleanObject(ExecutionEngine *engine, bool b) - : Object(engine->emptyClass, engine->booleanPrototype.asObject()), + : Object(engine->emptyClass, engine->booleanPrototype()), b(b) { } @@ -354,7 +359,7 @@ struct NumberObject : Object { } NumberObject(ExecutionEngine *engine, double val) - : Object(engine->emptyClass, engine->numberPrototype.asObject()), + : Object(engine->emptyClass, engine->numberPrototype()), value(val) { } @@ -367,7 +372,7 @@ struct ArrayObject : Object { }; ArrayObject(ExecutionEngine *engine) - : Heap::Object(engine->arrayClass, engine->arrayPrototype.asObject()) + : Heap::Object(engine->arrayClass, engine->arrayPrototype()) { init(); } ArrayObject(ExecutionEngine *engine, const QStringList &list); ArrayObject(InternalClass *ic, QV4::Object *prototype) @@ -400,7 +405,7 @@ struct ArrayObject: Object { void init(ExecutionEngine *engine); - static ReturnedValue getLookup(Managed *m, Lookup *l); + static ReturnedValue getLookup(const Managed *m, Lookup *l); using Object::getLength; static uint getLength(const Managed *m); @@ -454,14 +459,10 @@ inline void Object::arraySet(uint index, const Value &value) setArrayLengthUnchecked(index + 1); } -template<> -inline Object *value_cast(const Value &v) { - return v.asObject(); -} template<> -inline ArrayObject *value_cast(const Value &v) { - return v.asArrayObject(); +inline const ArrayObject *Value::as() const { + return isManaged() && m() && m()->vtable()->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : 0; } #ifndef V4_BOOTSTRAP diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index f36ee554a7..585f9f5c2e 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -35,6 +35,7 @@ #include "qv4stringobject_p.h" #include "qv4identifier_p.h" #include "qv4argumentsobject_p.h" +#include "qv4string_p.h" using namespace QV4; @@ -50,7 +51,7 @@ ObjectIterator::ObjectIterator(ExecutionEngine *e, Value *scratch1, Value *scrat init(o); } -ObjectIterator::ObjectIterator(Scope &scope, Object *o, uint flags) +ObjectIterator::ObjectIterator(Scope &scope, const Object *o, uint flags) : engine(scope.engine) , object(scope.alloc(1)) , current(scope.alloc(1)) @@ -62,14 +63,14 @@ ObjectIterator::ObjectIterator(Scope &scope, Object *o, uint flags) init(o); } -void ObjectIterator::init(Object *o) +void ObjectIterator::init(const Object *o) { - object->m = o ? o->m : 0; - current->m = o ? o->m : 0; + object->setM(o ? o->m() : 0); + current->setM(o ? o->m() : 0); #if QT_POINTER_SIZE == 4 - object->tag = QV4::Value::Managed_Type; - current->tag = QV4::Value::Managed_Type; + object->setTag(QV4::Value::Managed_Type); + current->setTag(QV4::Value::Managed_Type); #endif if (object->as<ArgumentsObject>()) { @@ -78,12 +79,12 @@ void ObjectIterator::init(Object *o) } } -void ObjectIterator::next(Heap::String **name, uint *index, Property *pd, PropertyAttributes *attrs) +void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttributes *attrs) { - *name = 0; + name->setM(0); *index = UINT_MAX; - if (!object->asObject()) { + if (!object->as<Object>()) { *attrs = PropertyAttributes(); return; } @@ -92,19 +93,19 @@ void ObjectIterator::next(Heap::String **name, uint *index, Property *pd, Proper ScopedString n(scope); while (1) { - if (!current->asObject()) + if (!current->as<Object>()) break; while (1) { - current->asObject()->advanceIterator(this, name, index, pd, attrs); + current->as<Object>()->advanceIterator(this, name, index, pd, attrs); if (attrs->isEmpty()) break; // check the property is not already defined earlier in the proto chain if (current->heapObject() != object->heapObject()) { - o = object->asObject(); + o = object->as<Object>(); n = *name; bool shadowed = false; - while (o->asObject()->d() != current->heapObject()) { + while (o->d() != current->heapObject()) { if ((!!n && o->hasOwnProperty(n)) || (*index != UINT_MAX && o->hasOwnProperty(*index))) { shadowed = true; @@ -119,9 +120,9 @@ void ObjectIterator::next(Heap::String **name, uint *index, Property *pd, Proper } if (flags & WithProtoChain) - current->m = current->objectValue()->prototype(); + current->setM(current->objectValue()->prototype()); else - current->m = (Heap::Base *)0; + current->setM(0); arrayIndex = 0; memberIndex = 0; @@ -131,7 +132,7 @@ void ObjectIterator::next(Heap::String **name, uint *index, Property *pd, Proper ReturnedValue ObjectIterator::nextPropertyName(Value *value) { - if (!object->asObject()) + if (!object->as<Object>()) return Encode::null(); PropertyAttributes attrs; @@ -147,13 +148,13 @@ ReturnedValue ObjectIterator::nextPropertyName(Value *value) if (!!name) return name->asReturnedValue(); - assert(index < UINT_MAX); + Q_ASSERT(index < UINT_MAX); return Encode(index); } ReturnedValue ObjectIterator::nextPropertyNameAsString(Value *value) { - if (!object->asObject()) + if (!object->as<Object>()) return Encode::null(); PropertyAttributes attrs; @@ -169,13 +170,13 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString(Value *value) if (!!name) return name->asReturnedValue(); - assert(index < UINT_MAX); + Q_ASSERT(index < UINT_MAX); return Encode(engine->newString(QString::number(index))); } ReturnedValue ObjectIterator::nextPropertyNameAsString() { - if (!object->asObject()) + if (!object->as<Object>()) return Encode::null(); PropertyAttributes attrs; diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index a7abd2ca10..bfe04b33aa 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -57,9 +57,9 @@ struct Q_QML_EXPORT ObjectIterator uint flags; ObjectIterator(ExecutionEngine *e, Value *scratch1, Value *scratch2, Object *o, uint flags); - ObjectIterator(Scope &scope, Object *o, uint flags); - void init(Object *o); - void next(Heap::String **name, uint *index, Property *pd, PropertyAttributes *attributes = 0); + ObjectIterator(Scope &scope, const Object *o, uint flags); + void init(const Object *o); + void next(Value *name, uint *index, Property *pd, PropertyAttributes *attributes = 0); ReturnedValue nextPropertyName(Value *value); ReturnedValue nextPropertyNameAsString(Value *value); ReturnedValue nextPropertyNameAsString(); diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 9356ea434e..1edf76e2de 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -34,10 +34,11 @@ #include "qv4objectproto_p.h" #include "qv4argumentsobject_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4runtime_p.h" #include "qv4objectiterator_p.h" +#include "qv4string_p.h" #include <QtCore/QDateTime> #include <QtCore/QStringList> @@ -52,14 +53,14 @@ Heap::ObjectCtor::ObjectCtor(QV4::ExecutionContext *scope) { } -ReturnedValue ObjectCtor::construct(Managed *that, CallData *callData) +ReturnedValue ObjectCtor::construct(const Managed *that, CallData *callData) { - ObjectCtor *ctor = static_cast<ObjectCtor *>(that); + const ObjectCtor *ctor = static_cast<const ObjectCtor *>(that); ExecutionEngine *v4 = ctor->engine(); Scope scope(v4); if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) { ScopedObject obj(scope, v4->newObject()); - ScopedObject proto(scope, ctor->get(v4->id_prototype)); + ScopedObject proto(scope, ctor->get(v4->id_prototype())); if (!!proto) obj->setPrototype(proto); return obj.asReturnedValue(); @@ -67,9 +68,9 @@ ReturnedValue ObjectCtor::construct(Managed *that, CallData *callData) return RuntimeHelpers::toObject(scope.engine, callData->args[0]); } -ReturnedValue ObjectCtor::call(Managed *m, CallData *callData) +ReturnedValue ObjectCtor::call(const Managed *m, CallData *callData) { - ObjectCtor *ctor = static_cast<ObjectCtor *>(m); + const ObjectCtor *ctor = static_cast<const ObjectCtor *>(m); ExecutionEngine *v4 = ctor->engine(); if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) return v4->newObject()->asReturnedValue(); @@ -81,8 +82,8 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) Scope scope(v4); ScopedObject o(scope, this); - ctor->defineReadonlyProperty(v4->id_prototype, o); - ctor->defineReadonlyProperty(v4->id_length, Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(v4->id_prototype(), o); + ctor->defineReadonlyProperty(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); @@ -98,9 +99,9 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) ctor->defineDefaultProperty(QStringLiteral("keys"), method_keys, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(v4->id_toString, method_toString, 0); + defineDefaultProperty(v4->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); - defineDefaultProperty(v4->id_valueOf, method_valueOf, 0); + defineDefaultProperty(v4->id_valueOf(), method_valueOf, 0); defineDefaultProperty(QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); defineDefaultProperty(QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); defineDefaultProperty(QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); @@ -109,9 +110,9 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) ScopedContext global(scope, scope.engine->rootContext()); ScopedProperty p(scope); - p->value = BuiltinFunction::create(global, v4->id___proto__, method_get_proto); - p->set = BuiltinFunction::create(global, v4->id___proto__, method_set_proto); - insertMember(v4->id___proto__, p, Attr_Accessor|Attr_NotEnumerable); + p->value = BuiltinFunction::create(global, v4->id___proto__(), method_get_proto); + p->set = BuiltinFunction::create(global, v4->id___proto__(), method_set_proto); + insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable); } ReturnedValue ObjectPrototype::method_getPrototypeOf(CallContext *ctx) @@ -140,8 +141,9 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(CallContext *ctx) if (scope.hasException()) return Encode::undefined(); PropertyAttributes attrs; - Property *desc = O->__getOwnProperty__(name, &attrs); - return fromPropertyDescriptor(scope.engine, desc, attrs); + Property desc; + O->getOwnProperty(name, &attrs, &desc); + return fromPropertyDescriptor(scope.engine, &desc, attrs); } ReturnedValue ObjectPrototype::method_getOwnPropertyNames(CallContext *context) @@ -163,7 +165,7 @@ ReturnedValue ObjectPrototype::method_create(CallContext *ctx) return ctx->engine()->throwTypeError(); ScopedObject newObject(scope, ctx->d()->engine->newObject()); - newObject->setPrototype(O->asObject()); + newObject->setPrototype(O->as<Object>()); if (ctx->argc() > 1 && !ctx->args()[1].isUndefined()) { ctx->d()->callData->args[0] = newObject.asReturnedValue(); @@ -390,7 +392,7 @@ ReturnedValue ObjectPrototype::method_toString(CallContext *ctx) } else { ScopedObject obj(scope, RuntimeHelpers::toObject(scope.engine, ctx->thisObject())); QString className = obj->className(); - return ctx->d()->engine->newString(QString::fromLatin1("[object %1]").arg(className))->asReturnedValue(); + return ctx->d()->engine->newString(QStringLiteral("[object %1]").arg(className))->asReturnedValue(); } } @@ -400,7 +402,7 @@ ReturnedValue ObjectPrototype::method_toLocaleString(CallContext *ctx) ScopedObject o(scope, ctx->thisObject().toObject(scope.engine)); if (!o) return Encode::undefined(); - ScopedFunctionObject f(scope, o->get(ctx->d()->engine->id_toString)); + ScopedFunctionObject f(scope, o->get(ctx->d()->engine->id_toString())); if (!f) return ctx->engine()->throwTypeError(); ScopedCallData callData(scope); @@ -462,7 +464,7 @@ ReturnedValue ObjectPrototype::method_propertyIsEnumerable(CallContext *ctx) if (scope.engine->hasException) return Encode::undefined(); PropertyAttributes attrs; - o->__getOwnProperty__(p, &attrs); + o->getOwnProperty(p, &attrs); return Encode(attrs.isEnumerable()); } @@ -484,7 +486,7 @@ ReturnedValue ObjectPrototype::method_defineGetter(CallContext *ctx) if (!o) { if (!ctx->thisObject().isUndefined()) return Encode::undefined(); - o = ctx->d()->engine->globalObject(); + o = ctx->d()->engine->globalObject; } ScopedProperty pd(scope); @@ -512,7 +514,7 @@ ReturnedValue ObjectPrototype::method_defineSetter(CallContext *ctx) if (!o) { if (!ctx->thisObject().isUndefined()) return Encode::undefined(); - o = ctx->d()->engine->globalObject(); + o = ctx->d()->engine->globalObject; } ScopedProperty pd(scope); @@ -525,7 +527,7 @@ ReturnedValue ObjectPrototype::method_defineSetter(CallContext *ctx) ReturnedValue ObjectPrototype::method_get_proto(CallContext *ctx) { Scope scope(ctx); - ScopedObject o(scope, ctx->thisObject().asObject()); + ScopedObject o(scope, ctx->thisObject().as<Object>()); if (!o) return ctx->engine()->throwTypeError(); @@ -572,15 +574,15 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value desc->set = Primitive::emptyValue(); ScopedValue tmp(scope); - if (o->hasProperty(engine->id_enumerable)) - attrs->setEnumerable((tmp = o->get(engine->id_enumerable))->toBoolean()); + if (o->hasProperty(engine->id_enumerable())) + attrs->setEnumerable((tmp = o->get(engine->id_enumerable()))->toBoolean()); - if (o->hasProperty(engine->id_configurable)) - attrs->setConfigurable((tmp = o->get(engine->id_configurable))->toBoolean()); + if (o->hasProperty(engine->id_configurable())) + attrs->setConfigurable((tmp = o->get(engine->id_configurable()))->toBoolean()); - if (o->hasProperty(engine->id_get)) { - ScopedValue get(scope, o->get(engine->id_get)); - FunctionObject *f = get->asFunctionObject(); + if (o->hasProperty(engine->id_get())) { + ScopedValue get(scope, o->get(engine->id_get())); + FunctionObject *f = get->as<FunctionObject>(); if (f || get->isUndefined()) { desc->value = get; } else { @@ -590,9 +592,9 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value attrs->setType(PropertyAttributes::Accessor); } - if (o->hasProperty(engine->id_set)) { - ScopedValue set(scope, o->get(engine->id_set)); - FunctionObject *f = set->asFunctionObject(); + if (o->hasProperty(engine->id_set())) { + ScopedValue set(scope, o->get(engine->id_set())); + FunctionObject *f = set->as<FunctionObject>(); if (f || set->isUndefined()) { desc->set = set; } else { @@ -602,22 +604,22 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value attrs->setType(PropertyAttributes::Accessor); } - if (o->hasProperty(engine->id_writable)) { + if (o->hasProperty(engine->id_writable())) { if (attrs->isAccessor()) { engine->throwTypeError(); return; } - attrs->setWritable((tmp = o->get(engine->id_writable))->toBoolean()); + attrs->setWritable((tmp = o->get(engine->id_writable()))->toBoolean()); // writable forces it to be a data descriptor desc->value = Primitive::undefinedValue(); } - if (o->hasProperty(engine->id_value)) { + if (o->hasProperty(engine->id_value())) { if (attrs->isAccessor()) { engine->throwTypeError(); return; } - desc->value = o->get(engine->id_value); + desc->value = o->get(engine->id_value()); attrs->setType(PropertyAttributes::Data); } diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index 4e96681017..d571e50cd4 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -53,8 +53,8 @@ struct ObjectCtor: FunctionObject { V4_OBJECT2(ObjectCtor, FunctionObject) - static ReturnedValue construct(Managed *that, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct ObjectPrototype: Object diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 88dc1946b8..4ec7103644 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -32,7 +32,7 @@ ****************************************************************************/ #include "qv4persistent_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4object_p.h" #include "PageAllocation.h" @@ -78,11 +78,11 @@ Page *allocatePage(PersistentValueStorage *storage) if (p->header.next) p->header.next->header.prev = &p->header.next; for (int i = 0; i < kEntriesPerPage - 1; ++i) { - p->values[i].tag = QV4::Value::Empty_Type; - p->values[i].int_32 = i + 1; + p->values[i].setTag(QV4::Value::Empty_Type); + p->values[i].setInt_32(i + 1); } - p->values[kEntriesPerPage - 1].tag = QV4::Value::Empty_Type; - p->values[kEntriesPerPage - 1].int_32 = -1; + p->values[kEntriesPerPage - 1].setTag(QV4::Value::Empty_Type); + p->values[kEntriesPerPage - 1].setInt_32(-1); storage->firstPage = p; @@ -97,7 +97,7 @@ PersistentValueStorage::Iterator &PersistentValueStorage::Iterator::operator++() while (p) { while (index < kEntriesPerPage - 1) { ++index; - if (static_cast<Page *>(p)->values[index].tag != QV4::Value::Empty_Type) + if (static_cast<Page *>(p)->values[index].tag() != QV4::Value::Empty_Type) return *this; } index = -1; @@ -147,10 +147,10 @@ Value *PersistentValueStorage::allocate() p = allocatePage(this); Value *v = p->values + p->header.freeList; - p->header.freeList = v->int_32; + p->header.freeList = v->int_32(); ++p->header.refCount; - v->val = Encode::undefined(); + v->setRawValue(Encode::undefined()); return v; } @@ -162,8 +162,8 @@ void PersistentValueStorage::free(Value *v) Page *p = getPage(v); - v->tag = QV4::Value::Empty_Type; - v->int_32 = p->header.freeList; + v->setTag(QV4::Value::Empty_Type); + v->setInt_32(p->header.freeList); p->header.freeList = v - p->values; if (!--p->header.refCount) { if (p->header.prev) @@ -178,8 +178,8 @@ static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) { while (engine->jsStackTop > markBase) { Heap::Base *h = engine->popForGC(); - Q_ASSERT (h->gcGetVtable()->markObjects); - h->gcGetVtable()->markObjects(h, engine); + Q_ASSERT (h->vtable()->markObjects); + h->vtable()->markObjects(h, engine); } } @@ -190,7 +190,7 @@ void PersistentValueStorage::mark(ExecutionEngine *e) Page *p = static_cast<Page *>(firstPage); while (p) { for (int i = 0; i < kEntriesPerPage; ++i) { - if (Managed *m = p->values[i].asManaged()) + if (Managed *m = p->values[i].as<Managed>()) m->mark(e); } drainMarkStack(e, markBase); @@ -312,6 +312,12 @@ WeakValue::WeakValue(const WeakValue &other) } } +WeakValue::WeakValue(ExecutionEngine *engine, const Value &value) +{ + val = engine->memoryManager->m_weakValues->allocate(); + *val = value; +} + WeakValue &WeakValue::operator=(const WeakValue &other) { if (!val) { diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index 7cac2ed95f..858734e9ed 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -33,7 +33,8 @@ #ifndef QV4PERSISTENT_H #define QV4PERSISTENT_H -#include "qv4value_inl_p.h" +#include "qv4value_p.h" +#include "qv4managed_p.h" QT_BEGIN_NAMESPACE @@ -96,7 +97,13 @@ public: Managed *asManaged() const { if (!val) return 0; - return val->asManaged(); + return val->as<Managed>(); + } + template<typename T> + T *as() const { + if (!val) + return 0; + return val->as<T>(); } ExecutionEngine *engine() const { @@ -122,6 +129,7 @@ class Q_QML_EXPORT WeakValue public: WeakValue() : val(0) {} WeakValue(const WeakValue &other); + WeakValue(ExecutionEngine *engine, const Value &value); WeakValue &operator=(const WeakValue &other); ~WeakValue(); @@ -138,7 +146,13 @@ public: Managed *asManaged() const { if (!val) return 0; - return val->asManaged(); + return val->as<Managed>(); + } + template <typename T> + T *as() const { + if (!val) + return 0; + return val->as<T>(); } ExecutionEngine *engine() const { diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index a7019d0558..9b77599904 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -32,7 +32,8 @@ ****************************************************************************/ #include "qv4profiling_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> +#include <private/qv4string_p.h> QT_BEGIN_NAMESPACE @@ -55,10 +56,10 @@ FunctionCallProperties FunctionCall::resolve() const Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine(engine) { - static int metatype = qRegisterMetaType<QList<QV4::Profiling::FunctionCallProperties> >(); - static int metatype2 = qRegisterMetaType<QList<QV4::Profiling::MemoryAllocationProperties> >(); - Q_UNUSED(metatype); - Q_UNUSED(metatype2); + static int meta = qRegisterMetaType<QVector<QV4::Profiling::FunctionCallProperties> >(); + static int meta2 = qRegisterMetaType<QVector<QV4::Profiling::MemoryAllocationProperties> >(); + Q_UNUSED(meta); + Q_UNUSED(meta2); m_timer.start(); } @@ -75,7 +76,7 @@ void Profiler::stopProfiling() void Profiler::reportData() { - QList<FunctionCallProperties> resolved; + QVector<FunctionCallProperties> resolved; resolved.reserve(m_data.size()); FunctionCallComparator comp; foreach (const FunctionCall &call, m_data) { @@ -83,14 +84,13 @@ void Profiler::reportData() resolved.insert(std::upper_bound(resolved.begin(), resolved.end(), props, comp), props); } emit dataReady(resolved, m_memory_data); + m_data.clear(); + m_memory_data.clear(); } void Profiler::startProfiling(quint64 features) { if (featuresEnabled == 0) { - m_data.clear(); - m_memory_data.clear(); - if (features & (1 << FeatureMemoryAllocation)) { qint64 timestamp = m_timer.nsecsElapsed(); MemoryAllocationProperties heap = {timestamp, diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index c3441eaacd..cc00af0193 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -155,14 +155,14 @@ public slots: void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: - void dataReady(const QList<QV4::Profiling::FunctionCallProperties> &, - const QList<QV4::Profiling::MemoryAllocationProperties> &); + void dataReady(const QVector<QV4::Profiling::FunctionCallProperties> &, + const QVector<QV4::Profiling::MemoryAllocationProperties> &); private: QV4::ExecutionEngine *m_engine; QElapsedTimer m_timer; QVector<FunctionCall> m_data; - QList<MemoryAllocationProperties> m_memory_data; + QVector<MemoryAllocationProperties> m_memory_data; friend class FunctionCallProfiler; }; @@ -202,7 +202,7 @@ Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCallProperties, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCall, Q_MOVABLE_TYPE); QT_END_NAMESPACE -Q_DECLARE_METATYPE(QList<QV4::Profiling::FunctionCallProperties>) -Q_DECLARE_METATYPE(QList<QV4::Profiling::MemoryAllocationProperties>) +Q_DECLARE_METATYPE(QVector<QV4::Profiling::FunctionCallProperties>) +Q_DECLARE_METATYPE(QVector<QV4::Profiling::MemoryAllocationProperties>) #endif // QV4PROFILING_H diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h index 1b55abd1f7..db8c6017e1 100644 --- a/src/qml/jsruntime/qv4property_p.h +++ b/src/qml/jsruntime/qv4property_p.h @@ -35,7 +35,6 @@ #include "qv4global_p.h" #include "qv4value_p.h" -#include "qv4internalclass_p.h" QT_BEGIN_NAMESPACE @@ -73,8 +72,8 @@ struct Property { inline Heap::FunctionObject *getter() const { return value.isManaged() ? reinterpret_cast<Heap::FunctionObject *>(value.heapObject()) : 0; } inline Heap::FunctionObject *setter() const { return set.isManaged() ? reinterpret_cast<Heap::FunctionObject *>(set.heapObject()) : 0; } - inline void setGetter(FunctionObject *g) { value = Primitive::fromManaged(reinterpret_cast<Managed *>(g)); } - inline void setSetter(FunctionObject *s) { set = s ? Primitive::fromManaged(reinterpret_cast<Managed *>(s)) : Value::fromHeapObject(0); } + inline void setGetter(FunctionObject *g) { value = reinterpret_cast<Managed *>(g); } + inline void setSetter(FunctionObject *s) { set = (s ? reinterpret_cast<Managed *>(s) : 0); } void copy(const Property *other, PropertyAttributes attrs) { value = other->value; @@ -85,12 +84,12 @@ struct Property { explicit Property() { value = Encode::undefined(); set = Value::fromHeapObject(0); } explicit Property(Value v) : value(v) { set = Value::fromHeapObject(0); } Property(FunctionObject *getter, FunctionObject *setter) { - value = Primitive::fromManaged(reinterpret_cast<Managed *>(getter)); - set = Primitive::fromManaged(reinterpret_cast<Managed *>(setter)); + value = reinterpret_cast<Managed *>(getter); + set = reinterpret_cast<Managed *>(setter); } Property(Heap::FunctionObject *getter, Heap::FunctionObject *setter) { - value.m = reinterpret_cast<Heap::Base *>(getter); - set.m = reinterpret_cast<Heap::Base *>(setter); + value.setM(reinterpret_cast<Heap::Base *>(getter)); + set.setM(reinterpret_cast<Heap::Base *>(setter)); } Property &operator=(Value v) { value = v; return *this; } private: diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 0a1aa56aab..e5946d8be6 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -55,6 +55,7 @@ #include <private/qv4objectproto_p.h> #include <private/qv4jsonobject_p.h> #include <private/qv4regexpobject_p.h> +#include <private/qv4dateobject_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qv4mm_p.h> #include <private/qqmlscriptstring_p.h> @@ -92,7 +93,7 @@ static QPair<QObject *, int> extractQtMethod(QV4::FunctionObject *function) static QPair<QObject *, int> extractQtSignal(const Value &value) { if (value.isObject()) { - QV4::ExecutionEngine *v4 = value.asObject()->engine(); + QV4::ExecutionEngine *v4 = value.as<Object>()->engine(); QV4::Scope scope(v4); QV4::ScopedFunctionObject function(scope, value); if (function) @@ -235,8 +236,8 @@ Heap::QObjectWrapper::QObjectWrapper(ExecutionEngine *engine, QObject *object) void QObjectWrapper::initializeBindings(ExecutionEngine *engine) { - engine->functionPrototype.asObject()->defineDefaultProperty(QStringLiteral("connect"), method_connect); - engine->functionPrototype.asObject()->defineDefaultProperty(QStringLiteral("disconnect"), method_disconnect); + engine->functionPrototype()->defineDefaultProperty(QStringLiteral("connect"), method_connect); + engine->functionPrototype()->defineDefaultProperty(QStringLiteral("disconnect"), method_disconnect); } QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const @@ -255,7 +256,7 @@ QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlCont } ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *n, QObjectWrapper::RevisionMode revisionMode, - bool *hasProperty, bool includeImports) + bool *hasProperty, bool includeImports) const { if (QQmlData::wasDeleted(d()->object)) { if (hasProperty) @@ -266,8 +267,8 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String QV4::Scope scope(engine()); QV4::ScopedString name(scope, n); - if (name->equals(scope.engine->id_destroy) || name->equals(scope.engine->id_toString)) { - int index = name->equals(scope.engine->id_destroy) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; + if (name->equals(scope.engine->id_destroy()) || name->equals(scope.engine->id_toString())) { + int index = name->equals(scope.engine->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; ScopedContext global(scope, scope.engine->rootContext()); QV4::ScopedValue method(scope, QV4::QObjectMethod::create(global, d()->object, index)); if (hasProperty) @@ -317,13 +318,12 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String if (hasProperty) *hasProperty = true; - ScopedContext ctx(scope, scope.engine->currentContext()); - return getProperty(d()->object, ctx, result); + return getProperty(scope.engine, d()->object, result); } -ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, bool captureRequired) +ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired) { - QV4::Scope scope(ctx); + QV4::Scope scope(engine); QQmlData::flushPendingBinding(object, property->coreIndex); @@ -333,16 +333,16 @@ ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx Q_ASSERT(vmemo); return vmemo->vmeMethod(property->coreIndex); } else if (property->isV4Function()) { - QV4::ScopedObject qmlcontextobject(scope, ctx->d()->engine->qmlContextObject()); + QV4::ScopedObject qmlcontextobject(scope, engine->qmlContextObject()); ScopedContext global(scope, scope.engine->rootContext()); return QV4::QObjectMethod::create(global, object, property->coreIndex, qmlcontextobject); } else if (property->isSignalHandler()) { - QV4::Scoped<QV4::QmlSignalHandler> handler(scope, scope.engine->memoryManager->alloc<QV4::QmlSignalHandler>(ctx->d()->engine, object, property->coreIndex)); + QV4::Scoped<QV4::QmlSignalHandler> handler(scope, scope.engine->memoryManager->alloc<QV4::QmlSignalHandler>(engine, object, property->coreIndex)); - QV4::ScopedString connect(scope, ctx->d()->engine->newIdentifier(QStringLiteral("connect"))); - QV4::ScopedString disconnect(scope, ctx->d()->engine->newIdentifier(QStringLiteral("disconnect"))); - handler->put(connect, QV4::ScopedValue(scope, ctx->d()->engine->functionPrototype.asObject()->get(connect))); - handler->put(disconnect, QV4::ScopedValue(scope, ctx->d()->engine->functionPrototype.asObject()->get(disconnect))); + QV4::ScopedString connect(scope, engine->newIdentifier(QStringLiteral("connect"))); + QV4::ScopedString disconnect(scope, engine->newIdentifier(QStringLiteral("disconnect"))); + handler->put(connect, QV4::ScopedValue(scope, engine->functionPrototype()->get(connect))); + handler->put(disconnect, QV4::ScopedValue(scope, engine->functionPrototype()->get(disconnect))); return handler.asReturnedValue(); } else { @@ -360,31 +360,32 @@ ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx if (ep && ep->propertyCapture && property->accessors->notifier) nptr = &n; - QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(ctx->d()->engine, object, *property, nptr)); + QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(engine, object, *property, nptr)); if (captureRequired) { if (property->accessors->notifier) { - if (n) - ep->captureProperty(n); + if (n && ep->propertyCapture) + ep->propertyCapture->captureProperty(n); } else { - ep->captureProperty(object, property->coreIndex, property->notifyIndex); + if (ep->propertyCapture) + ep->propertyCapture->captureProperty(object, property->coreIndex, property->notifyIndex); } } return rv->asReturnedValue(); } - if (captureRequired && ep && !property->isConstant()) - ep->captureProperty(object, property->coreIndex, property->notifyIndex); + if (captureRequired && ep && ep->propertyCapture && !property->isConstant()) + ep->propertyCapture->captureProperty(object, property->coreIndex, property->notifyIndex); if (property->isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); return vmemo->vmeProperty(property->coreIndex); } else if (property->isDirect()) { - return LoadProperty<ReadAccessor::Direct>(ctx->d()->engine, object, *property, 0); + return LoadProperty<ReadAccessor::Direct>(engine, object, *property, 0); } else { - return LoadProperty<ReadAccessor::Indirect>(ctx->d()->engine, object, *property, 0); + return LoadProperty<ReadAccessor::Indirect>(engine, object, *property, 0); } } @@ -435,21 +436,21 @@ bool QObjectWrapper::setQmlProperty(ExecutionEngine *engine, QQmlContextData *qm Scope scope(engine); ScopedContext ctx(scope, engine->currentContext()); - setProperty(object, ctx, result, value); + setProperty(engine, object, result, value); return true; } -void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, const Value &value) +void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value) { if (!property->isWritable() && !property->isQList()) { QString error = QLatin1String("Cannot assign to read-only property \"") + property->name(object) + QLatin1Char('\"'); - ctx->engine()->throwTypeError(error); + engine->throwTypeError(error); return; } QQmlBinding *newBinding = 0; - QV4::Scope scope(ctx); + QV4::Scope scope(engine); QV4::ScopedFunctionObject f(scope, value); if (f) { if (!f->isBinding()) { @@ -460,25 +461,25 @@ void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPro error += QLatin1String("[unknown property type]"); else error += QLatin1String(QMetaType::typeName(property->propType)); - ctx->engine()->throwError(error); + scope.engine->throwError(error); return; } } else { // binding assignment. - QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->d()->engine); + QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); bindingFunction->initBindingLocation(); newBinding = new QQmlBinding(value, object, callingQmlContext); - newBinding->setTarget(object, *property, callingQmlContext); + newBinding->setTarget(object, *property); } } - QQmlAbstractBinding *oldBinding = - QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding); - if (oldBinding) - oldBinding->destroy(); + if (newBinding) + QQmlPropertyPrivate::setBinding(newBinding); + else + QQmlPropertyPrivate::removeBinding(object, property->encodedIndex()); if (!newBinding && property->isVarProperty()) { // allow assignment of "special" values (null, undefined, function) to var properties @@ -505,16 +506,16 @@ void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPro } else if (value.isUndefined() && property->propType == QMetaType::QJsonValue) { PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); } else if (!newBinding && property->propType == qMetaTypeId<QJSValue>()) { - PROPERTY_STORE(QJSValue, QJSValue(ctx->d()->engine, value.asReturnedValue())); + PROPERTY_STORE(QJSValue, QJSValue(scope.engine, value.asReturnedValue())); } else if (value.isUndefined() && property->propType != qMetaTypeId<QQmlScriptString>()) { QString error = QLatin1String("Cannot assign [undefined] to "); if (!QMetaType::typeName(property->propType)) error += QLatin1String("[unknown property type]"); else error += QLatin1String(QMetaType::typeName(property->propType)); - ctx->engine()->throwError(error); + scope.engine->throwError(error); return; - } else if (value.asFunctionObject()) { + } else if (value.as<FunctionObject>()) { // this is handled by the binding creation above } else if (property->propType == QMetaType::Int && value.isNumber()) { PROPERTY_STORE(int, value.asDouble()); @@ -543,11 +544,11 @@ void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPro } else { QVariant v; if (property->isQList()) - v = ctx->d()->engine->toVariant(value, qMetaTypeId<QList<QObject *> >()); + v = scope.engine->toVariant(value, qMetaTypeId<QList<QObject *> >()); else - v = ctx->d()->engine->toVariant(value, property->propType); + v = scope.engine->toVariant(value, property->propType); - QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->d()->engine); + QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) { const char *valueType = 0; if (v.userType() == QVariant::Invalid) valueType = "null"; @@ -561,7 +562,7 @@ void QObjectWrapper::setProperty(QObject *object, ExecutionContext *ctx, QQmlPro QLatin1String(valueType) + QLatin1String(" to ") + QLatin1String(targetTypeName); - ctx->engine()->throwError(error); + scope.engine->throwError(error); return; } } @@ -634,7 +635,7 @@ void QObjectWrapper::markWrapper(QObject *object, ExecutionEngine *engine) engine->m_multiplyWrappedQObjects->mark(object, engine); } -ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx, int propertyIndex, bool captureRequired) +ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired) { if (QQmlData::wasDeleted(object)) return QV4::Encode::null(); @@ -646,14 +647,19 @@ ReturnedValue QObjectWrapper::getProperty(QObject *object, ExecutionContext *ctx Q_ASSERT(cache); QQmlPropertyData *property = cache->property(propertyIndex); Q_ASSERT(property); // We resolved this property earlier, so it better exist! - return getProperty(object, ctx, property, captureRequired); + return getProperty(engine, object, property, captureRequired); +} + +void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value) +{ + setProperty(engine, d()->object, propertyIndex, value); } -void QObjectWrapper::setProperty(ExecutionContext *ctx, int propertyIndex, const Value &value) +void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value) { - if (QQmlData::wasDeleted(d()->object)) + if (QQmlData::wasDeleted(object)) return; - QQmlData *ddata = QQmlData::get(d()->object, /*create*/false); + QQmlData *ddata = QQmlData::get(object, /*create*/false); if (!ddata) return; @@ -661,14 +667,14 @@ void QObjectWrapper::setProperty(ExecutionContext *ctx, int propertyIndex, const Q_ASSERT(cache); QQmlPropertyData *property = cache->property(propertyIndex); Q_ASSERT(property); // We resolved this property earlier, so it better exist! - return setProperty(d()->object, ctx, property, value); + return setProperty(engine, object, property, value); } bool QObjectWrapper::isEqualTo(Managed *a, Managed *b) { Q_ASSERT(a->as<QV4::QObjectWrapper>()); QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a); - QV4::Object *o = b->asObject(); + QV4::Object *o = b->as<Object>(); if (o) { if (QV4::QmlTypeWrapper *qmlTypeWrapper = o->as<QV4::QmlTypeWrapper>()) return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object(); @@ -684,10 +690,10 @@ ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object) return (engine->memoryManager->alloc<QV4::QObjectWrapper>(engine, object))->asReturnedValue(); } -QV4::ReturnedValue QObjectWrapper::get(Managed *m, String *name, bool *hasProperty) +QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *hasProperty) { - QObjectWrapper *that = static_cast<QObjectWrapper*>(m); - QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(that->engine()); + const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); + QQmlContextData *qmlContext = that->engine()->callingQmlContext(); return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true); } @@ -699,7 +705,7 @@ void QObjectWrapper::put(Managed *m, String *name, const Value &value) if (v4->hasException || QQmlData::wasDeleted(that->d()->object)) return; - QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); + QQmlContextData *qmlContext = v4->callingQmlContext(); if (!setQmlProperty(v4, qmlContext, that->d()->object, name, QV4::QObjectWrapper::IgnoreRevision, value)) { QQmlData *ddata = QQmlData::get(that->d()->object); // Types created by QML are not extensible at run-time, but for other QObjects we can store them @@ -718,23 +724,23 @@ PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) { const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); ExecutionEngine *engine = that->engine(); - QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(engine); + QQmlContextData *qmlContext = engine->callingQmlContext(); QQmlPropertyData local; if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local) - || name->equals(engine->id_destroy) || name->equals(engine->id_toString)) + || name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) return QV4::Attr_Data; else return QV4::Object::query(m, name); } -void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes) +void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()"); static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()"); - *name = (Heap::String *)0; + name->setM(0); *index = UINT_MAX; QObjectWrapper *that = static_cast<QObjectWrapper*>(m); @@ -745,10 +751,9 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Heap::Strin const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject; const int propertyCount = mo->propertyCount(); if (it->arrayIndex < static_cast<uint>(propertyCount)) { - // #### GC Scope scope(that->engine()); ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name()))); - *name = propName->d(); + name->setM(propName->d()); ++it->arrayIndex; *attributes = QV4::Attr_Data; p->value = that->get(propName); @@ -761,10 +766,9 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Heap::Strin ++it->arrayIndex; if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2))) continue; - // #### GC Scope scope(that->engine()); ScopedString methodName(scope, that->engine()->newString(QString::fromUtf8(method.name()))); - *name = methodName->d(); + name->setM(methodName->d()); *attributes = QV4::Attr_Data; p->value = that->get(methodName); return; @@ -811,7 +815,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase QV4::ScopedFunctionObject f(scope, This->function.value()); QV4::ScopedCallData callData(scope, argCount); - callData->thisObject = This->thisObject.isUndefined() ? v4->globalObject()->asReturnedValue() : This->thisObject.value(); + callData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value(); for (int ii = 0; ii < argCount; ++ii) { int type = argsTypes[ii + 1]; if (type == qMetaTypeId<QVariant>()) { @@ -826,7 +830,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase QQmlError error = v4->catchExceptionAsQmlError(); if (error.description().isEmpty()) { QV4::ScopedString name(scope, f->name()); - error.setDescription(QString::fromLatin1("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString())); + error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString())); } if (QQmlEngine *qmlEngine = v4->qmlEngine()) { QQmlEnginePrivate::get(qmlEngine)->warning(error); @@ -1028,48 +1032,31 @@ void QObjectWrapper::markObjects(Heap::Base *that, QV4::ExecutionEngine *e) QV4::Object::markObjects(that, e); } -namespace { - struct QObjectDeleter : public QV4::GCDeletable - { - QObjectDeleter(QObject *o) - : m_objectToDelete(o) - {} - ~QObjectDeleter() - { - QQmlData *ddata = QQmlData::get(m_objectToDelete, false); - if (ddata && ddata->ownContext && ddata->context) - ddata->context->emitDestruction(); - // This object is notionally destroyed now - ddata->isQueuedForDeletion = true; - if (lastCall) - delete m_objectToDelete; - else - m_objectToDelete->deleteLater(); - } - - QObject *m_objectToDelete; - }; -} - -void QObjectWrapper::destroy(Heap::Base *that) +void QObjectWrapper::destroyObject(bool lastCall) { - Heap::QObjectWrapper *This = static_cast<Heap::QObjectWrapper*>(that); - QPointer<QObject> object = This->object; - ExecutionEngine *engine = This->internalClass->engine; - This->~Data(); - This = 0; - if (!object) - return; - - QQmlData *ddata = QQmlData::get(object, false); - if (!ddata) - return; - - if (object->parent() || ddata->indestructible) - return; + Heap::QObjectWrapper *h = d(); + if (!h->internalClass) + return; // destroyObject already got called + + QPointer<QObject> object = h->object; + if (object) { + QQmlData *ddata = QQmlData::get(object, false); + if (ddata) { + if (!object->parent() && !ddata->indestructible) { + if (ddata && ddata->ownContext && ddata->context) + ddata->context->emitDestruction(); + // This object is notionally destroyed now + ddata->isQueuedForDeletion = true; + if (lastCall) + delete object; + else + object->deleteLater(); + } + } + } - QObjectDeleter *deleter = new QObjectDeleter(object); - engine->memoryManager->registerDeletable(deleter); + h->internalClass = 0; + h->~Data(); } @@ -1229,7 +1216,7 @@ static int MatchScore(const QV4::Value &actual, int conversionType) default: return 10; } - } else if (actual.asDateObject()) { + } else if (actual.as<DateObject>()) { switch (conversionType) { case QMetaType::QDateTime: return 0; @@ -1247,7 +1234,7 @@ static int MatchScore(const QV4::Value &actual, int conversionType) default: return 10; } - } else if (actual.asArrayObject()) { + } else if (actual.as<ArrayObject>()) { switch (conversionType) { case QMetaType::QJsonArray: return 3; @@ -1276,7 +1263,7 @@ static int MatchScore(const QV4::Value &actual, int conversionType) return 10; } } - } else if (QV4::Object *obj = actual.asObject()) { + } else if (const Object *obj = actual.as<Object>()) { if (obj->as<QV4::VariantObject>()) { if (conversionType == qMetaTypeId<QVariant>()) return 0; @@ -1379,7 +1366,7 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ if (returnType == QMetaType::UnknownType) { QString typeName = QString::fromLatin1(unknownTypeError); - QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName); + QString error = QStringLiteral("Unknown method return type: %1").arg(typeName); return engine->throwError(error); } @@ -1392,7 +1379,7 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ if (!args) { QString typeName = QString::fromLatin1(unknownTypeError); - QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName); + QString error = QStringLiteral("Unknown method parameter type: %1").arg(typeName); return engine->throwError(error); } @@ -1606,9 +1593,9 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q type = callType; } else if (callType == QMetaType::QObjectStar) { qobjectPtr = 0; - if (QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) + if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) qobjectPtr = qobjectWrapper->object(); - else if (QV4::QmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QmlTypeWrapper>()) + else if (const QV4::QmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QmlTypeWrapper>()) queryEngine = qmlTypeWrapper->isSingleton(); type = callType; } else if (callType == qMetaTypeId<QVariant>()) { @@ -1630,7 +1617,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q } } else { QObject *o = 0; - if (QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) + if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) o = qobjectWrapper->object(); qlistPtr->append(o); } @@ -1754,14 +1741,14 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in return method.asReturnedValue(); } -ReturnedValue QObjectMethod::create(ExecutionContext *scope, QQmlValueTypeWrapper *valueType, int index, const Value &qmlGlobal) +ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index, const Value &qmlGlobal) { Scope valueScope(scope); Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->alloc<QObjectMethod>(scope)); method->d()->propertyCache = valueType->d()->propertyCache; method->d()->index = index; method->d()->qmlGlobal = qmlGlobal; - method->d()->valueTypeWrapper = valueType; + method->d()->valueTypeWrapper = *valueType; return method.asReturnedValue(); } @@ -1777,7 +1764,7 @@ const QMetaObject *Heap::QObjectMethod::metaObject() return object->metaObject(); } -QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) +QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) const { QString result; if (const QMetaObject *metaObject = d()->metaObject()) { @@ -1803,7 +1790,7 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) return ctx->d()->engine->newString(result)->asReturnedValue(); } -QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) +QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const { if (!d()->object) return Encode::undefined(); @@ -1822,13 +1809,13 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, con return Encode::undefined(); } -ReturnedValue QObjectMethod::call(Managed *m, CallData *callData) +ReturnedValue QObjectMethod::call(const Managed *m, CallData *callData) { - QObjectMethod *This = static_cast<QObjectMethod*>(m); + const QObjectMethod *This = static_cast<const QObjectMethod*>(m); return This->callInternal(callData); } -ReturnedValue QObjectMethod::callInternal(CallData *callData) +ReturnedValue QObjectMethod::callInternal(CallData *callData) const { Scope scope(engine()); ScopedContext context(scope, scope.engine->currentContext()); diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 24e8b29e08..2d10006d97 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -54,7 +54,7 @@ #include <private/qqmlpropertycache_p.h> #include <private/qintrusivelist_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> QT_BEGIN_NAMESPACE @@ -104,7 +104,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object QObject *object() const { return d()->object.data(); } - ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = 0, bool includeImports = false); + ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = 0, bool includeImports = false) const; static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = 0); static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); @@ -114,26 +114,28 @@ struct Q_QML_EXPORT QObjectWrapper : public Object using Object::get; - static ReturnedValue getProperty(QObject *object, ExecutionContext *ctx, int propertyIndex, bool captureRequired); - void setProperty(ExecutionContext *ctx, int propertyIndex, const Value &value); + static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired); + static void setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value); + void setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value); + + void destroyObject(bool lastCall); protected: static bool isEqualTo(Managed *that, Managed *o); private: - static ReturnedValue getProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, bool captureRequired = true); - static void setProperty(QObject *object, ExecutionContext *ctx, QQmlPropertyData *property, const Value &value); + static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true); + static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value); static ReturnedValue create(ExecutionEngine *engine, QObject *object); QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void put(Managed *m, String *name, const Value &value); static PropertyAttributes query(const Managed *, String *name); - static void advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes); + static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); - static void destroy(Heap::Base *that); static ReturnedValue method_connect(CallContext *ctx); static ReturnedValue method_disconnect(CallContext *ctx); @@ -149,17 +151,17 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject enum { DestroyMethod = -1, ToStringMethod = -2 }; static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal = Primitive::undefinedValue()); - static ReturnedValue create(QV4::ExecutionContext *scope, QQmlValueTypeWrapper *valueType, int index, const Value &qmlGlobal = Primitive::undefinedValue()); + static ReturnedValue create(QV4::ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index, const Value &qmlGlobal = Primitive::undefinedValue()); int methodIndex() const { return d()->index; } QObject *object() const { return d()->object.data(); } - QV4::ReturnedValue method_toString(QV4::ExecutionContext *ctx); - QV4::ReturnedValue method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc); + QV4::ReturnedValue method_toString(QV4::ExecutionContext *ctx) const; + QV4::ReturnedValue method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const; - static ReturnedValue call(Managed *, CallData *callData); + static ReturnedValue call(const Managed *, CallData *callData); - ReturnedValue callInternal(CallData *callData); + ReturnedValue callInternal(CallData *callData) const; static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); }; diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 8e18a5fbdd..31fee534ad 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -34,16 +34,16 @@ #include "qv4regexp_p.h" #include "qv4engine_p.h" #include "qv4scopedvalue_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> using namespace QV4; RegExpCache::~RegExpCache() { - for (RegExpCache::Iterator it = begin(), e = end(); - it != e; ++it) - it.value()->cache = 0; - clear(); + for (RegExpCache::Iterator it = begin(), e = end(); it != e; ++it) { + if (RegExp *re = it.value().as<RegExp>()) + re->d()->cache = 0; + } } DEFINE_MANAGED_VTABLE(RegExp); @@ -68,19 +68,18 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo RegExpCacheKey key(pattern, ignoreCase, multiline); RegExpCache *cache = engine->regExpCache; - if (cache) { - if (Heap::RegExp *result = cache->value(key)) - return result; - } + if (!cache) + cache = engine->regExpCache = new RegExpCache; + + QV4::WeakValue &cachedValue = (*cache)[key]; + if (QV4::RegExp *result = cachedValue.as<RegExp>()) + return result->d(); Scope scope(engine); Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, ignoreCase, multiline)); - if (!cache) - cache = engine->regExpCache = new RegExpCache; - result->d()->cache = cache; - cache->insert(key, result->d()); + cachedValue.set(engine, result); return result->d(); } diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index 819e31e5f1..af6e346ea8 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -133,8 +133,7 @@ inline RegExpCacheKey::RegExpCacheKey(const RegExp::Data *re) inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW { return qHash(key.pattern, seed); } -// ### GC -class RegExpCache : public QHash<RegExpCacheKey, Heap::RegExp*> +class RegExpCache : public QHash<RegExpCacheKey, WeakValue> { public: ~RegExpCache(); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index f6e88e62b7..329e5d2c56 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -37,7 +37,7 @@ #include "qv4objectproto_p.h" #include "qv4regexp_p.h" #include "qv4stringobject_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include <private/qqmljsengine_p.h> @@ -75,7 +75,7 @@ Heap::RegExpObject::RegExpObject(InternalClass *ic, QV4::Object *prototype) } Heap::RegExpObject::RegExpObject(QV4::ExecutionEngine *engine, QV4::RegExp *value, bool global) - : Heap::Object(engine->emptyClass, engine->regExpPrototype.asObject()) + : Heap::Object(engine->emptyClass, engine->regExpPrototype()) , value(value->d()) , global(global) { @@ -88,7 +88,7 @@ Heap::RegExpObject::RegExpObject(QV4::ExecutionEngine *engine, QV4::RegExp *valu // The conversion is not 100% exact since ECMA regexp and QRegExp // have different semantics/flags, but we try to do our best. Heap::RegExpObject::RegExpObject(QV4::ExecutionEngine *engine, const QRegExp &re) - : Heap::Object(engine->emptyClass, engine->regExpPrototype.asObject()) + : Heap::Object(engine->emptyClass, engine->regExpPrototype()) { value = 0; global = false; @@ -174,7 +174,7 @@ void RegExpObject::markObjects(Heap::Base *that, ExecutionEngine *e) Property *RegExpObject::lastIndexProperty() { - Q_ASSERT(0 == internalClass()->find(engine()->id_lastIndex)); + Q_ASSERT(0 == internalClass()->find(engine()->id_lastIndex())); return propertyAt(0); } @@ -231,14 +231,14 @@ Heap::RegExpCtor::RegExpCtor(QV4::ExecutionContext *scope) void Heap::RegExpCtor::clearLastMatch() { lastMatch = Primitive::nullValue(); - lastInput = internalClass->engine->id_empty; + lastInput = internalClass->engine->id_empty()->d(); lastMatchStart = 0; lastMatchEnd = 0; } -ReturnedValue RegExpCtor::construct(Managed *m, CallData *callData) +ReturnedValue RegExpCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<Object *>(m)->engine()); + Scope scope(static_cast<const Object *>(m)->engine()); ScopedContext ctx(scope, scope.engine->currentContext()); ScopedValue r(scope, callData->argument(0)); @@ -286,7 +286,7 @@ ReturnedValue RegExpCtor::construct(Managed *m, CallData *callData) return Encode(ctx->d()->engine->newRegExpObject(regexp, global)); } -ReturnedValue RegExpCtor::call(Managed *that, CallData *callData) +ReturnedValue RegExpCtor::call(const Managed *that, CallData *callData) { if (callData->argc > 0 && callData->args[0].as<RegExpObject>()) { if (callData->argc == 1 || callData->args[1].isUndefined()) @@ -300,7 +300,7 @@ void RegExpCtor::markObjects(Heap::Base *that, ExecutionEngine *e) { RegExpCtor::Data *This = static_cast<RegExpCtor::Data *>(that); This->lastMatch.mark(e); - This->lastInput.mark(e); + This->lastInput->mark(e); FunctionObject::markObjects(that, e); } @@ -310,8 +310,8 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) ScopedObject o(scope); ScopedObject ctor(scope, constructor); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(2)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(2)); // Properties deprecated in the spec but required by "the web" :( ctor->defineAccessorProperty(QStringLiteral("lastMatch"), method_get_lastMatch_n<0>, 0); @@ -337,7 +337,7 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(QStringLiteral("exec"), method_exec, 1); defineDefaultProperty(QStringLiteral("test"), method_test, 1); - defineDefaultProperty(engine->id_toString, method_toString, 0); + defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("compile"), method_compile, 2); } @@ -363,7 +363,7 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) uint* matchOffsets = (uint*)alloca(r->value()->captureCount() * 2 * sizeof(uint)); const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets); - Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor); + Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor()); regExpCtor->d()->clearLastMatch(); if (result == -1) { @@ -372,7 +372,7 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) } // fill in result data - ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->regExpExecArrayClass, scope.engine->arrayPrototype.asObject())); + ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->regExpExecArrayClass, scope.engine->arrayPrototype())); int len = r->value()->captureCount(); array->arrayReserve(len); ScopedValue v(scope); @@ -388,7 +388,7 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx) RegExpCtor::Data *dd = regExpCtor->d(); dd->lastMatch = array; - dd->lastInput = arg->stringValue(); + dd->lastInput = arg->stringValue()->d(); dd->lastMatchStart = matchOffsets[0]; dd->lastMatchEnd = matchOffsets[1]; @@ -425,7 +425,7 @@ ReturnedValue RegExpPrototype::method_compile(CallContext *ctx) ScopedCallData callData(scope, ctx->argc()); memcpy(callData->args, ctx->args(), ctx->argc()*sizeof(Value)); - Scoped<RegExpObject> re(scope, ctx->d()->engine->regExpCtor.asFunctionObject()->construct(callData)); + Scoped<RegExpObject> re(scope, ctx->d()->engine->regExpCtor()->as<FunctionObject>()->construct(callData)); r->d()->value = re->value(); r->d()->global = re->global(); @@ -436,7 +436,7 @@ template <int index> ReturnedValue RegExpPrototype::method_get_lastMatch_n(CallContext *ctx) { Scope scope(ctx); - ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor.objectValue())->lastMatch()); + ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor())->lastMatch()); ScopedValue result(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined()); if (result->isUndefined()) return ctx->d()->engine->newString()->asReturnedValue(); @@ -446,7 +446,7 @@ ReturnedValue RegExpPrototype::method_get_lastMatch_n(CallContext *ctx) ReturnedValue RegExpPrototype::method_get_lastParen(CallContext *ctx) { Scope scope(ctx); - ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor.objectValue())->lastMatch()); + ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor())->lastMatch()); ScopedValue result(scope, lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined()); if (result->isUndefined()) return ctx->d()->engine->newString()->asReturnedValue(); @@ -455,13 +455,13 @@ ReturnedValue RegExpPrototype::method_get_lastParen(CallContext *ctx) ReturnedValue RegExpPrototype::method_get_input(CallContext *ctx) { - return static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor.objectValue())->lastInput().asReturnedValue(); + return static_cast<RegExpCtor*>(ctx->d()->engine->regExpCtor())->lastInput()->asReturnedValue(); } ReturnedValue RegExpPrototype::method_get_leftContext(CallContext *ctx) { Scope scope(ctx); - Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor); + Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor()); QString lastInput = regExpCtor->lastInput()->toQString(); return ctx->d()->engine->newString(lastInput.left(regExpCtor->lastMatchStart()))->asReturnedValue(); } @@ -469,7 +469,7 @@ ReturnedValue RegExpPrototype::method_get_leftContext(CallContext *ctx) ReturnedValue RegExpPrototype::method_get_rightContext(CallContext *ctx) { Scope scope(ctx); - Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor); + Scoped<RegExpCtor> regExpCtor(scope, ctx->d()->engine->regExpCtor()); QString lastInput = regExpCtor->lastInput()->toQString(); return ctx->d()->engine->newString(lastInput.mid(regExpCtor->lastMatchEnd()))->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index f5f255faf5..29d20614de 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -61,14 +61,14 @@ struct RegExpObject : Object { RegExpObject(QV4::ExecutionEngine *engine, QV4::RegExp *value, bool global); RegExpObject(QV4::ExecutionEngine *engine, const QRegExp &re); - RegExp *value; + Pointer<RegExp> value; bool global; }; struct RegExpCtor : FunctionObject { RegExpCtor(QV4::ExecutionContext *scope); Value lastMatch; - StringValue lastInput; + Pointer<String> lastInput; int lastMatchStart; int lastMatchEnd; void clearLastMatch(); @@ -117,12 +117,12 @@ struct RegExpCtor: FunctionObject V4_OBJECT2(RegExpCtor, FunctionObject) Value lastMatch() { return d()->lastMatch; } - StringValue lastInput() { return d()->lastInput; } + Heap::String *lastInput() { return d()->lastInput; } int lastMatchStart() { return d()->lastMatchStart; } int lastMatchEnd() { return d()->lastMatchEnd; } - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; @@ -146,7 +146,7 @@ struct RegExpPrototype: RegExpObject }; inline Heap::RegExpPrototype::RegExpPrototype(ExecutionEngine *e) - : RegExpObject(e->emptyClass, e->objectPrototype.asObject()) + : RegExpObject(e->emptyClass, e->objectPrototype()) { } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index b66e917b87..b8ea6d7a87 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -40,12 +40,15 @@ #include "qv4stringobject_p.h" #include "qv4argumentsobject_p.h" #include "qv4objectiterator_p.h" +#include "qv4dateobject_p.h" #include "qv4lookup_p.h" #include "qv4function_p.h" #include "private/qlocale_tools_p.h" #include "qv4scopedvalue_p.h" #include <private/qqmlcontextwrapper_p.h> #include <private/qqmltypewrapper_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmljavascriptexpression_p.h> #include "qv4qobjectwrapper_p.h" #include <private/qv8engine_p.h> #endif @@ -313,14 +316,14 @@ ReturnedValue Runtime::deleteName(ExecutionEngine *engine, int nameIndex) QV4::ReturnedValue Runtime::instanceof(ExecutionEngine *engine, const Value &left, const Value &right) { Scope scope(engine); - ScopedFunctionObject f(scope, right.asFunctionObject()); + ScopedFunctionObject f(scope, right.as<FunctionObject>()); if (!f) return engine->throwTypeError(); if (f->isBoundFunction()) f = static_cast<BoundFunction *>(f.getPointer())->target(); - ScopedObject v(scope, left.asObject()); + ScopedObject v(scope, left.as<Object>()); if (!v) return Encode(false); @@ -380,10 +383,10 @@ Heap::String *RuntimeHelpers::stringFromNumber(ExecutionEngine *engine, double n return engine->newString(qstr); } -ReturnedValue RuntimeHelpers::objectDefaultValue(Object *object, int typeHint) +ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeHint) { if (typeHint == PREFERREDTYPE_HINT) { - if (object->asDateObject()) + if (object->as<DateObject>()) typeHint = STRING_HINT; else typeHint = NUMBER_HINT; @@ -393,18 +396,18 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(Object *object, int typeHint) if (engine->hasException) return Encode::undefined(); - StringValue *meth1 = &engine->id_toString; - StringValue *meth2 = &engine->id_valueOf; + String *meth1 = engine->id_toString(); + String *meth2 = engine->id_valueOf(); if (typeHint == NUMBER_HINT) qSwap(meth1, meth2); Scope scope(engine); ScopedCallData callData(scope, 0); - callData->thisObject = object; + callData->thisObject = *object; - ScopedValue conv(scope, object->get(*meth1)); - if (FunctionObject *o = conv->asFunctionObject()) { + ScopedValue conv(scope, object->get(meth1)); + if (FunctionObject *o = conv->as<FunctionObject>()) { ScopedValue r(scope, o->call(callData)); if (r->isPrimitive()) return r->asReturnedValue(); @@ -413,8 +416,8 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(Object *object, int typeHint) if (engine->hasException) return Encode::undefined(); - conv = object->get(*meth2); - if (FunctionObject *o = conv->asFunctionObject()) { + conv = object->get(meth2); + if (FunctionObject *o = conv->as<FunctionObject>()) { ScopedValue r(scope, o->call(callData)); if (r->isPrimitive()) return r->asReturnedValue(); @@ -437,7 +440,7 @@ Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Val return engine->newBooleanObject(value.booleanValue()); case Value::Managed_Type: Q_ASSERT(value.isString()); - return engine->newStringObject(value); + return engine->newStringObject(value.stringValue()); case Value::Integer_Type: default: // double return engine->newNumberObject(value.asDouble()); @@ -450,14 +453,14 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); case Value::Undefined_Type: - return engine->id_undefined->d(); + return engine->id_undefined()->d(); case Value::Null_Type: - return engine->id_null->d(); + return engine->id_null()->d(); case Value::Boolean_Type: if (value.booleanValue()) - return engine->id_true->d(); + return engine->id_true()->d(); else - return engine->id_false->d(); + return engine->id_false()->d(); case Value::Managed_Type: if (value.isString()) return value.stringValue()->d(); @@ -467,7 +470,7 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val return RuntimeHelpers::convertToString(engine, prim); } case Value::Integer_Type: - return RuntimeHelpers::stringFromNumber(engine, value.int_32); + return RuntimeHelpers::stringFromNumber(engine, value.int_32()); default: // double return RuntimeHelpers::stringFromNumber(engine, value.doubleValue()); } // switch @@ -481,14 +484,14 @@ static Heap::String *convert_to_string_add(ExecutionEngine *engine, const Value case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); case Value::Undefined_Type: - return engine->id_undefined->d(); + return engine->id_undefined()->d(); case Value::Null_Type: - return engine->id_null->d(); + return engine->id_null()->d(); case Value::Boolean_Type: if (value.booleanValue()) - return engine->id_true->d(); + return engine->id_true()->d(); else - return engine->id_false->d(); + return engine->id_false()->d(); case Value::Managed_Type: if (value.isString()) return value.stringValue()->d(); @@ -498,7 +501,7 @@ static Heap::String *convert_to_string_add(ExecutionEngine *engine, const Value return RuntimeHelpers::convertToString(engine, prim); } case Value::Integer_Type: - return RuntimeHelpers::stringFromNumber(engine, value.int_32); + return RuntimeHelpers::stringFromNumber(engine, value.int_32()); default: // double return RuntimeHelpers::stringFromNumber(engine, value.doubleValue()); } // switch @@ -578,7 +581,7 @@ ReturnedValue Runtime::getElement(ExecutionEngine *engine, const Value &object, ScopedObject o(scope, object); if (!o) { if (idx < UINT_MAX) { - if (String *str = object.asString()) { + if (const String *str = object.as<String>()) { if (idx >= (uint)str->toQString().length()) { return Encode::undefined(); } @@ -912,7 +915,7 @@ ReturnedValue Runtime::callGlobalLookup(ExecutionEngine *engine, uint index, Cal return engine->throwTypeError(); ScopedString name(scope, engine->currentContext()->compilationUnit->runtimeStrings[l->nameIndex]); - if (o->d() == scope.engine->evalFunction && name->equals(scope.engine->id_eval)) + if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) return static_cast<EvalFunction *>(o.getPointer())->evalCall(callData, true); return o->call(callData); @@ -934,7 +937,7 @@ ReturnedValue Runtime::callActivationProperty(ExecutionEngine *engine, int nameI if (base) callData->thisObject = base; - FunctionObject *o = func->asFunctionObject(); + FunctionObject *o = func->as<FunctionObject>(); if (!o) { QString objectAsString = QStringLiteral("[null]"); if (base) @@ -943,13 +946,37 @@ ReturnedValue Runtime::callActivationProperty(ExecutionEngine *engine, int nameI return engine->throwTypeError(msg); } - if (o->d() == scope.engine->evalFunction && name->equals(scope.engine->id_eval)) { + if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) { return static_cast<EvalFunction *>(o)->evalCall(callData, true); } return o->call(callData); } +ReturnedValue Runtime::callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) +{ + Scope scope(engine); + ScopedFunctionObject o(scope, getQmlScopeObjectProperty(engine, callData->thisObject, propertyIndex)); + if (!o) { + QString error = QStringLiteral("Property '%1' of object %2 is not a function").arg(propertyIndex).arg(callData->thisObject.toQStringNoThrow()); + return engine->throwTypeError(error); + } + + return o->call(callData); +} + +ReturnedValue Runtime::callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) +{ + Scope scope(engine); + ScopedFunctionObject o(scope, getQmlContextObjectProperty(engine, callData->thisObject, propertyIndex)); + if (!o) { + QString error = QStringLiteral("Property '%1' of object %2 is not a function").arg(propertyIndex).arg(callData->thisObject.toQStringNoThrow()); + return engine->throwTypeError(error); + } + + return o->call(callData); +} + ReturnedValue Runtime::callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) { Scope scope(engine); @@ -1037,7 +1064,7 @@ ReturnedValue Runtime::constructActivationProperty(ExecutionEngine *engine, int if (scope.engine->hasException) return Encode::undefined(); - Object *f = func->asObject(); + Object *f = func->as<Object>(); if (!f) return engine->throwTypeError(); @@ -1046,7 +1073,7 @@ ReturnedValue Runtime::constructActivationProperty(ExecutionEngine *engine, int ReturnedValue Runtime::constructValue(ExecutionEngine *engine, const Value &func, CallData *callData) { - Object *f = func.asObject(); + const Object *f = func.as<Object>(); if (!f) return engine->throwTypeError(); @@ -1092,24 +1119,24 @@ ReturnedValue Runtime::typeofValue(ExecutionEngine *engine, const Value &value) ScopedString res(scope); switch (value.type()) { case Value::Undefined_Type: - res = engine->id_undefined; + res = engine->id_undefined(); break; case Value::Null_Type: - res = engine->id_object; + res = engine->id_object(); break; case Value::Boolean_Type: - res = engine->id_boolean; + res = engine->id_boolean(); break; case Value::Managed_Type: if (value.isString()) - res = engine->id_string; - else if (value.objectValue()->asFunctionObject()) - res = engine->id_function; + res = engine->id_string(); + else if (value.objectValue()->as<FunctionObject>()) + res = engine->id_function(); else - res = engine->id_object; // ### implementation-defined + res = engine->id_object(); // ### implementation-defined break; default: - res = engine->id_number; + res = engine->id_number(); break; } return res.asReturnedValue(); @@ -1202,7 +1229,7 @@ ReturnedValue Runtime::objectLiteral(ExecutionEngine *engine, const QV4::Value * { Scope scope(engine); QV4::InternalClass *klass = engine->currentContext()->compilationUnit->runtimeClasses[classId]; - ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype.asObject())); + ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype())); { bool needSparseArray = arrayGetterSetterCountAndFlags >> 30; @@ -1242,7 +1269,7 @@ ReturnedValue Runtime::objectLiteral(ExecutionEngine *engine, const QV4::Value * QV4::ReturnedValue Runtime::setupArgumentsObject(ExecutionEngine *engine) { - Q_ASSERT(engine->currentContext()->type >= Heap::ExecutionContext::Type_CallContext); + Q_ASSERT(engine->currentContext()->type == Heap::ExecutionContext::Type_CallContext); Scope scope(engine); Scoped<CallContext> c(scope, static_cast<Heap::CallContext *>(engine->currentContext())); return (engine->memoryManager->alloc<ArgumentsObject>(c))->asReturnedValue(); @@ -1327,34 +1354,16 @@ unsigned Runtime::doubleToUInt(const double &d) return Primitive::toUInt32(d); } -#ifndef V4_BOOTSTRAP - -ReturnedValue Runtime::regexpLiteral(ExecutionEngine *engine, int id) +ReturnedValue Runtime::getQmlContext(NoThrowEngine *engine) { - return engine->currentContext()->compilationUnit->runtimeRegularExpressions[id].asReturnedValue(); + return engine->qmlContext()->asReturnedValue(); } -ReturnedValue Runtime::getQmlIdArray(NoThrowEngine *engine) -{ - Q_ASSERT(engine->qmlContextObject()); - Scope scope(engine); - Scoped<QmlContextWrapper> wrapper(scope, engine->qmlContextObject()); - return wrapper->idObjectsArray(); -} - -ReturnedValue Runtime::getQmlContextObject(NoThrowEngine *engine) -{ - QQmlContextData *context = QmlContextWrapper::callingContext(engine); - if (!context) - return Encode::undefined(); - return QObjectWrapper::wrap(engine, context->contextObject); -} +#ifndef V4_BOOTSTRAP -ReturnedValue Runtime::getQmlScopeObject(NoThrowEngine *engine) +ReturnedValue Runtime::regexpLiteral(ExecutionEngine *engine, int id) { - Scope scope(engine); - QV4::Scoped<QmlContextWrapper> c(scope, engine->qmlContextObject()); - return QObjectWrapper::wrap(engine, c->getScopeObject()); + return engine->currentContext()->compilationUnit->runtimeRegularExpressions[id].asReturnedValue(); } ReturnedValue Runtime::getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) @@ -1365,21 +1374,29 @@ ReturnedValue Runtime::getQmlQObjectProperty(ExecutionEngine *engine, const Valu engine->throwTypeError(QStringLiteral("Cannot read property of null")); return Encode::undefined(); } - ScopedContext ctx(scope, engine->currentContext()); - return QV4::QObjectWrapper::getProperty(wrapper->object(), ctx, propertyIndex, captureRequired); + return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->object(), propertyIndex, captureRequired); } QV4::ReturnedValue Runtime::getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex) { - Scope scope(engine); - QV4::Scoped<QmlContextWrapper> c(scope, engine->qmlContextObject()); - QObject *scopeObject = c->getScopeObject(); + QObject *scopeObject = engine->qmlScopeObject(); QObject *attachedObject = qmlAttachedPropertiesObjectById(attachedPropertiesId, scopeObject); QJSEngine *jsEngine = engine->jsEngine(); QQmlData::ensurePropertyCache(jsEngine, attachedObject); - ScopedContext ctx(scope, engine->currentContext()); - return QV4::QObjectWrapper::getProperty(attachedObject, ctx, propertyIndex, /*captureRequired*/true); + return QV4::QObjectWrapper::getProperty(engine, attachedObject, propertyIndex, /*captureRequired*/true); +} + +ReturnedValue Runtime::getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) +{ + const QmlContext &c = static_cast<const QmlContext &>(context); + return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->scopeObject, propertyIndex, false); +} + +ReturnedValue Runtime::getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) +{ + const QmlContext &c = static_cast<const QmlContext &>(context); + return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->context->contextObject, propertyIndex, false); } ReturnedValue Runtime::getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) @@ -1390,8 +1407,34 @@ ReturnedValue Runtime::getQmlSingletonQObjectProperty(ExecutionEngine *engine, c scope.engine->throwTypeError(QStringLiteral("Cannot read property of null")); return Encode::undefined(); } - ScopedContext ctx(scope, engine->currentContext()); - return QV4::QObjectWrapper::getProperty(wrapper->singletonObject(), ctx, propertyIndex, captureRequired); + return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->singletonObject(), propertyIndex, captureRequired); +} + +ReturnedValue Runtime::getQmlIdObject(ExecutionEngine *engine, const Value &c, uint index) +{ + Scope scope(engine); + const QmlContext &qmlContext = static_cast<const QmlContext &>(c); + QQmlContextData *context = qmlContext.d()->qml->context; + if (!context || index >= (uint)context->idValueCount) + return Encode::undefined(); + + QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0; + if (ep && ep->propertyCapture) + ep->propertyCapture->captureProperty(&context->idValues[index].bindings); + + return QObjectWrapper::wrap(engine, context->idValues[index].data()); +} + +void Runtime::setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) +{ + const QmlContext &c = static_cast<const QmlContext &>(context); + return QV4::QObjectWrapper::setProperty(engine, c.d()->qml->scopeObject, propertyIndex, value); +} + +void Runtime::setQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) +{ + const QmlContext &c = static_cast<const QmlContext &>(context); + return QV4::QObjectWrapper::setProperty(engine, c.d()->qml->context->contextObject, propertyIndex, value); } void Runtime::setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value) @@ -1402,13 +1445,12 @@ void Runtime::setQmlQObjectProperty(ExecutionEngine *engine, const Value &object engine->throwTypeError(QStringLiteral("Cannot write property of null")); return; } - ScopedContext ctx(scope, engine->currentContext()); - wrapper->setProperty(ctx, propertyIndex, value); + wrapper->setProperty(engine, propertyIndex, value); } ReturnedValue Runtime::getQmlImportedScripts(NoThrowEngine *engine) { - QQmlContextData *context = QmlContextWrapper::callingContext(engine); + QQmlContextData *context = engine->callingQmlContext(); if (!context) return Encode::undefined(); return context->importedScripts.value(); @@ -1428,7 +1470,7 @@ void Runtime::convertThisToObject(ExecutionEngine *engine) if (t->isObject()) return; if (t->isNullOrUndefined()) { - *t = engine->globalObject()->asReturnedValue(); + *t = engine->globalObject->asReturnedValue(); } else { *t = t->toObject(engine)->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index f2f90bbc15..f597e4b2e3 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -34,8 +34,9 @@ #define QMLJS_RUNTIME_H #include "qv4global_p.h" -#include "qv4value_inl_p.h" +#include "qv4value_p.h" #include "qv4context_p.h" +#include "qv4engine_p.h" #include "qv4math_p.h" #include <QtCore/qnumeric.h> @@ -90,6 +91,8 @@ struct Q_QML_PRIVATE_EXPORT Runtime { // call static ReturnedValue callGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData); static ReturnedValue callActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData); + static ReturnedValue callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData); + static ReturnedValue callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData); static ReturnedValue callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData); static ReturnedValue callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData); static ReturnedValue callElement(ExecutionEngine *engine, const Value &index, CallData *callData); @@ -206,19 +209,23 @@ struct Q_QML_PRIVATE_EXPORT Runtime { static unsigned doubleToUInt(const double &d); // qml - static ReturnedValue getQmlIdArray(NoThrowEngine *ctx); - static ReturnedValue getQmlImportedScripts(NoThrowEngine *ctx); - static ReturnedValue getQmlContextObject(NoThrowEngine *ctx); - static ReturnedValue getQmlScopeObject(NoThrowEngine *ctx); - static ReturnedValue getQmlSingleton(NoThrowEngine *ctx, int nameIndex); + static ReturnedValue getQmlContext(NoThrowEngine *engine); + static ReturnedValue getQmlImportedScripts(NoThrowEngine *engine); + static ReturnedValue getQmlSingleton(NoThrowEngine *engine, int nameIndex); static ReturnedValue getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex); + static ReturnedValue getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex); + static ReturnedValue getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex); static ReturnedValue getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired); static ReturnedValue getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired); + static ReturnedValue getQmlIdObject(ExecutionEngine *engine, const Value &context, uint index); + + static void setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value); + static void setQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value); static void setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value); }; struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { - static ReturnedValue objectDefaultValue(Object *object, int typeHint); + static ReturnedValue objectDefaultValue(const Object *object, int typeHint); static ReturnedValue toPrimitive(const Value &value, int typeHint); static double stringToNumber(const QString &s); @@ -243,7 +250,7 @@ struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { #ifndef V4_BOOTSTRAP inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, int typeHint) { - Object *o = value.asObject(); + const Object *o = value.as<Object>(); if (!o) return value.asReturnedValue(); return RuntimeHelpers::objectDefaultValue(o, typeHint); @@ -262,7 +269,7 @@ inline ReturnedValue Runtime::uPlus(const Value &value) if (value.isNumber()) return value.asReturnedValue(); if (value.integerCompatible()) - return Encode(value.int_32); + return Encode(value.int_32()); double n = value.toNumberImpl(); return Encode(n); @@ -369,6 +376,15 @@ inline ReturnedValue Runtime::div(const Value &left, const Value &right) { TRACE2(left, right); + if (Value::integerCompatible(left, right)) { + int lval = left.integerValue(); + int rval = right.integerValue(); + if (rval != 0 && (lval % rval == 0)) + return Encode(int(lval / rval)); + else + return Encode(double(lval) / rval); + } + double lval = left.toNumber(); double rval = right.toNumber(); return Primitive::fromDouble(lval / rval).asReturnedValue(); diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 908248f0f0..e19aeaf882 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -85,10 +85,7 @@ struct Scope { #ifndef QT_NO_DEBUG size += nValues; #endif - Value *ptr = engine->jsStackTop; - engine->jsStackTop = ptr + nValues; - memset(ptr, 0, nValues*sizeof(Value)); - return ptr; + return engine->jsAlloca(nValues); } bool hasException() const { @@ -110,7 +107,7 @@ struct ScopedValue ScopedValue(const Scope &scope) { ptr = scope.engine->jsStackTop++; - ptr->val = 0; + ptr->setRawValue(0); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -128,9 +125,9 @@ struct ScopedValue ScopedValue(const Scope &scope, Heap::Base *o) { ptr = scope.engine->jsStackTop++; - ptr->m = o; + ptr->setM(o); #if QT_POINTER_SIZE == 4 - ptr->tag = QV4::Value::Managed_Type; + ptr->setTag(QV4::Value::Managed_Type); #endif #ifndef QT_NO_DEBUG ++scope.size; @@ -140,7 +137,7 @@ struct ScopedValue ScopedValue(const Scope &scope, Managed *m) { ptr = scope.engine->jsStackTop++; - ptr->val = m->asReturnedValue(); + ptr->setRawValue(m->asReturnedValue()); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -149,7 +146,7 @@ struct ScopedValue ScopedValue(const Scope &scope, const ReturnedValue &v) { ptr = scope.engine->jsStackTop++; - ptr->val = v; + ptr->setRawValue(v); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -161,9 +158,9 @@ struct ScopedValue } ScopedValue &operator=(Heap::Base *o) { - ptr->m = o; + ptr->setM(o); #if QT_POINTER_SIZE == 4 - ptr->tag = QV4::Value::Managed_Type; + ptr->setTag(QV4::Value::Managed_Type); #endif return *this; } @@ -174,7 +171,7 @@ struct ScopedValue } ScopedValue &operator=(const ReturnedValue &v) { - ptr->val = v; + ptr->setRawValue(v); return *this; } @@ -202,30 +199,29 @@ struct Scoped { enum _Convert { Convert }; - inline void setPointer(Managed *p) { - ptr->m = p ? p->m : 0; + inline void setPointer(const Managed *p) { + ptr->setM(p ? p->m() : 0); #if QT_POINTER_SIZE == 4 - ptr->tag = QV4::Value::Managed_Type; + ptr->setTag(QV4::Value::Managed_Type); #endif } Scoped(const Scope &scope) { ptr = scope.engine->jsStackTop++; - ptr->m = 0; + ptr->setM(0); #if QT_POINTER_SIZE == 4 - ptr->tag = QV4::Value::Managed_Type; + ptr->setTag(QV4::Value::Managed_Type); #endif #ifndef QT_NO_DEBUG ++scope.size; #endif } - // ### GC FIX casting below to be safe Scoped(const Scope &scope, const Value &v) { ptr = scope.engine->jsStackTop++; - setPointer(value_cast<T>(v)); + setPointer(v.as<T>()); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -235,7 +231,7 @@ struct Scoped Value v; v = o; ptr = scope.engine->jsStackTop++; - setPointer(value_cast<T>(v)); + setPointer(v.as<T>()); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -243,7 +239,7 @@ struct Scoped Scoped(const Scope &scope, const ScopedValue &v) { ptr = scope.engine->jsStackTop++; - setPointer(value_cast<T>(*v.ptr)); + setPointer(v.ptr->as<T>()); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -252,7 +248,7 @@ struct Scoped Scoped(const Scope &scope, const Value &v, _Convert) { ptr = scope.engine->jsStackTop++; - ptr->val = value_convert<T>(scope.engine, v); + ptr->setRawValue(value_convert<T>(scope.engine, v)); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -261,7 +257,7 @@ struct Scoped Scoped(const Scope &scope, const Value *v) { ptr = scope.engine->jsStackTop++; - setPointer(v ? value_cast<T>(*v) : 0); + setPointer(v ? v->as<T>() : 0); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -287,7 +283,7 @@ struct Scoped Scoped(const Scope &scope, const ReturnedValue &v) { ptr = scope.engine->jsStackTop++; - setPointer(value_cast<T>(QV4::Value::fromReturnedValue(v))); + setPointer(QV4::Value::fromReturnedValue(v).as<T>()); #ifndef QT_NO_DEBUG ++scope.size; #endif @@ -295,16 +291,14 @@ struct Scoped Scoped(const Scope &scope, const ReturnedValue &v, _Convert) { ptr = scope.engine->jsStackTop++; - ptr->val = value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v)); + ptr->setRawValue(value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v))); #ifndef QT_NO_DEBUG ++scope.size; #endif } Scoped<T> &operator=(Heap::Base *o) { - Value v; - v = o; - setPointer(value_cast<T>(v)); + setPointer(Value::fromHeapObject(o).as<T>()); return *this; } Scoped<T> &operator=(typename T::Data *t) { @@ -312,16 +306,16 @@ struct Scoped return *this; } Scoped<T> &operator=(const Value &v) { - setPointer(value_cast<T>(v)); + setPointer(v.as<T>()); return *this; } Scoped<T> &operator=(Value *v) { - setPointer(v ? value_cast<T>(*v) : 0); + setPointer(v ? v->as<T>() : 0); return *this; } Scoped<T> &operator=(const ReturnedValue &v) { - setPointer(value_cast<T>(QV4::Value::fromReturnedValue(v))); + setPointer(QV4::Value::fromReturnedValue(v).as<T>()); return *this; } @@ -347,21 +341,21 @@ struct Scoped } bool operator!() const { - return !ptr->m; + return !ptr->m(); } operator void *() const { - return ptr->m; + return ptr->m(); } T *getPointer() { return ptr->cast<T>(); } - typename T::Data **getRef() { - return reinterpret_cast<typename T::Data **>(&ptr->m); + Value *getRef() { + return ptr; } ReturnedValue asReturnedValue() const { - return ptr->m ? ptr->val : Encode::undefined(); + return ptr->m() ? ptr->rawValue() : Encode::undefined(); } Value *ptr; @@ -390,41 +384,14 @@ struct ScopedCallData { inline Value &Value::operator =(const ScopedValue &v) { - val = v.ptr->val; + _val = v.ptr->val(); return *this; } template<typename T> inline Value &Value::operator=(const Scoped<T> &t) { - val = t.ptr->val; - return *this; -} - -template<typename T> -inline TypedValue<T> &TypedValue<T>::operator =(T *t) -{ - m = t ? t->m : 0; -#if QT_POINTER_SIZE == 4 - tag = Managed_Type; -#endif - return *this; -} - -template<typename T> -inline TypedValue<T> &TypedValue<T>::operator =(const Scoped<T> &v) -{ - m = v.ptr->m; -#if QT_POINTER_SIZE == 4 - tag = Managed_Type; -#endif - return *this; -} - -template<typename T> -inline TypedValue<T> &TypedValue<T>::operator=(const TypedValue<T> &t) -{ - val = t.val; + _val = t.ptr->val(); return *this; } @@ -453,18 +420,18 @@ struct ExecutionContextSaver : engine(context->d()->engine) , savedContext(scope.alloc(1)) { - savedContext->m = context->d(); + savedContext->setM(context->d()); #if QT_POINTER_SIZE == 4 - savedContext->tag = QV4::Value::Managed_Type; + savedContext->setTag(QV4::Value::Managed_Type); #endif } ExecutionContextSaver(Scope &scope, Heap::ExecutionContext *context) : engine(context->engine) , savedContext(scope.alloc(1)) { - savedContext->m = context; + savedContext->setM(context); #if QT_POINTER_SIZE == 4 - savedContext->tag = QV4::Value::Managed_Type; + savedContext->setTag(QV4::Value::Managed_Type); #endif } ~ExecutionContextSaver() diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 4fde0e2445..0bf4f21acb 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -32,11 +32,12 @@ ****************************************************************************/ #include "qv4script_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4functionobject_p.h" #include "qv4function_p.h" #include "qv4context_p.h" #include "qv4debugging_p.h" +#include "qv4profiling_p.h" #include "qv4scopedvalue_p.h" #include <private/qqmljsengine_p.h> @@ -44,6 +45,7 @@ #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> #include <private/qqmlengine_p.h> +#include <private/qv4profiling_p.h> #include <qv4jsir_p.h> #include <qv4codegen_p.h> #include <private/qqmlcontextwrapper_p.h> @@ -86,8 +88,8 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(QmlBindingWrapper); DEFINE_OBJECT_VTABLE(CompilationUnitHolder); -Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::ExecutionContext *scope, Function *f, QV4::Object *qml) - : Heap::FunctionObject(scope, scope->d()->engine->id_eval, /*createProto = */ false) +Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::ExecutionContext *scope, Function *f, QV4::QmlContextWrapper *qml) + : Heap::FunctionObject(scope, scope->d()->engine->id_eval(), /*createProto = */ false) , qml(qml->d()) { Q_ASSERT(scope->inUse()); @@ -97,46 +99,43 @@ Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::ExecutionContext *scope, Functio function->compilationUnit->addref(); Scope s(scope); - Scoped<QV4::QmlBindingWrapper> o(s, this); + Scoped<QV4::QmlBindingWrapper> protectThis(s, this); - o->defineReadonlyProperty(scope->d()->engine->id_length, Primitive::fromInt32(1)); - - ScopedContext ctx(s, s.engine->currentContext()); - o->d()->qmlContext = ctx->newQmlContext(o, qml); - s.engine->popContext(); + this->scope = scope->newQmlContext(qml); + internalClass->engine->popContext(); } -Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::ExecutionContext *scope, QV4::Object *qml) - : Heap::FunctionObject(scope, scope->d()->engine->id_eval, /*createProto = */ false) +Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::ExecutionContext *scope, QV4::QmlContextWrapper *qml) + : Heap::FunctionObject(scope, scope->d()->engine->id_eval(), /*createProto = */ false) , qml(qml->d()) { Q_ASSERT(scope->inUse()); Scope s(scope); - Scoped<QV4::QmlBindingWrapper> o(s, this); - - o->defineReadonlyProperty(scope->d()->engine->id_length, Primitive::fromInt32(1)); + Scoped<QV4::QmlBindingWrapper> protectThis(s, this); - ScopedContext ctx(s, s.engine->currentContext()); - o->d()->qmlContext = ctx->newQmlContext(o, qml); - s.engine->popContext(); + this->scope = scope->newQmlContext(qml); + internalClass->engine->popContext(); } -ReturnedValue QmlBindingWrapper::call(Managed *that, CallData *) +ReturnedValue QmlBindingWrapper::call(const Managed *that, CallData *callData) { - ExecutionEngine *engine = static_cast<Object *>(that)->engine(); - CHECK_STACK_LIMITS(engine); + const QmlBindingWrapper *This = static_cast<const QmlBindingWrapper *>(that); + ExecutionEngine *v4 = static_cast<const Object *>(that)->engine(); + if (v4->hasException) + return Encode::undefined(); + CHECK_STACK_LIMITS(v4); - Scope scope(engine); - QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that); - if (!This->function()) + Scope scope(v4); + QV4::Function *f = This->function(); + if (!f) return QV4::Encode::undefined(); - Scoped<CallContext> ctx(scope, This->d()->qmlContext); - std::fill(ctx->d()->locals, ctx->d()->locals + ctx->d()->function->varCount(), Primitive::undefinedValue()); - engine->pushContext(ctx); - ScopedValue result(scope, This->function()->code(engine, This->function()->codeData)); - engine->popContext(); + ScopedContext context(scope, v4->currentContext()); + Scoped<CallContext> ctx(scope, context->newCallContext(This, callData)); + + ExecutionContextSaver ctxSaver(scope, context); + ScopedValue result(scope, Q_V4_PROFILE(v4, f)); return result->asReturnedValue(); } @@ -147,14 +146,12 @@ void QmlBindingWrapper::markObjects(Heap::Base *m, ExecutionEngine *e) if (wrapper->qml) wrapper->qml->mark(e); FunctionObject::markObjects(m, e); - if (wrapper->qmlContext) - wrapper->qmlContext->mark(e); } static ReturnedValue signalParameterGetter(QV4::CallContext *ctx, uint parameterIndex) { QV4::Scope scope(ctx); - QV4::Scoped<CallContext> signalEmittingContext(scope, static_cast<Heap::CallContext *>(ctx->d()->parent)); + QV4::Scoped<CallContext> signalEmittingContext(scope, ctx->d()->parent.cast<Heap::CallContext>()); Q_ASSERT(signalEmittingContext && signalEmittingContext->d()->type >= QV4::Heap::ExecutionContext::Type_SimpleCallContext); return signalEmittingContext->argument(parameterIndex); } @@ -163,10 +160,10 @@ Heap::FunctionObject *QmlBindingWrapper::createQmlCallableForFunction(QQmlContex { ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(qmlContext->engine); QV4::Scope valueScope(engine); - QV4::ScopedObject qmlScopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(engine, qmlContext, scopeObject)); + QV4::Scoped<QmlContextWrapper> qmlScopeObject(valueScope, QV4::QmlContextWrapper::qmlScope(engine, qmlContext, scopeObject)); ScopedContext global(valueScope, valueScope.engine->rootContext()); QV4::Scoped<QV4::QmlBindingWrapper> wrapper(valueScope, engine->memoryManager->alloc<QV4::QmlBindingWrapper>(global, qmlScopeObject)); - QV4::Scoped<CallContext> wrapperContext(valueScope, wrapper->context()); + QV4::Scoped<QmlContext> wrapperContext(valueScope, wrapper->context()); if (!signalParameters.isEmpty()) { if (error) @@ -214,7 +211,7 @@ void Script::parse() parsed = true; - ExecutionEngine *v4 = scope->engine; + ExecutionEngine *v4 = scope->engine(); Scope valueScope(v4); MemoryManager::GCBlocker gcBlocker(v4->memoryManager); @@ -285,7 +282,7 @@ ReturnedValue Script::run() if (!vmFunction) return Encode::undefined(); - QV4::ExecutionEngine *engine = scope->engine; + QV4::ExecutionEngine *engine = scope->engine(); QV4::Scope valueScope(engine); if (qml.isUndefined()) { @@ -293,15 +290,14 @@ ReturnedValue Script::run() ExecutionContextSaver ctxSaver(valueScope, scope); ContextStateSaver stateSaver(valueScope, scope); - scope->strictMode = vmFunction->isStrict(); - scope->lookups = vmFunction->compilationUnit->runtimeLookups; - scope->compilationUnit = vmFunction->compilationUnit; + scope->d()->strictMode = vmFunction->isStrict(); + scope->d()->lookups = vmFunction->compilationUnit->runtimeLookups; + scope->d()->compilationUnit = vmFunction->compilationUnit; - return vmFunction->code(engine, vmFunction->codeData); + return Q_V4_PROFILE(engine, vmFunction); } else { - ScopedObject qmlObj(valueScope, qml.value()); - ScopedContext ctx(valueScope, scope); - ScopedFunctionObject f(valueScope, engine->memoryManager->alloc<QmlBindingWrapper>(ctx, vmFunction, qmlObj)); + Scoped<QmlContextWrapper> qmlObj(valueScope, qml.value()); + ScopedFunctionObject f(valueScope, engine->memoryManager->alloc<QmlBindingWrapper>(scope, vmFunction, qmlObj)); ScopedCallData callData(valueScope); callData->thisObject = Primitive::undefinedValue(); return f->call(callData); @@ -376,11 +372,10 @@ ReturnedValue Script::qmlBinding() { if (!parsed) parse(); - ExecutionEngine *v4 = scope->engine; + ExecutionEngine *v4 = scope->engine(); Scope valueScope(v4); - ScopedObject qmlObj(valueScope, qml.value()); - ScopedContext ctx(valueScope, scope); - ScopedObject v(valueScope, v4->memoryManager->alloc<QmlBindingWrapper>(ctx, vmFunction, qmlObj)); + Scoped<QmlContextWrapper> qmlObj(valueScope, qml.value()); + ScopedObject v(valueScope, v4->memoryManager->alloc<QmlBindingWrapper>(scope, vmFunction, qmlObj)); return v.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 05a9e45f45..77784dfc4f 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -63,7 +63,7 @@ struct ContextStateSaver { , compilationUnit(context->d()->compilationUnit) , lineNumber(context->d()->lineNumber) { - savedContext->m = context->d(); + savedContext->setM(context->d()); } ContextStateSaver(Scope &scope, Heap::ExecutionContext *context) : savedContext(scope.alloc(1)) @@ -72,12 +72,12 @@ struct ContextStateSaver { , compilationUnit(context->compilationUnit) , lineNumber(context->lineNumber) { - savedContext->m = context; + savedContext->setM(context); } ~ContextStateSaver() { - Heap::ExecutionContext *ctx = static_cast<Heap::ExecutionContext *>(savedContext->m); + Heap::ExecutionContext *ctx = static_cast<Heap::ExecutionContext *>(savedContext->m()); ctx->strictMode = strictMode; ctx->lookups = lookups; ctx->compilationUnit = compilationUnit; @@ -87,11 +87,10 @@ struct ContextStateSaver { namespace Heap { struct QmlBindingWrapper : Heap::FunctionObject { - QmlBindingWrapper(QV4::ExecutionContext *scope, Function *f, QV4::Object *qml); + QmlBindingWrapper(QV4::ExecutionContext *scope, Function *f, QV4::QmlContextWrapper *qml); // Constructor for QML functions and signal handlers, resulting binding wrapper is not callable! - QmlBindingWrapper(QV4::ExecutionContext *scope, QV4::Object *qml); - Object *qml; - CallContext *qmlContext; + QmlBindingWrapper(QV4::ExecutionContext *scope, QV4::QmlContextWrapper *qml); + Pointer<Object> qml; }; } @@ -99,21 +98,19 @@ struct QmlBindingWrapper : Heap::FunctionObject { struct Q_QML_EXPORT QmlBindingWrapper : FunctionObject { V4_OBJECT2(QmlBindingWrapper, FunctionObject) - static ReturnedValue call(Managed *that, CallData *); + static ReturnedValue call(const Managed *that, CallData *callData); static void markObjects(Heap::Base *m, ExecutionEngine *e); - Heap::CallContext *context() const { return d()->qmlContext; } + Heap::QmlContext *context() const { return static_cast<Heap::QmlContext *>(d()->scope.ptr); } static Heap::FunctionObject *createQmlCallableForFunction(QQmlContextData *qmlContext, QObject *scopeObject, QV4::Function *runtimeFunction, const QList<QByteArray> &signalParameters = QList<QByteArray>(), QString *error = 0); - -private: }; struct Q_QML_EXPORT Script { Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , scope(scope->d()), strictMode(false), inheritContext(false), parsed(false) + , scope(scope), strictMode(false), inheritContext(false), parsed(false) , vmFunction(0), parseAsBinding(false) {} Script(ExecutionEngine *engine, Object *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) @@ -125,8 +122,7 @@ struct Q_QML_EXPORT Script { int line; int column; QString sourceCode; - // ### GC - Heap::ExecutionContext *scope; + ExecutionContext *scope; bool strictMode; bool inheritContext; bool parsed; diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index f1f546bece..23bbcc60e1 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -175,7 +175,7 @@ template <> QUrl convertValueToElement(const Value &value) template <> QModelIndex convertValueToElement(const Value &value) { - const QQmlValueTypeWrapper *v = value_cast<QQmlValueTypeWrapper>(value); + const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); if (v) return v->toVariant().toModelIndex(); return QModelIndex(); @@ -183,7 +183,7 @@ template <> QModelIndex convertValueToElement(const Value &value) template <> QItemSelectionRange convertValueToElement(const Value &value) { - const QQmlValueTypeWrapper *v = value_cast<QQmlValueTypeWrapper>(value); + const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); if (v) return v->toVariant().value<QItemSelectionRange>(); return QItemSelectionRange(); @@ -231,7 +231,7 @@ public: defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length); } - QV4::ReturnedValue containerGetIndexed(uint index, bool *hasProperty) + QV4::ReturnedValue containerGetIndexed(uint index, bool *hasProperty) const { /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) { @@ -316,9 +316,9 @@ public: return (signedIdx < d()->container.count()) ? QV4::Attr_Data : QV4::Attr_Invalid; } - void containerAdvanceIterator(ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attrs) + void containerAdvanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { - *name = (Heap::String *)0; + name->setM(0); *index = UINT_MAX; if (d()->isReference) { @@ -400,7 +400,7 @@ public: ScopedCallData callData(scope, 2); callData->args[0] = convertElementToValue(this->m_ctx->d()->engine, lhs); callData->args[1] = convertElementToValue(this->m_ctx->d()->engine, rhs); - callData->thisObject = this->m_ctx->d()->engine->globalObject(); + callData->thisObject = this->m_ctx->d()->engine->globalObject; QV4::ScopedValue result(scope, compare->call(callData)); return result->toNumber() < 0; } @@ -419,7 +419,7 @@ public: } QV4::Scope scope(ctx); - if (ctx->argc() == 1 && ctx->args()[0].asFunctionObject()) { + if (ctx->argc() == 1 && ctx->args()[0].as<FunctionObject>()) { CompareFunctor cf(ctx, ctx->args()[0]); std::sort(d()->container.begin(), d()->container.end(), cf); } else { @@ -526,8 +526,8 @@ public: QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a); } - static QV4::ReturnedValue getIndexed(QV4::Managed *that, uint index, bool *hasProperty) - { return static_cast<QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); } + 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 QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) @@ -536,7 +536,7 @@ public: { return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); } static bool isEqualTo(Managed *that, Managed *other) { return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); } - static void advanceIterator(Managed *that, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attrs) + static void advanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { return static_cast<QQmlSequence<Container> *>(that)->containerAdvanceIterator(it, name, index, p, attrs); } }; @@ -544,7 +544,7 @@ public: template <typename Container> Heap::QQmlSequence<Container>::QQmlSequence(QV4::ExecutionEngine *engine, const Container &container) - : Heap::Object(engine->emptyClass, engine->sequencePrototype.asObject()) + : Heap::Object(engine->emptyClass, engine->sequencePrototype()) , container(container) , propertyIndex(-1) , isReference(false) @@ -557,7 +557,7 @@ Heap::QQmlSequence<Container>::QQmlSequence(QV4::ExecutionEngine *engine, const template <typename Container> Heap::QQmlSequence<Container>::QQmlSequence(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex) - : Heap::Object(engine->emptyClass, engine->sequencePrototype.asObject()) + : Heap::Object(engine->emptyClass, engine->sequencePrototype()) , object(object) , propertyIndex(propertyIndex) , isReference(true) @@ -605,7 +605,7 @@ void SequencePrototype::init() { FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE) defineDefaultProperty(QStringLiteral("sort"), method_sort, 1); - defineDefaultProperty(engine()->id_valueOf, method_valueOf, 0); + defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0); } #undef REGISTER_QML_SEQUENCE_METATYPE @@ -700,11 +700,11 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, boo { *succeeded = true; - if (!array.asArrayObject()) { + if (!array.as<ArrayObject>()) { *succeeded = false; return QVariant(); } - QV4::Scope scope(array.asObject()->engine()); + QV4::Scope scope(array.as<Object>()->engine()); QV4::ScopedArrayObject a(scope, array); FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); } @@ -717,7 +717,7 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, boo return qMetaTypeId<SequenceType>(); \ } else -int SequencePrototype::metaTypeForSequence(QV4::Object *object) +int SequencePrototype::metaTypeForSequence(const QV4::Object *object) { FOREACH_QML_SEQUENCE_TYPE(MAP_META_TYPE) /*else*/ { diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index 9949278a89..560c3c27ca 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -48,9 +48,10 @@ #include <QtCore/qglobal.h> #include <QtCore/qvariant.h> -#include "qv4value_inl_p.h" +#include "qv4value_p.h" #include "qv4object_p.h" #include "qv4context_p.h" +#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -70,7 +71,7 @@ struct SequencePrototype : public QV4::Object static bool isSequenceType(int sequenceTypeId); static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded); - static int metaTypeForSequence(Object *object); + static int metaTypeForSequence(const Object *object); static QVariant toVariant(Object *object); static QVariant toVariant(const Value &array, int typeHint, bool *succeeded); }; diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp index 31d85df13e..33b40796a4 100644 --- a/src/qml/jsruntime/qv4serialize.cpp +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -37,7 +37,7 @@ #include <private/qqmllistmodel_p.h> #include <private/qqmllistmodelworkeragent_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4regexpobject_p.h> #include <private/qv4sequenceobject_p.h> @@ -168,11 +168,11 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine char *buffer = data.data() + offset; memcpy(buffer, qstr.constData(), length*sizeof(QChar)); - } else if (v.asFunctionObject()) { + } else if (v.as<FunctionObject>()) { // XXX TODO: Implement passing function objects between the main and // worker scripts push(data, valueheader(WorkerUndefined)); - } else if (QV4::ArrayObject *array = v.asArrayObject()) { + } else if (const QV4::ArrayObject *array = v.as<ArrayObject>()) { uint length = array->getLength(); if (length > 0xFFFFFF) { push(data, valueheader(WorkerUndefined)); @@ -195,11 +195,11 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine reserve(data, sizeof(quint32) + sizeof(double)); push(data, valueheader(WorkerNumber)); push(data, v.asDouble()); - } else if (QV4::DateObject *d = v.asDateObject()) { + } else if (const QV4::DateObject *d = v.as<DateObject>()) { reserve(data, sizeof(quint32) + sizeof(double)); push(data, valueheader(WorkerDate)); - push(data, d->date().asDouble()); - } else if (RegExpObject *re = v.as<RegExpObject>()) { + push(data, d->date()); + } else if (const RegExpObject *re = v.as<RegExpObject>()) { quint32 flags = re->flags(); QString pattern = re->source(); int length = pattern.length() + 1; @@ -218,7 +218,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine char *buffer = data.data() + offset; memcpy(buffer, pattern.constData(), length*sizeof(QChar)); - } else if (QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { + } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { // XXX TODO: Generalize passing objects between the main thread and worker scripts so // that others can trivially plug in their elements. QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object()); @@ -231,10 +231,10 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine } // No other QObject's are allowed to be sent push(data, valueheader(WorkerUndefined)); - } else if (Object *o = v.asObject()) { + } else if (const Object *o = v.as<Object>()) { if (o->isListType()) { // valid sequence. we generate a length (sequence length + 1 for the sequence type) - uint seqLength = ScopedValue(scope, o->get(engine->id_length))->toUInt32(); + uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32(); uint length = seqLength + 1; if (length > 0xFFFFFF) { push(data, valueheader(WorkerUndefined)); @@ -265,7 +265,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine s = properties->getIndexed(ii); serialize(data, s, engine); - QV4::String *str = s->asString(); + QV4::String *str = s->as<String>(); val = o->get(str); if (scope.hasException()) scope.engine->catchException(); @@ -356,7 +356,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) QVariant var = qVariantFromValue(ref); QV4::ScopedValue v(scope, scope.engine->fromVariant(var)); QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref"))); - rv->asObject()->defineReadonlyProperty(s, v); + rv->as<Object>()->defineReadonlyProperty(s, v); agent->release(); agent->setEngine(engine); diff --git a/src/qml/jsruntime/qv4serialize_p.h b/src/qml/jsruntime/qv4serialize_p.h index 06eaffe4c4..d5d48edee7 100644 --- a/src/qml/jsruntime/qv4serialize_p.h +++ b/src/qml/jsruntime/qv4serialize_p.h @@ -46,7 +46,7 @@ // #include <QtCore/qbytearray.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h index 861c7dd28d..8496fc32e7 100644 --- a/src/qml/jsruntime/qv4sparsearray_p.h +++ b/src/qml/jsruntime/qv4sparsearray_p.h @@ -35,11 +35,7 @@ #define QV4SPARSEARRAY_H #include "qv4global_p.h" -#include <QtCore/qmap.h> -#include "qv4value_inl_p.h" -#include "qv4scopedvalue_p.h" -#include "qv4property_p.h" -#include <assert.h> +#include <QtCore/qlist.h> //#define Q_MAP_DEBUG #ifdef Q_MAP_DEBUG @@ -188,7 +184,7 @@ public: typedef qptrdiff difference_type; typedef int size_type; -#ifndef QT_NO_DEBUG +#ifdef Q_MAP_DEBUG void dump() const; #endif }; @@ -261,7 +257,7 @@ inline void SparseArray::push_back(uint index, uint len) n->value = index; } -#ifndef QT_NO_DEBUG +#ifdef Q_MAP_DEBUG inline void SparseArray::dump() const { const SparseArrayNode *it = begin(); @@ -344,4 +340,4 @@ inline SparseArrayNode *SparseArray::upperBound(uint akey) QT_END_NAMESPACE -#endif // QMAP_H +#endif diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 6d55eb2c18..24a13ddd10 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -32,7 +32,7 @@ ****************************************************************************/ #include "qv4string_p.h" -#include "qv4value_inl_p.h" +#include "qv4value_p.h" #ifndef V4_BOOTSTRAP #include "qv4identifiertable_p.h" #include "qv4runtime_p.h" @@ -110,7 +110,7 @@ bool String::isEqualTo(Managed *t, Managed *o) if (t == o) return true; - if (!o->d()->vtable->isString) + if (!o->d()->vtable()->isString) return false; return static_cast<String *>(t)->isEqualTo(static_cast<String *>(o)); diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 1cf8f51a29..93a25d7ed5 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -188,6 +188,20 @@ public: static uint toArrayIndex(const QString &str); }; +template<> +inline const String *Value::as() const { + return isManaged() && m() && m()->vtable()->isString ? static_cast<const String *>(this) : 0; +} + +#ifndef V4_BOOTSTRAP +template<> +inline ReturnedValue value_convert<String>(ExecutionEngine *e, const Value &v) +{ + return v.toString(e)->asReturnedValue(); +} +#endif + + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index e0b84f6da3..9fbafa7ded 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -36,7 +36,7 @@ #include "qv4regexp_p.h" #include "qv4regexpobject_p.h" #include "qv4objectproto_p.h" -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4alloca_p.h" #include <QtCore/QDateTime> @@ -70,44 +70,45 @@ DEFINE_OBJECT_VTABLE(StringObject); Heap::StringObject::StringObject(InternalClass *ic, QV4::Object *prototype) : Heap::Object(ic, prototype) { - Q_ASSERT(vtable == QV4::StringObject::staticVTable()); - value = ic->engine->newString()->asReturnedValue(); - tmpProperty.value = Primitive::undefinedValue(); + Q_ASSERT(vtable() == QV4::StringObject::staticVTable()); + string = ic->engine->newString(); Scope scope(ic->engine); ScopedObject s(scope, this); - s->defineReadonlyProperty(ic->engine->id_length, Primitive::fromInt32(0)); + s->defineReadonlyProperty(ic->engine->id_length(), Primitive::fromInt32(0)); } -Heap::StringObject::StringObject(ExecutionEngine *engine, const Value &val) - : Heap::Object(engine->emptyClass, engine->stringPrototype.asObject()) +Heap::StringObject::StringObject(ExecutionEngine *engine, const QV4::String *str) + : Heap::Object(engine->emptyClass, engine->stringPrototype()) { - value = val; - Q_ASSERT(value.isString()); - tmpProperty.value = Primitive::undefinedValue(); + string = str->d(); Scope scope(engine); ScopedObject s(scope, this); - s->defineReadonlyProperty(engine->id_length, Primitive::fromUInt32(value.stringValue()->toQString().length())); + s->defineReadonlyProperty(engine->id_length(), Primitive::fromUInt32(length())); } -Property *Heap::StringObject::getIndex(uint index) const +Heap::String *Heap::StringObject::getIndex(uint index) const { - QString str = value.stringValue()->toQString(); + QString str = string->toQString(); if (index >= (uint)str.length()) return 0; - tmpProperty.value = Encode(internalClass->engine->newString(str.mid(index, 1))); - return &tmpProperty; + return internalClass->engine->newString(str.mid(index, 1)); +} + +uint Heap::StringObject::length() const +{ + return string->toQString().length(); } bool StringObject::deleteIndexedProperty(Managed *m, uint index) { ExecutionEngine *v4 = static_cast<StringObject *>(m)->engine(); Scope scope(v4); - Scoped<StringObject> o(scope, m->asStringObject()); + Scoped<StringObject> o(scope, m->as<StringObject>()); Q_ASSERT(!!o); - if (index < static_cast<uint>(o->d()->value.stringValue()->toQString().length())) { + if (index < static_cast<uint>(o->d()->string->toQString().length())) { if (v4->currentContext()->strictMode) v4->throwTypeError(); return false; @@ -115,20 +116,21 @@ bool StringObject::deleteIndexedProperty(Managed *m, uint index) return true; } -void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attrs) +void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { - *name = (Heap::String *)0; + name->setM(0); StringObject *s = static_cast<StringObject *>(m); - uint slen = s->d()->value.stringValue()->toQString().length(); + uint slen = s->d()->string->toQString().length(); if (it->arrayIndex <= slen) { while (it->arrayIndex < slen) { *index = it->arrayIndex; ++it->arrayIndex; PropertyAttributes a; - Property *pd = s->__getOwnProperty__(*index, &a); + Property pd; + s->getOwnProperty(*index, &a, &pd); if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { *attrs = a; - p->copy(pd, a); + p->copy(&pd, a); return; } } @@ -146,8 +148,7 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Heap::String void StringObject::markObjects(Heap::Base *that, ExecutionEngine *e) { StringObject::Data *o = static_cast<StringObject::Data *>(that); - o->value.stringValue()->mark(e); - o->tmpProperty.value.mark(e); + o->string->mark(e); Object::markObjects(that, e); } @@ -158,11 +159,11 @@ Heap::StringCtor::StringCtor(QV4::ExecutionContext *scope) { } -ReturnedValue StringCtor::construct(Managed *m, CallData *callData) +ReturnedValue StringCtor::construct(const Managed *m, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); Scope scope(v4); - ScopedValue value(scope); + ScopedString value(scope); if (callData->argc) value = callData->args[0].toString(v4); else @@ -170,9 +171,9 @@ ReturnedValue StringCtor::construct(Managed *m, CallData *callData) return Encode(v4->newStringObject(value)); } -ReturnedValue StringCtor::call(Managed *m, CallData *callData) +ReturnedValue StringCtor::call(const Managed *m, CallData *callData) { - ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); Scope scope(v4); ScopedValue value(scope); if (callData->argc) @@ -187,13 +188,13 @@ void StringPrototype::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(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), method_fromCharCode, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString, method_toString); - defineDefaultProperty(engine->id_valueOf, method_toString); // valueOf and toString are identical + defineDefaultProperty(engine->id_toString(), method_toString); + defineDefaultProperty(engine->id_valueOf(), method_toString); // valueOf and toString are identical defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1); defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1); defineDefaultProperty(QStringLiteral("concat"), method_concat, 1); @@ -220,8 +221,8 @@ static QString getThisString(ExecutionContext *ctx) ScopedValue t(scope, ctx->thisObject()); if (t->isString()) return t->stringValue()->toQString(); - if (StringObject *thisString = t->asStringObject()) - return thisString->d()->value.stringValue()->toQString(); + if (StringObject *thisString = t->as<StringObject>()) + return thisString->d()->string->toQString(); if (t->isUndefined() || t->isNull()) { scope.engine->throwTypeError(); return QString(); @@ -234,10 +235,10 @@ ReturnedValue StringPrototype::method_toString(CallContext *context) if (context->thisObject().isString()) return context->thisObject().asReturnedValue(); - StringObject *o = context->thisObject().asStringObject(); + StringObject *o = context->thisObject().as<StringObject>(); if (!o) return context->engine()->throwTypeError(); - return o->d()->value.asReturnedValue(); + return Encode(o->d()->string); } ReturnedValue StringPrototype::method_charAt(CallContext *context) @@ -368,7 +369,7 @@ ReturnedValue StringPrototype::method_match(CallContext *context) if (!rx) { ScopedCallData callData(scope, 1); callData->args[0] = regexp; - rx = context->d()->engine->regExpCtor.asFunctionObject()->construct(callData); + rx = context->d()->engine->regExpCtor()->construct(callData); } if (!rx) @@ -379,7 +380,7 @@ ReturnedValue StringPrototype::method_match(CallContext *context) // ### use the standard builtin function, not the one that might be redefined in the proto ScopedString execString(scope, scope.engine->newString(QStringLiteral("exec"))); - ScopedFunctionObject exec(scope, scope.engine->regExpPrototype.asObject()->get(execString)); + ScopedFunctionObject exec(scope, scope.engine->regExpPrototype()->get(execString)); ScopedCallData callData(scope, 1); callData->thisObject = rx; @@ -470,8 +471,8 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) { Scope scope(ctx); QString string; - if (StringObject *thisString = ctx->thisObject().asStringObject()) - string = thisString->d()->value.stringValue()->toQString(); + if (StringObject *thisString = ctx->thisObject().as<StringObject>()) + string = thisString->d()->string->toQString(); else string = ctx->thisObject().toQString(); @@ -481,12 +482,12 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) uint allocatedMatchOffsets = 64; uint _matchOffsets[64]; uint *matchOffsets = _matchOffsets; - uint nMatchOffsets = 0; ScopedValue searchValue(scope, ctx->argument(0)); Scoped<RegExpObject> regExp(scope, searchValue); if (regExp) { uint offset = 0; + uint nMatchOffsets = 0; // We extract the pointer here to work around a compiler bug on Android. Scoped<RegExp> re(scope, regExp->value()); @@ -519,7 +520,6 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx) int idx = string.indexOf(searchString); if (idx != -1) { numStringMatches = 1; - nMatchOffsets = 2; matchOffsets[0] = idx; matchOffsets[1] = idx + searchString.length(); } @@ -593,7 +593,7 @@ ReturnedValue StringPrototype::method_search(CallContext *ctx) if (!regExp) { ScopedCallData callData(scope, 1); callData->args[0] = regExpValue; - regExpValue = ctx->d()->engine->regExpCtor.asFunctionObject()->construct(callData); + regExpValue = ctx->d()->engine->regExpCtor()->construct(callData); if (scope.engine->hasException) return Encode::undefined(); regExp = regExpValue->as<RegExpObject>(); diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 459dc1322e..aa56a79bc3 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -45,12 +45,11 @@ namespace Heap { struct StringObject : Object { StringObject(InternalClass *ic, QV4::Object *prototype); - StringObject(ExecutionEngine *engine, const Value &value); - Value value; + StringObject(ExecutionEngine *engine, const QV4::String *string); + String *string; - Property *getIndex(uint index) const; - // ### get rid of tmpProperty - mutable Property tmpProperty; + Heap::String *getIndex(uint index) const; + uint length() const; }; struct StringCtor : FunctionObject { @@ -63,14 +62,17 @@ struct StringObject: Object { V4_OBJECT2(StringObject, Object) Q_MANAGED_TYPE(StringObject) - Property *getIndex(uint index) const { + Heap::String *getIndex(uint index) const { return d()->getIndex(index); } + uint length() const { + return d()->length(); + } static bool deleteIndexedProperty(Managed *m, uint index); protected: - static void advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attrs); + static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; @@ -78,8 +80,8 @@ struct StringCtor: FunctionObject { V4_OBJECT2(StringCtor, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; struct StringPrototype: StringObject diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 19e541dba8..f06eeb08b9 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -32,6 +32,7 @@ ****************************************************************************/ #include "qv4typedarray_p.h" #include "qv4arraybuffer_p.h" +#include "qv4string_p.h" #include <cmath> @@ -201,10 +202,10 @@ Heap::TypedArrayCtor::TypedArrayCtor(QV4::ExecutionContext *scope, TypedArray::T { } -ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) +ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData) { - Scope scope(static_cast<Object *>(m)->engine()); - Scoped<TypedArrayCtor> that(scope, static_cast<TypedArrayCtor *>(m)); + Scope scope(static_cast<const Object *>(m)->engine()); + Scoped<TypedArrayCtor> that(scope, static_cast<const TypedArrayCtor *>(m)); if (!callData->argc || !callData->args[0].isObject()) { // ECMA 6 22.2.1.1 @@ -257,7 +258,7 @@ ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) TypedArrayWrite write =array->d()->type->write; for (uint i = 0; i < l; ++i) { Primitive val; - val.val = read(src, i*srcElementSize); + val.setRawValue(read(src, i*srcElementSize)); write(scope.engine, dest, i*destElementSize, val); } } @@ -299,7 +300,7 @@ ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) // ECMA 6 22.2.1.3 ScopedObject o(scope, callData->argument(0)); - uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length))->toInteger(), (double)UINT_MAX); + uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length()))->toInteger(), (double)UINT_MAX); if (scope.engine->hasException) return scope.engine->throwTypeError(); @@ -329,13 +330,13 @@ ReturnedValue TypedArrayCtor::construct(Managed *m, CallData *callData) return array.asReturnedValue(); } -ReturnedValue TypedArrayCtor::call(Managed *that, CallData *callData) +ReturnedValue TypedArrayCtor::call(const Managed *that, CallData *callData) { return construct(that, callData); } Heap::TypedArray::TypedArray(ExecutionEngine *e, Type t) - : Heap::Object(e->emptyClass, e->typedArrayPrototype[t].asObject()), + : Heap::Object(e->emptyClass, e->typedArrayPrototype + t), type(operations + t), arrayType(t) { @@ -347,10 +348,10 @@ void TypedArray::markObjects(Heap::Base *that, ExecutionEngine *e) Object::markObjects(that, e); } -ReturnedValue TypedArray::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty) { - Scope scope(static_cast<Object *>(m)->engine()); - Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m)); + Scope scope(static_cast<const Object *>(m)->engine()); + Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m)); uint bytesPerElement = a->d()->type->bytesPerElement; uint byteOffset = a->d()->byteOffset + index * bytesPerElement; @@ -390,10 +391,10 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length, Primitive::fromInt32(3)); - ctor->defineReadonlyProperty(engine->id_prototype, (o = this)); + ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement)); - defineDefaultProperty(engine->id_constructor, (o = ctor)); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, 0); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, 0); defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, 0); @@ -470,7 +471,7 @@ ReturnedValue TypedArrayPrototype::method_set(CallContext *ctx) if (scope.engine->hasException || !o) return scope.engine->throwTypeError(); - double len = ScopedValue(scope, o->get(scope.engine->id_length))->toNumber(); + double len = ScopedValue(scope, o->get(scope.engine->id_length()))->toNumber(); uint l = (uint)len; if (scope.engine->hasException || l != len) return scope.engine->throwTypeError(); @@ -523,7 +524,7 @@ ReturnedValue TypedArrayPrototype::method_set(CallContext *ctx) TypedArrayWrite write = a->d()->type->write; for (uint i = 0; i < l; ++i) { Primitive val; - val.val = read(src, i*srcElementSize); + val.setRawValue(read(src, i*srcElementSize)); write(scope.engine, dest, i*elementSize, val); } @@ -563,7 +564,7 @@ ReturnedValue TypedArrayPrototype::method_subarray(CallContext *ctx) int newLen = end - begin; - ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor)); + ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); if (!constructor) return scope.engine->throwTypeError(); diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index afd1bb97e7..8e1090dcd2 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -72,7 +72,7 @@ struct TypedArray : Object { TypedArray(ExecutionEngine *e, Type t); const TypedArrayOperations *type; - ArrayBuffer *buffer; + Pointer<ArrayBuffer> buffer; uint byteLength; uint byteOffset; Type arrayType; @@ -113,7 +113,7 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object } static void markObjects(Heap::Base *that, ExecutionEngine *e); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static void putIndexed(Managed *m, uint index, const Value &value); }; @@ -121,8 +121,8 @@ struct TypedArrayCtor: FunctionObject { V4_OBJECT2(TypedArrayCtor, FunctionObject) - static ReturnedValue construct(Managed *m, CallData *callData); - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue construct(const Managed *m, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index 68228f06bb..4c81199a07 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -32,10 +32,11 @@ ****************************************************************************/ #include <qv4engine_p.h> #include <qv4runtime_p.h> +#include <qv4string_p.h> #ifndef V4_BOOTSTRAP #include <qv4object_p.h> #include <qv4objectproto_p.h> -#include "qv4mm_p.h" +#include <private/qv4mm_p.h> #endif #include <wtf/MathExtras.h> @@ -68,10 +69,32 @@ int Value::toUInt16() const return (unsigned short)number; } +bool Value::toBoolean() const +{ + switch (type()) { + case Value::Undefined_Type: + case Value::Null_Type: + return false; + case Value::Boolean_Type: + case Value::Integer_Type: + return (bool)int_32(); + case Value::Managed_Type: +#ifdef V4_BOOTSTRAP + Q_UNIMPLEMENTED(); +#else + if (isString()) + return stringValue()->toQString().length() > 0; +#endif + return true; + default: // double + return doubleValue() && !std::isnan(doubleValue()); + } +} + double Value::toInteger() const { if (integerCompatible()) - return int_32; + return int_32(); return Primitive::toInteger(toNumber()); } @@ -87,10 +110,10 @@ double Value::toNumberImpl() const #else if (isString()) return RuntimeHelpers::stringToNumber(stringValue()->toQString()); - { - Q_ASSERT(isObject()); - Scope scope(objectValue()->engine()); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, NUMBER_HINT)); + { + Q_ASSERT(isObject()); + Scope scope(objectValue()->engine()); + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, NUMBER_HINT)); if (scope.engine->hasException) return 0; return prim->toNumber(); @@ -99,7 +122,7 @@ double Value::toNumberImpl() const case QV4::Value::Null_Type: case QV4::Value::Boolean_Type: case QV4::Value::Integer_Type: - return int_32; + return int_32(); default: // double Q_UNREACHABLE(); } @@ -148,7 +171,7 @@ QString Value::toQStringNoThrow() const } case Value::Integer_Type: { QString str; - RuntimeHelpers::numberToString(&str, (double)int_32, 10); + RuntimeHelpers::numberToString(&str, (double)int_32(), 10); return str; } default: { // double @@ -184,7 +207,7 @@ QString Value::toQString() const } case Value::Integer_Type: { QString str; - RuntimeHelpers::numberToString(&str, (double)int_32, 10); + RuntimeHelpers::numberToString(&str, (double)int_32(), 10); return str; } default: { // double @@ -197,14 +220,14 @@ QString Value::toQString() const #endif // V4_BOOTSTRAP bool Value::sameValue(Value other) const { - if (val == other.val) + if (_val == other._val) return true; if (isString() && other.isString()) return stringValue()->isEqualTo(other.stringValue()); if (isInteger() && other.isDouble()) - return int_32 ? (double(int_32) == other.doubleValue()) : (other.val == 0); + return int_32() ? (double(int_32()) == other.doubleValue()) : (other._val == 0); if (isDouble() && other.isInteger()) - return other.int_32 ? (doubleValue() == double(other.int_32)) : (val == 0); + return other.int_32() ? (doubleValue() == double(other.int_32())) : (_val == 0); return false; } @@ -281,4 +304,35 @@ Heap::Object *Value::toObject(ExecutionEngine *e) const return RuntimeHelpers::convertToObject(e, *this); } +uint Value::asArrayLength(bool *ok) const +{ + *ok = true; + if (isInteger()) { + if (int_32() >= 0) { + return (uint)int_32(); + } else { + *ok = false; + return UINT_MAX; + } + } + if (isNumber()) { + double d = doubleValue(); + uint idx = (uint)d; + if (idx != d) { + *ok = false; + return UINT_MAX; + } + return idx; + } + if (isString()) + return stringValue()->toUInt(ok); + + uint idx = toUInt32(); + double d = toNumber(); + if (d != idx) { + *ok = false; + return UINT_MAX; + } + return idx; +} #endif // V4_BOOTSTRAP diff --git a/src/qml/jsruntime/qv4value_inl_p.h b/src/qml/jsruntime/qv4value_inl_p.h deleted file mode 100644 index f3026900d6..0000000000 --- a/src/qml/jsruntime/qv4value_inl_p.h +++ /dev/null @@ -1,293 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4VALUE_INL_H -#define QV4VALUE_INL_H - -#include <cmath> // this HAS to come - -#include "qv4value_p.h" - -#include "qv4string_p.h" -#include "qv4managed_p.h" -#include "qv4engine_p.h" - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -inline bool Value::isString() const -{ - if (!isManaged()) - return false; - return m && m->vtable->isString; -} -inline bool Value::isObject() const -{ - if (!isManaged()) - return false; - return m && m->vtable->isObject; -} - -inline bool Value::isPrimitive() const -{ - return !isObject(); -} - -inline String *Value::asString() const -{ - if (isString()) - return stringValue(); - return 0; -} - -inline void Value::mark(ExecutionEngine *e) const -{ - if (!val) - return; - Managed *m = asManaged(); - if (m) - m->mark(e); -} - -inline Primitive Primitive::nullValue() -{ - Primitive v; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - v.val = quint64(_Null_Type) << Tag_Shift; -#else - v.tag = _Null_Type; - v.int_32 = 0; -#endif - return v; -} - -inline Primitive Primitive::fromBoolean(bool b) -{ - Primitive v; - v.tag = _Boolean_Type; - v.int_32 = (bool)b; - return v; -} - -inline Primitive Primitive::fromDouble(double d) -{ - Primitive v; - v.setDouble(d); - return v; -} - -inline Primitive Primitive::fromInt32(int i) -{ - Primitive v; - v.tag = _Integer_Type; - v.int_32 = i; - return v; -} - -inline Primitive Primitive::fromUInt32(uint i) -{ - Primitive v; - if (i < INT_MAX) { - v.tag = _Integer_Type; - v.int_32 = (int)i; - } else { - v.setDouble(i); - } - return v; -} - -inline double Value::toNumber() const -{ - if (isInteger()) - return int_32; - if (isDouble()) - return doubleValue(); - return toNumberImpl(); -} - -inline int Value::toInt32() const -{ - if (isInteger()) - return int_32; - double d = isNumber() ? doubleValue() : toNumberImpl(); - - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - - if ((d >= -D31 && d < D31)) - return static_cast<int>(d); - - return Primitive::toInt32(d); -} - -inline unsigned int Value::toUInt32() const -{ - return (unsigned int)toInt32(); -} - - -inline bool Value::toBoolean() const -{ - switch (type()) { - case Value::Undefined_Type: - case Value::Null_Type: - return false; - case Value::Boolean_Type: - case Value::Integer_Type: - return (bool)int_32; - case Value::Managed_Type: -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else - if (isString()) - return stringValue()->toQString().length() > 0; -#endif - return true; - default: // double - return doubleValue() && !std::isnan(doubleValue()); - } -} - -#ifndef V4_BOOTSTRAP -inline uint Value::asArrayIndex() const -{ -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - if (!isNumber()) - return UINT_MAX; - if (isInteger()) - return int_32 >= 0 ? (uint)int_32 : UINT_MAX; -#else - if (isInteger() && int_32 >= 0) - return (uint)int_32; - if (!isDouble()) - return UINT_MAX; -#endif - double d = doubleValue(); - uint idx = (uint)d; - if (idx != d) - return UINT_MAX; - return idx; -} - -inline uint Value::asArrayLength(bool *ok) const -{ - *ok = true; - if (isInteger()) { - if (int_32 >= 0) { - return (uint)int_32; - } else { - *ok = false; - return UINT_MAX; - } - } - if (isNumber()) { - double d = doubleValue(); - uint idx = (uint)d; - if (idx != d) { - *ok = false; - return UINT_MAX; - } - return idx; - } - if (isString()) - return stringValue()->toUInt(ok); - - uint idx = toUInt32(); - double d = toNumber(); - if (d != idx) { - *ok = false; - return UINT_MAX; - } - return idx; -} - -inline Object *Value::asObject() const -{ - return isObject() ? objectValue() : 0; -} - -inline FunctionObject *Value::asFunctionObject() const -{ - return isObject() ? managed()->asFunctionObject() : 0; -} - -inline NumberObject *Value::asNumberObject() const -{ - return isObject() ? managed()->asNumberObject() : 0; -} - -inline StringObject *Value::asStringObject() const -{ - return isObject() ? managed()->asStringObject() : 0; -} - -inline DateObject *Value::asDateObject() const -{ - return isObject() ? managed()->asDateObject() : 0; -} - -inline ArrayObject *Value::asArrayObject() const -{ - return isObject() ? managed()->asArrayObject() : 0; -} - -inline ErrorObject *Value::asErrorObject() const -{ - return isObject() ? managed()->asErrorObject() : 0; -} - -template<typename T> -inline T *Value::as() const { Managed *m = isObject() ? managed() : 0; return m ? m->as<T>() : 0; } - -#ifndef V4_BOOTSTRAP - -template<> -inline String *value_cast(const Value &v) { - return v.asString(); -} - -template<> -inline ReturnedValue value_convert<String>(ExecutionEngine *e, const Value &v) -{ - return v.toString(e)->asReturnedValue(); -} - -#endif - -#endif - -} // namespace QV4 - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 628950784a..6305e6dfbb 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -37,6 +37,7 @@ #include <QtCore/QString> #include "qv4global_p.h" +#include <private/qv4heap_p.h> /* We cannot rely on QT_POINTER_SIZE to be set correctly on host builds. In qmldevtools the Value objects are only used to store primitives, never object pointers. So we can use the 64-bit encoding. */ @@ -50,56 +51,12 @@ QT_BEGIN_NAMESPACE namespace QV4 { -typedef uint Bool; - namespace Heap { - -struct Q_QML_EXPORT Base { - union { - const ManagedVTable *vtable; - quintptr mm_data; - }; - - inline ReturnedValue asReturnedValue() const; - inline void mark(QV4::ExecutionEngine *engine); - - enum { - MarkBit = 0x1, - NotInUse = 0x2, - PointerMask = ~0x3 - }; - - ManagedVTable *gcGetVtable() const { - return reinterpret_cast<ManagedVTable *>(mm_data & PointerMask); - } - inline bool isMarked() const { - return mm_data & MarkBit; - } - inline void setMarkBit() { - mm_data |= MarkBit; - } - inline void clearMarkBit() { - mm_data &= ~MarkBit; - } - - inline bool inUse() const { - return !(mm_data & NotInUse); - } - - Base *nextFree() { - return reinterpret_cast<Base *>(mm_data & PointerMask); - } - void setNextFree(Base *m) { - mm_data = (reinterpret_cast<quintptr>(m) | NotInUse); - } - - void *operator new(size_t, Managed *m) { return m; } - void *operator new(size_t, Heap::Base *m) { return m; } - void operator delete(void *, Heap::Base *) {} -}; - + struct Base; } +typedef uint Bool; + struct Q_QML_PRIVATE_EXPORT Value { /* @@ -124,30 +81,38 @@ struct Q_QML_PRIVATE_EXPORT Value Bit 15-17 is then used to encode other immediates. */ + quint64 _val; + + Q_ALWAYS_INLINE quint64 val() const { return _val; } + Q_ALWAYS_INLINE void setVal(quint64 v) { _val = v; } + Q_ALWAYS_INLINE void setValue(quint32 v) { setTagValue(tag(), v); } + Q_ALWAYS_INLINE void setTag(quint32 t) { setTagValue(t, value()); } - union { - quint64 val; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Heap::Base *m; -#else - double dbl; -#endif - struct { -#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN - uint tag; -#endif - union { - uint uint_32; - int int_32; -#ifndef QV4_USE_64_BIT_VALUE_ENCODING - Heap::Base *m; -#endif - }; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - uint tag; + static inline int valueOffset() { return 0; } + static inline int tagOffset() { return 4; } + Q_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } + Q_ALWAYS_INLINE quint32 value() const { return _val & quint64(~quint32(0)); } + Q_ALWAYS_INLINE quint32 tag() const { return _val >> 32; } +#else // !Q_LITTLE_ENDIAN + static inline int valueOffset() { return 4; } + static inline int tagOffset() { return 0; } + Q_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(value) << 32 | tag; } + Q_ALWAYS_INLINE quint32 tag() const { return _val & quint64(~quint32(0)); } + Q_ALWAYS_INLINE quint32 value() const { return _val >> 32; } #endif - }; - }; + +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + Q_ALWAYS_INLINE Heap::Base *m() const { Heap::Base *b; memcpy(&b, &_val, 8); return b; } + Q_ALWAYS_INLINE void setM(Heap::Base *b) { memcpy(&_val, &b, 8); } +#else // !QV4_USE_64_BIT_VALUE_ENCODING + Q_ALWAYS_INLINE Heap::Base *m() const { Heap::Base *b; quint32 v = value(); memcpy(&b, &v, 4); return b; } + Q_ALWAYS_INLINE void setM(Heap::Base *b) { quint32 v; memcpy(&v, &b, 4); setValue(v); } +#endif + + Q_ALWAYS_INLINE int int_32() const { int i; quint32 v = value(); memcpy(&i, &v, 4); return i; } + Q_ALWAYS_INLINE void setInt_32(int i) { quint32 u; memcpy(&u, &i, 4); setValue(u); } + Q_ALWAYS_INLINE uint uint_32() const { return value(); } #ifndef QV4_USE_64_BIT_VALUE_ENCODING enum Masks { @@ -220,127 +185,127 @@ struct Q_QML_PRIVATE_EXPORT Value #endif inline unsigned type() const { - return tag & Type_Mask; + return tag() & Type_Mask; } // used internally in property - inline bool isEmpty() const { return tag == Empty_Type; } + inline bool isEmpty() const { return tag() == Empty_Type; } - inline bool isUndefined() const { return tag == Undefined_Type; } - inline bool isNull() const { return tag == _Null_Type; } - inline bool isBoolean() const { return tag == _Boolean_Type; } + inline bool isUndefined() const { return tag() == Undefined_Type; } + inline bool isNull() const { return tag() == _Null_Type; } + inline bool isBoolean() const { return tag ()== _Boolean_Type; } #ifdef QV4_USE_64_BIT_VALUE_ENCODING - inline bool isInteger() const { return (val >> IsNumber_Shift) == 1; } - inline bool isDouble() const { return (val >> IsDouble_Shift); } - inline bool isNumber() const { return (val >> IsNumber_Shift); } - inline bool isManaged() const { return !(val >> IsManaged_Shift); } - inline bool isNullOrUndefined() const { return ((val >> IsManaged_Shift) & ~2) == 1; } - inline bool integerCompatible() const { return ((val >> IsConvertibleToInt_Shift) & ~2) == 1; } + inline bool isInteger() const { return (_val >> IsNumber_Shift) == 1; } + inline bool isDouble() const { return (_val >> IsDouble_Shift); } + inline bool isNumber() const { return (_val >> IsNumber_Shift); } + inline bool isManaged() const { return !(_val >> IsManaged_Shift); } + inline bool isNullOrUndefined() const { return ((_val >> IsManaged_Shift) & ~2) == 1; } + inline bool integerCompatible() const { return ((_val >> IsConvertibleToInt_Shift) & ~2) == 1; } static inline bool integerCompatible(Value a, Value b) { return a.integerCompatible() && b.integerCompatible(); } static inline bool bothDouble(Value a, Value b) { return a.isDouble() && b.isDouble(); } - double doubleValue() const { - Q_ASSERT(isDouble()); - union { - quint64 i; - double d; - } v; - v.i = val ^ NaNEncodeMask; - return v.d; - } - void setDouble(double d) { - union { - quint64 i; - double d; - } v; - v.d = d; - val = v.i ^ NaNEncodeMask; - Q_ASSERT(isDouble()); - } - inline bool isNaN() const { return (tag & 0x7fff8000) == 0x00078000; } + inline bool isNaN() const { return (tag() & 0x7fff8000) == 0x00078000; } #else - inline bool isInteger() const { return tag == _Integer_Type; } - inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } - inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } - inline bool isManaged() const { return tag == Managed_Type; } - inline bool isNullOrUndefined() const { return (tag & IsNullOrUndefined_Mask) == Undefined_Type; } - inline bool integerCompatible() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } + inline bool isInteger() const { return tag() == _Integer_Type; } + inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; } + inline bool isNumber() const { return tag() == _Integer_Type || (tag() & NotDouble_Mask) != NotDouble_Mask; } + inline bool isManaged() const { return tag() == Managed_Type; } + inline bool isNullOrUndefined() const { return (tag() & IsNullOrUndefined_Mask) == Undefined_Type; } + inline bool integerCompatible() const { return (tag() & ConvertibleToInt) == ConvertibleToInt; } static inline bool integerCompatible(Value a, Value b) { - return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; + return ((a.tag() & b.tag()) & ConvertibleToInt) == ConvertibleToInt; } static inline bool bothDouble(Value a, Value b) { - return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; + return ((a.tag() | b.tag()) & NotDouble_Mask) != NotDouble_Mask; } - double doubleValue() const { Q_ASSERT(isDouble()); return dbl; } - void setDouble(double d) { dbl = d; Q_ASSERT(isDouble()); } - inline bool isNaN() const { return (tag & QV4::Value::NotDouble_Mask) == QV4::Value::NaN_Mask; } + inline bool isNaN() const { return (tag() & QV4::Value::NotDouble_Mask) == QV4::Value::NaN_Mask; } #endif + Q_ALWAYS_INLINE double doubleValue() const { + Q_ASSERT(isDouble()); + double d; + quint64 v = _val; +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + v ^= NaNEncodeMask; +#endif + memcpy(&d, &v, 8); + return d; + } + Q_ALWAYS_INLINE void setDouble(double d) { + memcpy(&_val, &d, 8); +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + _val ^= NaNEncodeMask; +#endif + Q_ASSERT(isDouble()); + } inline bool isString() const; inline bool isObject() const; inline bool isInt32() { - if (tag == _Integer_Type) + if (tag() == _Integer_Type) return true; if (isDouble()) { double d = doubleValue(); int i = (int)d; if (i == d) { - int_32 = i; - tag = _Integer_Type; + setInt_32(i); + setTag(_Integer_Type); return true; } } return false; } double asDouble() const { - if (tag == _Integer_Type) - return int_32; + if (tag() == _Integer_Type) + return int_32(); return doubleValue(); } bool booleanValue() const { - return int_32; + return int_32(); } int integerValue() const { - return int_32; + return int_32(); } - String *stringValue() const { - return m ? reinterpret_cast<String*>(const_cast<Value *>(this)) : 0; + Q_ALWAYS_INLINE String *stringValue() const { + return m() ? reinterpret_cast<String*>(const_cast<Value *>(this)) : 0; } - Object *objectValue() const { - return m ? reinterpret_cast<Object*>(const_cast<Value *>(this)) : 0; + Q_ALWAYS_INLINE Object *objectValue() const { + return m() ? reinterpret_cast<Object*>(const_cast<Value *>(this)) : 0; } - Managed *managed() const { - return m ? reinterpret_cast<Managed*>(const_cast<Value *>(this)) : 0; + Q_ALWAYS_INLINE Managed *managed() const { + return m() ? reinterpret_cast<Managed*>(const_cast<Value *>(this)) : 0; } - Heap::Base *heapObject() const { - return m; + Q_ALWAYS_INLINE Heap::Base *heapObject() const { + return m(); } - quint64 rawValue() const { - return val; + Q_ALWAYS_INLINE quint64 &rawValueRef() { + return _val; + } + Q_ALWAYS_INLINE quint64 rawValue() const { + return _val; } + Q_ALWAYS_INLINE void setRawValue(quint64 raw) { _val = raw; } static inline Value fromHeapObject(Heap::Base *m) { Value v; - v.m = m; + v.setRawValue(0); + v.setM(m); #ifndef QV4_USE_64_BIT_VALUE_ENCODING - v.tag = Managed_Type; + v.setTag(Managed_Type); #endif return v; } - static inline Value fromManaged(Managed *m); - int toUInt16() const; inline int toInt32() const; inline unsigned int toUInt32() const; - inline bool toBoolean() const; + bool toBoolean() const; double toInteger() const; inline double toNumber() const; double toNumberImpl() const; @@ -353,21 +318,32 @@ struct Q_QML_PRIVATE_EXPORT Value inline bool tryIntegerConversion() { bool b = integerCompatible(); if (b) - tag = _Integer_Type; + setTag(_Integer_Type); return b; } - inline String *asString() const; - inline Managed *asManaged() const; - inline Object *asObject() const; - inline FunctionObject *asFunctionObject() const; - inline NumberObject *asNumberObject() const; - inline StringObject *asStringObject() const; - inline DateObject *asDateObject() const; - inline ArrayObject *asArrayObject() const; - inline ErrorObject *asErrorObject() const; + template <typename T> + const T *as() const { + if (!m() || !isManaged()) + return 0; + + Q_ASSERT(m()->vtable()); +#if !defined(QT_NO_QOBJECT_CHECK) + static_cast<const T *>(this)->qt_check_for_QMANAGED_macro(static_cast<const T *>(this)); +#endif + const VTable *vt = m()->vtable(); + while (vt) { + if (vt == T::staticVTable()) + return static_cast<const T *>(this); + vt = vt->parent; + } + return 0; + } + template <typename T> + T *as() { + return const_cast<T *>(const_cast<const Value *>(this)->as<T>()); + } - template<typename T> inline T *as() const; template<typename T> inline T *cast() { return static_cast<T *>(managed()); } @@ -376,26 +352,32 @@ struct Q_QML_PRIVATE_EXPORT Value } inline uint asArrayIndex() const; - inline uint asArrayLength(bool *ok) const; +#ifndef V4_BOOTSTRAP + uint asArrayLength(bool *ok) const; +#endif - ReturnedValue asReturnedValue() const { return val; } - static Value fromReturnedValue(ReturnedValue val) { Value v; v.val = val; return v; } + ReturnedValue asReturnedValue() const { return _val; } + static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; } // Section 9.12 bool sameValue(Value other) const; - inline void mark(ExecutionEngine *e) const; + inline void mark(ExecutionEngine *e); Value &operator =(const ScopedValue &v); - Value &operator=(ReturnedValue v) { val = v; return *this; } + Value &operator=(ReturnedValue v) { _val = v; return *this; } Value &operator=(Managed *m) { - val = Value::fromManaged(m).val; + if (!m) { + setTagValue(Undefined_Type, 0); + } else { + _val = reinterpret_cast<Value *>(m)->_val; + } return *this; } Value &operator=(Heap::Base *o) { - m = o; + setM(o); #ifndef QV4_USE_64_BIT_VALUE_ENCODING - tag = Managed_Type; + setTag(Managed_Type); #endif return *this; } @@ -403,17 +385,68 @@ struct Q_QML_PRIVATE_EXPORT Value template<typename T> Value &operator=(const Scoped<T> &t); Value &operator=(const Value &v) { - val = v.val; + _val = v._val; return *this; } }; -inline Managed *Value::asManaged() const +inline bool Value::isString() const +{ + if (!isManaged()) + return false; + return m() && m()->vtable()->isString; +} +inline bool Value::isObject() const +{ + if (!isManaged()) + return false; + return m() && m()->vtable()->isObject; +} + +inline bool Value::isPrimitive() const +{ + return !isObject(); +} + +inline double Value::toNumber() const +{ + if (isInteger()) + return int_32(); + if (isDouble()) + return doubleValue(); + return toNumberImpl(); +} + + +#ifndef V4_BOOTSTRAP +inline uint Value::asArrayIndex() const { - if (isManaged()) - return managed(); - return 0; +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + if (!isNumber()) + return UINT_MAX; + if (isInteger()) + return int_32() >= 0 ? (uint)int_32() : UINT_MAX; +#else + if (isInteger() && int_32() >= 0) + return (uint)int_32(); + if (!isDouble()) + return UINT_MAX; +#endif + double d = doubleValue(); + uint idx = (uint)d; + if (idx != d) + return UINT_MAX; + return idx; } +#endif + +inline +ReturnedValue Heap::Base::asReturnedValue() const +{ + return Value::fromHeapObject(const_cast<Heap::Base *>(this)).asReturnedValue(); +} + + struct Q_QML_PRIVATE_EXPORT Primitive : public Value { @@ -437,10 +470,11 @@ inline Primitive Primitive::undefinedValue() { Primitive v; #ifdef QV4_USE_64_BIT_VALUE_ENCODING - v.val = quint64(Undefined_Type) << Tag_Shift; + v.setRawValue(quint64(Undefined_Type) << Tag_Shift); #else - v.tag = Undefined_Type; - v.int_32 = 0; + v.setRawValue(0); + v.setTag(Undefined_Type); + v.setValue(0); #endif return v; } @@ -448,39 +482,54 @@ inline Primitive Primitive::undefinedValue() inline Primitive Primitive::emptyValue() { Primitive v; - v.tag = Value::Empty_Type; - v.uint_32 = 0; + v.setTagValue(Value::Empty_Type, 0); return v; } -template <typename T> -struct TypedValue : public Value +inline Primitive Primitive::nullValue() { - template<typename X> - TypedValue &operator =(X *x) { - m = x; + Primitive v; #ifndef QV4_USE_64_BIT_VALUE_ENCODING - tag = Managed_Type; + v.setRawValue(quint64(_Null_Type) << Tag_Shift); +#else + v.setTagValue(_Null_Type, 0); #endif - return *this; - } - TypedValue &operator =(T *t); - TypedValue &operator =(const Scoped<T> &v); -// TypedValue &operator =(const ManagedRef<T> &v); - - TypedValue &operator =(const TypedValue<T> &t); + return v; +} - bool operator!() const { return !managed(); } +inline Primitive Primitive::fromBoolean(bool b) +{ + Primitive v; + v.setTagValue(_Boolean_Type, b); + return v; +} - operator T *() { return static_cast<T *>(managed()); } - T *operator->() { return static_cast<T *>(managed()); } - const T *operator->() const { return static_cast<T *>(managed()); } - T *getPointer() const { return static_cast<T *>(managed()); } +inline Primitive Primitive::fromDouble(double d) +{ + Primitive v; + v.setDouble(d); + return v; +} - void mark(ExecutionEngine *e) { if (managed()) managed()->mark(e); } -}; -typedef TypedValue<String> StringValue; +inline Primitive Primitive::fromInt32(int i) +{ + Primitive v; + v.setTagValue(_Integer_Type, 0); // For mingw482, because it complains, and for VS9, because of internal compiler errors. + v.setInt_32(i); + return v; +} +inline Primitive Primitive::fromUInt32(uint i) +{ + Primitive v; + if (i < INT_MAX) { + v.setTagValue(_Integer_Type, 0); // For mingw482, because it complains, and for VS9, because of internal compiler errors. + v.setInt_32((int)i); + } else { + v.setDouble(i); + } + return v; +} struct Encode { static ReturnedValue undefined() { @@ -496,7 +545,7 @@ struct Encode { Encode(double d) { Value v; v.setDouble(d); - val = v.val; + val = v.rawValue(); } Encode(int i) { val = (quint64(Value::_Integer_Type) << Value::Tag_Shift) | (uint)i; @@ -507,7 +556,7 @@ struct Encode { } else { Value v; v.setDouble(i); - val = v.val; + val = v.rawValue(); } } Encode(ReturnedValue v) { @@ -527,21 +576,29 @@ private: Encode(void *); }; -inline -ReturnedValue Heap::Base::asReturnedValue() const +template<typename T> +ReturnedValue value_convert(ExecutionEngine *e, const Value &v); + +inline int Value::toInt32() const { - return Value::fromHeapObject(const_cast<Heap::Base *>(this)).asReturnedValue(); -} + if (isInteger()) + return int_32(); + double d = isNumber() ? doubleValue() : toNumberImpl(); + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; -template<typename T> -T *value_cast(const Value &v) + if ((d >= -D31 && d < D31)) + return static_cast<int>(d); + + return Primitive::toInt32(d); +} + +inline unsigned int Value::toUInt32() const { - return v.as<T>(); + return (unsigned int)toInt32(); } -template<typename T> -ReturnedValue value_convert(ExecutionEngine *e, const Value &v); } diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index 966f83acef..f9e26efe71 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -44,7 +44,7 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(VariantObject); Heap::VariantObject::VariantObject(QV4::ExecutionEngine *engine, const QVariant &value) - : Heap::Object(engine->emptyClass, engine->variantPrototype.asObject()) + : Heap::Object(engine->emptyClass, engine->variantPrototype()) { data = value; if (isScarce()) @@ -96,8 +96,8 @@ void VariantPrototype::init() { defineDefaultProperty(QStringLiteral("preserve"), method_preserve, 0); defineDefaultProperty(QStringLiteral("destroy"), method_destroy, 0); - defineDefaultProperty(engine()->id_valueOf, method_valueOf, 0); - defineDefaultProperty(engine()->id_toString, method_toString, 0); + defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0); + defineDefaultProperty(engine()->id_toString(), method_toString, 0); } QV4::ReturnedValue VariantPrototype::method_preserve(CallContext *ctx) @@ -129,7 +129,7 @@ QV4::ReturnedValue VariantPrototype::method_toString(CallContext *ctx) return Encode::undefined(); QString result = o->d()->data.toString(); if (result.isEmpty() && !o->d()->data.canConvert(QVariant::String)) - result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(o->d()->data.typeName())); + result = QStringLiteral("QVariant(%0)").arg(QString::fromLatin1(o->d()->data.typeName())); return Encode(ctx->d()->engine->newString(result)); } diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index 2b48412c4d..b19f12bb98 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -49,7 +49,7 @@ #include <QtQml/qqmllist.h> #include <QtCore/qvariant.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 390bbf5ee3..0edd7378a7 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -37,13 +37,14 @@ #include <QtCore/qjsondocument.h> #include <QtCore/qjsonobject.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4debugging_p.h> #include <private/qv4function_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4math_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qv4lookup_p.h> +#include <private/qv4string_p.h> #include <iostream> #include "qv4alloca_p.h" @@ -512,6 +513,28 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code STOREVALUE(instr.result, Runtime::getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); MOTH_END_INSTR(LoadQObjectProperty) + MOTH_BEGIN_INSTR(StoreScopeObjectProperty) + Runtime::setQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + CHECK_EXCEPTION; + MOTH_END_INSTR(StoreScopeObjectProperty) + + MOTH_BEGIN_INSTR(LoadScopeObjectProperty) + STOREVALUE(instr.result, Runtime::getQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex)); + MOTH_END_INSTR(LoadScopeObjectProperty) + + MOTH_BEGIN_INSTR(StoreContextObjectProperty) + Runtime::setQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + CHECK_EXCEPTION; + MOTH_END_INSTR(StoreContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadContextObjectProperty) + STOREVALUE(instr.result, Runtime::getQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex)); + MOTH_END_INSTR(LoadContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadIdObject) + STOREVALUE(instr.result, Runtime::getQmlIdObject(engine, VALUE(instr.base), instr.index)); + MOTH_END_INSTR(LoadIdObject) + MOTH_BEGIN_INSTR(LoadAttachedQObjectProperty) STOREVALUE(instr.result, Runtime::getQmlAttachedProperty(engine, instr.attachedPropertiesId, instr.propertyIndex)); MOTH_END_INSTR(LoadAttachedQObjectProperty) @@ -565,6 +588,26 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code STOREVALUE(instr.result, Runtime::callPropertyLookup(engine, instr.lookupIndex, callData)); MOTH_END_INSTR(CallPropertyLookup) + MOTH_BEGIN_INSTR(CallScopeObjectProperty) + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); + callData->tag = QV4::Value::Integer_Type; + callData->argc = instr.argc; + callData->thisObject = VALUE(instr.base); + STOREVALUE(instr.result, Runtime::callQmlScopeObjectProperty(engine, instr.index, callData)); + MOTH_END_INSTR(CallScopeObjectProperty) + + MOTH_BEGIN_INSTR(CallContextObjectProperty) + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); + callData->tag = QV4::Value::Integer_Type; + callData->argc = instr.argc; + callData->thisObject = VALUE(instr.base); + STOREVALUE(instr.result, Runtime::callQmlContextObjectProperty(engine, instr.index, callData)); + MOTH_END_INSTR(CallContextObjectProperty) + MOTH_BEGIN_INSTR(CallElement) Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); @@ -858,28 +901,20 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code engine->currentContext()->lineNumber = instr.lineNumber; if (qt_v4IsDebugging) qt_v4CheckForBreak(context, scopes, scopeDepth); - MOTH_END_INSTR(Debug) + MOTH_END_INSTR(Line) MOTH_BEGIN_INSTR(LoadThis) VALUE(instr.result) = context->thisObject(); MOTH_END_INSTR(LoadThis) - MOTH_BEGIN_INSTR(LoadQmlIdArray) - VALUE(instr.result) = Runtime::getQmlIdArray(static_cast<QV4::NoThrowEngine*>(engine)); - MOTH_END_INSTR(LoadQmlIdArray) + MOTH_BEGIN_INSTR(LoadQmlContext) + VALUE(instr.result) = Runtime::getQmlContext(static_cast<QV4::NoThrowEngine*>(engine)); + MOTH_END_INSTR(LoadQmlContext) MOTH_BEGIN_INSTR(LoadQmlImportedScripts) VALUE(instr.result) = Runtime::getQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlImportedScripts) - MOTH_BEGIN_INSTR(LoadQmlContextObject) - VALUE(instr.result) = Runtime::getQmlContextObject(static_cast<QV4::NoThrowEngine*>(engine)); - MOTH_END_INSTR(LoadContextObject) - - MOTH_BEGIN_INSTR(LoadQmlScopeObject) - VALUE(instr.result) = Runtime::getQmlScopeObject(static_cast<QV4::NoThrowEngine*>(engine)); - MOTH_END_INSTR(LoadScopeObject) - MOTH_BEGIN_INSTR(LoadQmlSingleton) VALUE(instr.result) = Runtime::getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name); MOTH_END_INSTR(LoadQmlSingleton) diff --git a/src/qml/memory/memory.pri b/src/qml/memory/memory.pri new file mode 100644 index 0000000000..04b7566ccc --- /dev/null +++ b/src/qml/memory/memory.pri @@ -0,0 +1,14 @@ +INCLUDEPATH += $$PWD +INCLUDEPATH += $$OUT_PWD + +!qmldevtools_build { +SOURCES += \ + $$PWD/qv4mm.cpp \ + +HEADERS += \ + $$PWD/qv4mm_p.h + +} + +HEADERS += \ + $$PWD/qv4heap_p.h diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h new file mode 100644 index 0000000000..a93e4191b0 --- /dev/null +++ b/src/qml/memory/qv4heap_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4HEAP_P_H +#define QV4HEAP_P_H + +#include <QtCore/QString> +#include <private/qv4global_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct VTable +{ + const VTable * const parent; + uint isExecutionContext : 1; + uint isString : 1; + uint isObject : 1; + uint isFunctionObject : 1; + uint isErrorObject : 1; + uint isArrayData : 1; + uint unused : 18; + uint type : 8; + const char *className; + void (*destroy)(Heap::Base *); + void (*markObjects)(Heap::Base *, ExecutionEngine *e); + bool (*isEqualTo)(Managed *m, Managed *other); +}; + +namespace Heap { + +struct Q_QML_EXPORT Base { + quintptr mm_data; // vtable and markbit + + inline ReturnedValue asReturnedValue() const; + inline void mark(QV4::ExecutionEngine *engine); + + enum { + MarkBit = 0x1, + NotInUse = 0x2, + PointerMask = ~0x3 + }; + + void setVtable(const VTable *v) { + Q_ASSERT(!(mm_data & MarkBit)); + mm_data = reinterpret_cast<quintptr>(v); + } + VTable *vtable() const { + return reinterpret_cast<VTable *>(mm_data & PointerMask); + } + inline bool isMarked() const { + return mm_data & MarkBit; + } + inline void setMarkBit() { + mm_data |= MarkBit; + } + inline void clearMarkBit() { + mm_data &= ~MarkBit; + } + + inline bool inUse() const { + return !(mm_data & NotInUse); + } + + Base *nextFree() { + return reinterpret_cast<Base *>(mm_data & PointerMask); + } + void setNextFree(Base *m) { + mm_data = (reinterpret_cast<quintptr>(m) | NotInUse); + } + + void *operator new(size_t, Managed *m) { return m; } + void *operator new(size_t, Heap::Base *m) { return m; } + void operator delete(void *, Heap::Base *) {} +}; + +template <typename T> +struct Pointer { + Pointer() {} + Pointer(T *t) : ptr(t) {} + + T *operator->() const { return ptr; } + operator T *() const { return ptr; } + + Pointer &operator =(T *t) { ptr = t; return *this; } + + template <typename Type> + Type *cast() { return static_cast<Type *>(ptr); } + + T *ptr; +}; + +} + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 64491e1376..38b4e1eaf1 100644 --- a/src/qml/jsruntime/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -111,8 +111,6 @@ struct MemoryManager::Data LargeItem *largeItems; std::size_t totalLargeItemsAllocated; - GCDeletable *deletable; - // statistics: #ifdef DETAILED_MM_STATS QVector<unsigned> allocSizeCounters; @@ -129,7 +127,6 @@ struct MemoryManager::Data , unmanagedHeapSizeGCLimit(64 * 1024) , largeItems(0) , totalLargeItemsAllocated(0) - , deletable(0) { memset(nonFullChunks, 0, sizeof(nonFullChunks)); memset(nChunks, 0, sizeof(nChunks)); @@ -189,15 +186,15 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec #ifdef V4_USE_VALGRIND VALGRIND_ENABLE_ERROR_REPORTING; #endif - if (std::size_t(header->itemSize) == MemoryManager::align(sizeof(Heap::String)) && m->gcGetVtable()->isString) { + if (std::size_t(header->itemSize) == MemoryManager::align(sizeof(Heap::String)) && m->vtable()->isString) { std::size_t heapBytes = static_cast<Heap::String *>(m)->retainedTextSize(); Q_ASSERT(*unmanagedHeapSize >= heapBytes); // qDebug() << "-- it's a string holding on to" << heapBytes << "bytes"; *unmanagedHeapSize -= heapBytes; } - if (m->gcGetVtable()->destroy) - m->gcGetVtable()->destroy(m); + if (m->vtable()->destroy) + m->vtable()->destroy(m); memset(m, 0, header->itemSize); #ifdef V4_USE_VALGRIND @@ -307,7 +304,6 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize Q_V4_PROFILE_ALLOC(m_d->engine, allocSize, Profiling::HeapPage), OSAllocator::JSGCHeapPages); m_d->heapChunks.append(allocation); - std::sort(m_d->heapChunks.begin(), m_d->heapChunks.end()); header = reinterpret_cast<Data::ChunkHeader *>(allocation.base()); header->itemSize = int(size); @@ -353,8 +349,8 @@ static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) { while (engine->jsStackTop > markBase) { Heap::Base *h = engine->popForGC(); - Q_ASSERT (h->gcGetVtable()->markObjects); - h->gcGetVtable()->markObjects(h, engine); + Q_ASSERT (h->vtable()->markObjects); + h->vtable()->markObjects(h, engine); } } @@ -377,11 +373,9 @@ void MemoryManager::mark() for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { if (!(*it).isManaged()) continue; - if ((*it).managed()->d()->gcGetVtable() != QObjectWrapper::staticVTable()) + if ((*it).managed()->d()->vtable() != QObjectWrapper::staticVTable()) continue; QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper*>((*it).managed()); - if (!qobjectWrapper) - continue; QObject *qobject = qobjectWrapper->object(); if (!qobject) continue; @@ -408,13 +402,20 @@ void MemoryManager::mark() void MemoryManager::sweep(bool lastSweep) { - if (m_weakValues) { - for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { - if (Managed *m = (*it).asManaged()) { - if (!m->markBit()) - (*it) = Primitive::undefinedValue(); - } + for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { + if (!(*it).isManaged()) + continue; + Managed *m = (*it).as<Managed>(); + if (m->markBit()) + continue; + // we need to call detroyObject on qobjectwrappers now, so that they can emit the destroyed + // signal before we start sweeping the heap + if ((*it).managed()->d()->vtable() == QObjectWrapper::staticVTable()) { + QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper*>((*it).managed()); + qobjectWrapper->destroyObject(lastSweep); } + + (*it) = Primitive::undefinedValue(); } if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = m_d->engine->m_multiplyWrappedQObjects) { @@ -473,8 +474,8 @@ void MemoryManager::sweep(bool lastSweep) i = i->next; continue; } - if (m->gcGetVtable()->destroy) - m->gcGetVtable()->destroy(m); + if (m->vtable()->destroy) + m->vtable()->destroy(m); *last = i->next; free(Q_V4_PROFILE_DEALLOC(m_d->engine, i, i->size + sizeof(Data::LargeItem), @@ -482,15 +483,6 @@ void MemoryManager::sweep(bool lastSweep) i = *last; } - GCDeletable *deletable = m_d->deletable; - m_d->deletable = 0; - while (deletable) { - GCDeletable *next = deletable->next; - deletable->lastCall = lastSweep; - delete deletable; - deletable = next; - } - // some execution contexts are allocated on the stack, make sure we clear their markBit as well if (!lastSweep) { Heap::ExecutionContext *ctx = engine()->current; @@ -554,7 +546,7 @@ void MemoryManager::runGC() size_t MemoryManager::getUsedMem() const { size_t usedMem = 0; - for (QVector<PageAllocation>::const_iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) { + for (QVector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) { Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(i->base()); for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) { Heap::Base *m = reinterpret_cast<Heap::Base *>(item); @@ -590,10 +582,10 @@ void MemoryManager::growUnmanagedHeapSizeUsage(size_t delta) MemoryManager::~MemoryManager() { delete m_persistentValues; - delete m_weakValues; - m_weakValues = 0; sweep(/*lastSweep*/true); + + delete m_weakValues; #ifdef V4_USE_VALGRIND VALGRIND_DESTROY_MEMPOOL(this); #endif @@ -618,12 +610,6 @@ void MemoryManager::dumpStats() const #endif // DETAILED_MM_STATS } -void MemoryManager::registerDeletable(GCDeletable *d) -{ - d->next = m_d->deletable; - m_d->deletable = d; -} - #ifdef DETAILED_MM_STATS void MemoryManager::willAllocate(std::size_t size) { @@ -641,7 +627,7 @@ void MemoryManager::collectFromJSStack() const Value *v = m_d->engine->jsStackBase; Value *top = m_d->engine->jsStackTop; while (v < top) { - Managed *m = v->asManaged(); + Managed *m = v->as<Managed>(); if (m && m->inUse()) // Skip pointers to already freed objects, they are bogus as well m->mark(m_d->engine); diff --git a/src/qml/jsruntime/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 7f6d2edba3..c01866ff11 100644 --- a/src/qml/jsruntime/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -34,9 +34,9 @@ #ifndef QV4GC_H #define QV4GC_H -#include "qv4global_p.h" -#include "qv4value_inl_p.h" -#include "qv4scopedvalue_p.h" +#include <private/qv4global_p.h> +#include <private/qv4value_p.h> +#include <private/qv4scopedvalue_p.h> //#define DETAILED_MM_STATS @@ -87,7 +87,7 @@ public: { size = align(size); Heap::Base *o = allocData(size, unmanagedSize); - o->vtable = ManagedType::staticVTable(); + o->setVtable(ManagedType::staticVTable()); return static_cast<typename ManagedType::Data *>(o); } @@ -162,8 +162,6 @@ public: void dumpStats() const; - void registerDeletable(GCDeletable *d); - size_t getUsedMem() const; size_t getAllocatedMem() const; size_t getLargeItemsMem() const; diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/parser/qqmljsmemorypool_p.h index 16927251c7..c7ac5919bd 100644 --- a/src/qml/parser/qqmljsmemorypool_p.h +++ b/src/qml/parser/qqmljsmemorypool_p.h @@ -104,28 +104,6 @@ public: template <typename _Tp> _Tp *New() { return new (this->allocate(sizeof(_Tp))) _Tp(); } - template <typename PoolContentType, typename Visitor> - void visitManagedPool(Visitor &visitor) - { - for (int i = 0; i <= _blockCount; ++i) { - char *p = _blocks[i]; - char *end = p + BLOCK_SIZE; - if (i == _blockCount) { - Q_ASSERT(_ptr <= end); - end = _ptr; - } - - Q_ASSERT(p <= end); - - const qptrdiff increment = (sizeof(PoolContentType) + 7) & ~7; - - while (p + increment <= end) { - visitor(reinterpret_cast<PoolContentType*>(p)); - p += increment; - } - } - } - private: void *allocate_helper(size_t size) { diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 6e06fb42ef..d75262bf0b 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -8,6 +8,9 @@ win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS win32:!wince*:!winrt:LIBS += -lshell32 solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 +# Ensure this gcc optimization is switched off for mips platforms to avoid trouble with JIT. +gcc:isEqual(QT_ARCH, "mips"): QMAKE_CXXFLAGS += -fno-reorder-blocks + MODULE_PLUGIN_TYPES = \ qmltooling @@ -33,6 +36,7 @@ HEADERS += qtqmlglobal.h \ #modules include(util/util.pri) +include(memory/memory.pri) include(parser/parser.pri) include(compiler/compiler.pri) include(jsapi/jsapi.pri) diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri index 4b109107f9..51697b0aff 100644 --- a/src/qml/qml/ftw/ftw.pri +++ b/src/qml/qml/ftw/ftw.pri @@ -4,7 +4,6 @@ HEADERS += \ $$PWD/qpodvector_p.h \ $$PWD/qhashedstring_p.h \ $$PWD/qqmlrefcount_p.h \ - $$PWD/qqmlpool_p.h \ $$PWD/qfieldlist_p.h \ $$PWD/qhashfield_p.h \ $$PWD/qqmlthread_p.h \ @@ -20,7 +19,6 @@ HEADERS += \ SOURCES += \ $$PWD/qintrusivelist.cpp \ $$PWD/qhashedstring.cpp \ - $$PWD/qqmlpool.cpp \ $$PWD/qqmlthread.cpp \ # mirrors logic in $$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri diff --git a/src/qml/qml/ftw/qqmlpool_p.h b/src/qml/qml/ftw/qqmlpool_p.h deleted file mode 100644 index 4956cd81d8..0000000000 --- a/src/qml/qml/ftw/qqmlpool_p.h +++ /dev/null @@ -1,270 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLPOOL_P_H -#define QQMLPOOL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4global_p.h> -#include <QtCore/qstring.h> -#include <QtCore/qurl.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlPool -{ -public: - // The class has a destructor that needs to be called - class Class { - public: - inline QQmlPool *pool() const; - - private: - void *operator new(size_t); - void *operator new(size_t, void *m) { return m; } - friend class QQmlPool; - - QQmlPool *_pool; - Class *_next; - void (*_destroy)(Class *); - }; - - // The class is plain old data and no destructor needs to - // be called - class POD { - public: - inline QQmlPool *pool() const; - - private: - void *operator new(size_t); - void *operator new(size_t, void *m) { return m; } - friend class QQmlPool; - - QQmlPool *_pool; - }; - - inline QQmlPool(); - inline ~QQmlPool(); - - void clear(); - - template<typename T> - inline T *New(); - template<typename T> - inline T *NewRaw(); - template<typename T> - inline T *NewRawArray(int length); - - inline QString *NewString(const QString &); - inline QByteArray *NewByteArray(const QByteArray &); - inline QUrl *NewUrl(const QUrl &); - - template<typename T> - struct List { - List() : m_length(0), m_data(0) {} - List(const List &o) : m_length(o.m_length), m_data(o.m_data) {} - List &operator=(const List &o) { - m_length = o.m_length; - m_data = o.m_data; - return *this; - } - - int count() const { - return m_length; - } - int length() const { - return m_length; - } - const T &at(int index) const { - Q_ASSERT(index < m_length); - return m_data[index]; - }; - T &operator[](int index) { - Q_ASSERT(index < m_length); - return m_data[index]; - }; - const T *data() const { return m_data; } - private: - friend class QQmlPool; - List(T *d, int l) : m_length(l), m_data(d) {} - int m_length; - T *m_data; - }; - - template<typename T> - inline List<T> NewRawList(int length); - -private: - struct StringClass : public QString, public Class { - }; - struct ByteArrayClass : public QByteArray, public Class { - }; - struct UrlClass : public QUrl, public Class { - }; - - inline void *allocate(int size); - void newpage(); - - template<typename T> - inline void initialize(POD *); - template<typename T> - inline void initialize(Class *); - template<typename T> - static void destroy(Class *c); - - struct Page { - struct Header { - Page *next; - char *free; - } header; - - static const int pageSize = 4 * 4096 - sizeof(Header); - - char memory[pageSize]; - }; - - Page *_page; - Class *_classList; -}; - -QQmlPool::QQmlPool() -: _page(0), _classList(0) -{ -} - -QQmlPool::~QQmlPool() -{ - clear(); -} - -template<typename T> -T *QQmlPool::New() -{ - T *rv = new (allocate(sizeof(T))) T; - initialize<T>(rv); - rv->_pool = this; - return rv; -} - -template<typename T> -T *QQmlPool::NewRaw() -{ - return (T*)allocate(sizeof(T)); -} - -template<typename T> -T *QQmlPool::NewRawArray(int length) -{ - return (T*)allocate(length * sizeof(T)); -} - -template<typename T> -QQmlPool::List<T> QQmlPool::NewRawList(int length) -{ - return List<T>(NewRawArray<T>(length), length); -} - -QString *QQmlPool::NewString(const QString &s) -{ - QString *rv = New<StringClass>(); - *rv = s; - return rv; -} - -QByteArray *QQmlPool::NewByteArray(const QByteArray &s) -{ - QByteArray *rv = New<ByteArrayClass>(); - *rv = s; - return rv; -} - -QUrl *QQmlPool::NewUrl(const QUrl &s) -{ - QUrl *rv = New<UrlClass>(); - *rv = s; - return rv; -} - -void *QQmlPool::allocate(int size) -{ - if (!_page || (_page->header.free + size) > (_page->memory + Page::pageSize)) - newpage(); - - void *rv = _page->header.free; - _page->header.free += size + ((8 - size) & 7); // ensure 8 byte alignment; - return rv; -} - -template<typename T> -void QQmlPool::initialize(QQmlPool::POD *) -{ -} - -template<typename T> -void QQmlPool::initialize(QQmlPool::Class *c) -{ - c->_next = _classList; - c->_destroy = &destroy<T>; - _classList = c; -} - -template<typename T> -void QQmlPool::destroy(Class *c) -{ - static_cast<T *>(c)->~T(); -} - -QQmlPool *QQmlPool::Class::pool() const -{ - return _pool; -} - -QQmlPool *QQmlPool::POD::pool() const -{ - return _pool; -} - -QT_END_NAMESPACE - -#endif // QQMLPOOL_P_H - diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h index 59ed77b580..ba2ca43990 100644 --- a/src/qml/qml/ftw/qqmlrefcount_p.h +++ b/src/qml/qml/ftw/qqmlrefcount_p.h @@ -72,13 +72,16 @@ template<class T> class QQmlRefPointer { public: + enum Mode { + AddRef, + Adopt + }; inline QQmlRefPointer(); - inline QQmlRefPointer(T *); + inline QQmlRefPointer(T *, Mode m = AddRef); inline QQmlRefPointer(const QQmlRefPointer<T> &); inline ~QQmlRefPointer(); inline QQmlRefPointer<T> &operator=(const QQmlRefPointer<T> &o); - inline QQmlRefPointer<T> &operator=(T *); inline bool isNull() const { return !o; } @@ -87,7 +90,7 @@ public: inline operator T*() const { return o; } inline T* data() const { return o; } - inline QQmlRefPointer<T> &take(T *); + inline QQmlRefPointer<T> &adopt(T *); private: T *o; @@ -133,10 +136,11 @@ QQmlRefPointer<T>::QQmlRefPointer() } template<class T> -QQmlRefPointer<T>::QQmlRefPointer(T *o) +QQmlRefPointer<T>::QQmlRefPointer(T *o, Mode m) : o(o) { - if (o) o->addref(); + if (m == AddRef && o) + o->addref(); } template<class T> @@ -161,21 +165,12 @@ QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(const QQmlRefPointer<T> &other) return *this; } -template<class T> -QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(T *other) -{ - if (other) other->addref(); - if (o) o->release(); - o = other; - return *this; -} - /*! Takes ownership of \a other. take() does *not* add a reference, as it assumes ownership of the callers reference of other. */ template<class T> -QQmlRefPointer<T> &QQmlRefPointer<T>::take(T *other) +QQmlRefPointer<T> &QQmlRefPointer<T>::adopt(T *other) { if (o) o->release(); o = other; diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp index 4addcd9e58..62f6f76a7e 100644 --- a/src/qml/qml/ftw/qqmlthread.cpp +++ b/src/qml/qml/ftw/qqmlthread.cpp @@ -377,4 +377,31 @@ void QQmlThread::internalPostMethodToMain(Message *message) d->unlock(); } +void QQmlThread::waitForNextMessage() +{ + Q_ASSERT(!isThisThread()); + d->lock(); + Q_ASSERT(d->m_mainThreadWaiting == false); + + d->m_mainThreadWaiting = true; + + if (d->mainSync || !d->threadList.isEmpty()) { + if (d->mainSync) { + QQmlThread::Message *message = d->mainSync; + unlock(); + message->call(this); + delete message; + lock(); + d->mainSync = 0; + wakeOne(); + } else { + d->wait(); + } + } + + d->m_mainThreadWaiting = false; + d->unlock(); +} + + QT_END_NAMESPACE diff --git a/src/qml/qml/ftw/qqmlthread_p.h b/src/qml/qml/ftw/qqmlthread_p.h index 86d7d2cf19..73658536ac 100644 --- a/src/qml/qml/ftw/qqmlthread_p.h +++ b/src/qml/qml/ftw/qqmlthread_p.h @@ -108,6 +108,8 @@ public: template<typename T, typename T2, class V, class V2, class O> inline void postMethodToMain(void (O::*Member)(V, V2), const T &, const T2 &); + void waitForNextMessage(); + protected: virtual void startupThread(); virtual void shutdownThread(); diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index e733bcec05..4d84cc82ae 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -23,7 +23,6 @@ SOURCES += \ $$PWD/qqmlvaluetype.cpp \ $$PWD/qqmlaccessors.cpp \ $$PWD/qqmlxmlhttprequest.cpp \ - $$PWD/qqmlwatcher.cpp \ $$PWD/qqmlcleanup.cpp \ $$PWD/qqmlpropertycache.cpp \ $$PWD/qqmlnotifier.cpp \ @@ -35,7 +34,6 @@ SOURCES += \ $$PWD/qqmlimport.cpp \ $$PWD/qqmllist.cpp \ $$PWD/qqmllocale.cpp \ - $$PWD/qqmlabstractexpression.cpp \ $$PWD/qqmljavascriptexpression.cpp \ $$PWD/qqmlabstractbinding.cpp \ $$PWD/qqmlvaluetypeproxybinding.cpp \ @@ -92,7 +90,6 @@ HEADERS += \ $$PWD/qqmlvaluetype_p.h \ $$PWD/qqmlaccessors_p.h \ $$PWD/qqmlxmlhttprequest_p.h \ - $$PWD/qqmlwatcher_p.h \ $$PWD/qqmlcleanup_p.h \ $$PWD/qqmlpropertycache_p.h \ $$PWD/qqmlnotifier_p.h \ @@ -107,7 +104,6 @@ HEADERS += \ $$PWD/qqmlscriptstring_p.h \ $$PWD/qqmllocale_p.h \ $$PWD/qqmlcomponentattached_p.h \ - $$PWD/qqmlabstractexpression_p.h \ $$PWD/qqmljavascriptexpression_p.h \ $$PWD/qqmlabstractbinding_p.h \ $$PWD/qqmlvaluetypeproxybinding_p.h \ diff --git a/src/qml/qml/qqmlabstractbinding.cpp b/src/qml/qml/qqmlabstractbinding.cpp index 40c8f451b4..4e0763e95a 100644 --- a/src/qml/qml/qqmlabstractbinding.cpp +++ b/src/qml/qml/qqmlabstractbinding.cpp @@ -39,24 +39,19 @@ QT_BEGIN_NAMESPACE -extern QQmlAbstractBinding::VTable QQmlBinding_vtable; -extern QQmlAbstractBinding::VTable QQmlValueTypeProxyBinding_vtable; - -QQmlAbstractBinding::VTable *QQmlAbstractBinding::vTables[] = { - &QQmlBinding_vtable, - &QQmlValueTypeProxyBinding_vtable -}; - -QQmlAbstractBinding::QQmlAbstractBinding(BindingType bt) - : m_nextBindingPtr(bt) +QQmlAbstractBinding::QQmlAbstractBinding() + : m_targetIndex(-1) { + Q_ASSERT(!isAddedToObject()); } QQmlAbstractBinding::~QQmlAbstractBinding() { - Q_ASSERT(isAddedToObject() == false); - Q_ASSERT(nextBinding() == 0); - Q_ASSERT(*m_mePtr == 0); + Q_ASSERT(!ref); + Q_ASSERT(!isAddedToObject()); + + if (m_nextBinding.data() && !m_nextBinding->ref.deref()) + delete m_nextBinding.data(); } /*! @@ -72,40 +67,45 @@ void QQmlAbstractBinding::addToObject() Q_ASSERT(!nextBinding()); Q_ASSERT(isAddedToObject() == false); - QObject *obj = object(); + QObject *obj = targetObject(); Q_ASSERT(obj); QQmlData *data = QQmlData::get(obj, true); int coreIndex; - if (QQmlPropertyData::decodeValueTypePropertyIndex(propertyIndex(), &coreIndex) != -1) { + if (QQmlPropertyData::decodeValueTypePropertyIndex(targetPropertyIndex(), &coreIndex) != -1) { // Value type // Find the value type proxy (if there is one) QQmlValueTypeProxyBinding *proxy = 0; if (data->hasBindingBit(coreIndex)) { QQmlAbstractBinding *b = data->bindings; - while (b && b->propertyIndex() != coreIndex) + while (b && b->targetPropertyIndex() != coreIndex) b = b->nextBinding(); - Q_ASSERT(b && b->bindingType() == QQmlAbstractBinding::ValueTypeProxy); + Q_ASSERT(b && b->isValueTypeProxy()); proxy = static_cast<QQmlValueTypeProxyBinding *>(b); } if (!proxy) { proxy = new QQmlValueTypeProxyBinding(obj, coreIndex); - Q_ASSERT(proxy->propertyIndex() == coreIndex); - Q_ASSERT(proxy->object() == obj); + Q_ASSERT(proxy->targetPropertyIndex() == coreIndex); + Q_ASSERT(proxy->targetObject() == obj); proxy->addToObject(); } - setNextBinding(proxy->m_bindings); + setNextBinding(proxy->m_bindings.data()); proxy->m_bindings = this; } else { setNextBinding(data->bindings); + if (data->bindings) { + data->bindings->ref.deref(); + Q_ASSERT(data->bindings->ref.refCount > 0); + } data->bindings = this; + ref.ref(); data->setBindingBit(obj, coreIndex); } @@ -118,95 +118,81 @@ Remove the binding from the object. */ void QQmlAbstractBinding::removeFromObject() { - if (isAddedToObject()) { - QObject *obj = object(); - QQmlData *data = QQmlData::get(obj, false); - Q_ASSERT(data); - - int coreIndex; - if (QQmlPropertyData::decodeValueTypePropertyIndex(propertyIndex(), &coreIndex) != -1) { - - // Find the value type binding - QQmlAbstractBinding *vtbinding = data->bindings; - while (vtbinding->propertyIndex() != coreIndex) { - vtbinding = vtbinding->nextBinding(); - Q_ASSERT(vtbinding); - } - Q_ASSERT(vtbinding->bindingType() == QQmlAbstractBinding::ValueTypeProxy); - - QQmlValueTypeProxyBinding *vtproxybinding = - static_cast<QQmlValueTypeProxyBinding *>(vtbinding); - - QQmlAbstractBinding *binding = vtproxybinding->m_bindings; - if (binding == this) { - vtproxybinding->m_bindings = nextBinding(); - } else { - while (binding->nextBinding() != this) { - binding = binding->nextBinding(); - Q_ASSERT(binding); - } - binding->setNextBinding(nextBinding()); - } - - // Value type - we don't remove the proxy from the object. It will sit their happily - // doing nothing until it is removed by a write, a binding change or it is reused - // to hold more sub-bindings. + if (!isAddedToObject()) + return; - } else { + setAddedToObject(false); - if (data->bindings == this) { - data->bindings = nextBinding(); - } else { - QQmlAbstractBinding *binding = data->bindings; - while (binding->nextBinding() != this) { - binding = binding->nextBinding(); - Q_ASSERT(binding); - } - binding->setNextBinding(nextBinding()); - } - - data->clearBindingBit(coreIndex); - } + QObject *obj = targetObject(); + QQmlData *data = QQmlData::get(obj, false); + Q_ASSERT(data); - setNextBinding(0); - setAddedToObject(false); - } -} + QQmlAbstractBinding::Ptr next; + next = nextBinding(); + setNextBinding(0); -void QQmlAbstractBinding::printBindingLoopError(QQmlProperty &prop) -{ - qmlInfo(prop.object()) << QString(QLatin1String("Binding loop detected for property \"%1\"")).arg(prop.name()); -} + int coreIndex; + if (QQmlPropertyData::decodeValueTypePropertyIndex(targetPropertyIndex(), &coreIndex) != -1) { + // Find the value type binding + QQmlAbstractBinding *vtbinding = data->bindings; + while (vtbinding->targetPropertyIndex() != coreIndex) { + vtbinding = vtbinding->nextBinding(); + Q_ASSERT(vtbinding); + } + Q_ASSERT(vtbinding->isValueTypeProxy()); -static void bindingDummyDeleter(QQmlAbstractBinding *) -{ -} + QQmlValueTypeProxyBinding *vtproxybinding = + static_cast<QQmlValueTypeProxyBinding *>(vtbinding); -QQmlAbstractBinding::Pointer QQmlAbstractBinding::weakPointer() -{ - if (m_mePtr.value().isNull()) - m_mePtr.value() = QSharedPointer<QQmlAbstractBinding>(this, bindingDummyDeleter); + QQmlAbstractBinding *binding = vtproxybinding->m_bindings.data(); + if (binding == this) { + vtproxybinding->m_bindings = next; + } else { + while (binding->nextBinding() != this) { + binding = binding->nextBinding(); + Q_ASSERT(binding); + } + binding->setNextBinding(next.data()); + } - return m_mePtr.value().toWeakRef(); -} + // Value type - we don't remove the proxy from the object. It will sit their happily + // doing nothing until it is removed by a write, a binding change or it is reused + // to hold more sub-bindings. + return; + } -void QQmlAbstractBinding::clear() -{ - if (!m_mePtr.isNull()) { - **m_mePtr = 0; - m_mePtr = 0; + if (data->bindings == this) { + if (next.data()) + next->ref.ref(); + data->bindings = next.data(); + if (!ref.deref()) + delete this; + } else { + QQmlAbstractBinding *binding = data->bindings; + while (binding->nextBinding() != this) { + binding = binding->nextBinding(); + Q_ASSERT(binding); + } + binding->setNextBinding(next.data()); } + + data->clearBindingBit(coreIndex); } -void QQmlAbstractBinding::default_retargetBinding(QQmlAbstractBinding *, QObject *, int) +void QQmlAbstractBinding::printBindingLoopError(QQmlProperty &prop) { - qFatal("QQmlAbstractBinding::retargetBinding() called on illegal binding."); + qmlInfo(prop.object()) << QString(QLatin1String("Binding loop detected for property \"%1\"")).arg(prop.name()); } -QString QQmlAbstractBinding::default_expression(const QQmlAbstractBinding *) +QString QQmlAbstractBinding::expression() const { return QLatin1String("<Unknown>"); } +bool QQmlAbstractBinding::isValueTypeProxy() const +{ + return false; +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index b5d8181ca5..dd14301aa9 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -46,6 +46,7 @@ // #include <QtCore/qsharedpointer.h> +#include <QtCore/qshareddata.h> #include <private/qtqmlglobal_p.h> #include <private/qqmlproperty_p.h> #include <private/qpointervaluepair_p.h> @@ -56,162 +57,83 @@ class QQmlObjectCreator; class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding { +protected: + QQmlAbstractBinding(); public: - enum DestroyMode { - - // The binding should disconnect itself upon destroy - DisconnectBinding, - - // The binding doesn't need to disconnect itself, but it can if it wants to. - // - // This is used in QQmlData::destroyed() - at the point at which the bindings are - // destroyed, the notifiers are already disconnected, so no need to disconnect each - // binding again. - // - // Bindings can use this flag to speed up destruction, especially for v4 bindings - // disconnecting a single binding might be slow. - KeepBindingConnected - }; - - struct VTable { - void (*destroy)(QQmlAbstractBinding *, DestroyMode destroyMode); - QString (*expression)(const QQmlAbstractBinding *); - int (*propertyIndex)(const QQmlAbstractBinding *); - QObject *(*object)(const QQmlAbstractBinding *); - void (*setEnabled)(QQmlAbstractBinding *, bool, QQmlPropertyPrivate::WriteFlags); - void (*update)(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); - void (*retargetBinding)(QQmlAbstractBinding *, QObject *, int); - }; - - typedef QWeakPointer<QQmlAbstractBinding> Pointer; + virtual ~QQmlAbstractBinding(); - enum BindingType { Binding = 0, ValueTypeProxy = 1 }; - inline BindingType bindingType() const; + typedef QExplicitlySharedDataPointer<QQmlAbstractBinding> Ptr; - // Destroy the binding. Use this instead of calling delete. - // Bindings are free to implement their own memory management, so the delete operator is - // not necessarily safe. The default implementation clears the binding, removes it from - // the object and calls delete. - void destroy(DestroyMode destroyMode = DisconnectBinding) - { vtable()->destroy(this, destroyMode); } + virtual QString expression() const; - QString expression() const { return vtable()->expression(this); } + virtual bool isValueTypeProxy() const; // Should return the encoded property index for the binding. Should return this value // even if the binding is not enabled or added to an object. // Encoding is: coreIndex | (valueTypeIndex << 16) - int propertyIndex() const { return vtable()->propertyIndex(this); } + int targetPropertyIndex() const { return m_targetIndex; } // Should return the object for the binding. Should return this object even if the // binding is not enabled or added to the object. - QObject *object() const { return vtable()->object(this); } - - void setEnabled(bool e) { setEnabled(e, QQmlPropertyPrivate::DontRemoveBinding); } - void setEnabled(bool e, QQmlPropertyPrivate::WriteFlags f) { vtable()->setEnabled(this, e, f); } + QObject *targetObject() const { return m_target.data(); } - void update() { update(QQmlPropertyPrivate::DontRemoveBinding); } - void update(QQmlPropertyPrivate::WriteFlags f) { vtable()->update(this, f); } + virtual void setEnabled(bool e, QQmlPropertyPrivate::WriteFlags f = QQmlPropertyPrivate::DontRemoveBinding) = 0; void addToObject(); void removeFromObject(); - static inline Pointer getPointer(QQmlAbstractBinding *p); static void printBindingLoopError(QQmlProperty &prop); - // Default implementation for some VTable functions - template<typename T> - static void default_destroy(QQmlAbstractBinding *, DestroyMode); - static QString default_expression(const QQmlAbstractBinding *); - static void default_retargetBinding(QQmlAbstractBinding *, QObject *, int); - -protected: - QQmlAbstractBinding(BindingType); - ~QQmlAbstractBinding(); - void clear(); + inline QQmlAbstractBinding *nextBinding() const; - // Called by QQmlPropertyPrivate to "move" a binding to a different property. - // This is only used for alias properties. The default implementation qFatal()'s - // to ensure that the method is never called for binding types that don't support it. - void retargetBinding(QObject *o, int i) { vtable()->retargetBinding(this, o, i); } -private: - Pointer weakPointer(); + struct RefCount { + RefCount() : refCount(0) {} + int refCount; + void ref() { ++refCount; } + int deref() { return --refCount; } + operator int() const { return refCount; } + }; + RefCount ref; +protected: friend class QQmlData; - friend class QQmlComponentPrivate; friend class QQmlValueTypeProxyBinding; - friend class QQmlPropertyPrivate; - friend class QtSharedPointer::ExternalRefCount<QQmlAbstractBinding>; - friend class QV4Bindings; friend class QQmlObjectCreator; - typedef QSharedPointer<QQmlAbstractBinding> SharedPointer; - // To save memory, we also store the rarely used weakPointer() instance in here - // We also use the flag bits: - // m_mePtr.flag1: added to object - QPointerValuePair<QQmlAbstractBinding*, SharedPointer> m_mePtr; - inline void setAddedToObject(bool v); inline bool isAddedToObject() const; - inline QQmlAbstractBinding *nextBinding() const; inline void setNextBinding(QQmlAbstractBinding *); + int m_targetIndex; + QFlagPointer<QObject> m_target; // Pointer to the next binding in the linked list of bindings. - // Being a pointer, the address is always aligned to at least 4 bytes, which means the last two - // bits of the pointer are free to be used for something else. They are used to store the binding - // type. The binding type serves as an index into the static vTables array, which is used instead - // of a compiler-generated vTable. Instead of virtual functions, pointers to static functions in - // the vTables array are used for dispatching. - // This saves a compiler-generated pointer to a compiler-generated vTable, and thus reduces - // the binding object size by sizeof(void*). - qintptr m_nextBindingPtr; - - static VTable *vTables[]; - inline const VTable *vtable() const { return vTables[bindingType()]; } + QFlagPointer<QQmlAbstractBinding> m_nextBinding; }; -QQmlAbstractBinding::Pointer -QQmlAbstractBinding::getPointer(QQmlAbstractBinding *p) -{ - return p ? p->weakPointer() : Pointer(); -} - void QQmlAbstractBinding::setAddedToObject(bool v) { - m_mePtr.setFlagValue(v); + m_nextBinding.setFlagValue(v); } bool QQmlAbstractBinding::isAddedToObject() const { - return m_mePtr.flag(); + return m_nextBinding.flag(); } QQmlAbstractBinding *QQmlAbstractBinding::nextBinding() const { - return (QQmlAbstractBinding *)(m_nextBindingPtr & ~0x3); + return m_nextBinding.data(); } void QQmlAbstractBinding::setNextBinding(QQmlAbstractBinding *b) { - m_nextBindingPtr = qintptr(b) | (m_nextBindingPtr & 0x3); -} - -QQmlAbstractBinding::BindingType QQmlAbstractBinding::bindingType() const -{ - return (BindingType)(m_nextBindingPtr & 0x3); -} - -template<typename T> -void QQmlAbstractBinding::default_destroy(QQmlAbstractBinding *This, DestroyMode mode) -{ - // Assume the binding disconnects itself in the destructor, which for example QQmlBinding - // does in the destructor of its base class, QQmlJavaScriptExpression - Q_UNUSED(mode); - - This->removeFromObject(); - This->clear(); - delete static_cast<T *>(This); + if (b) + b->ref.ref(); + if (m_nextBinding.data() && !m_nextBinding->ref.deref()) + delete m_nextBinding.data(); + m_nextBinding = b; } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlabstractexpression.cpp b/src/qml/qml/qqmlabstractexpression.cpp deleted file mode 100644 index c55c86952c..0000000000 --- a/src/qml/qml/qqmlabstractexpression.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlabstractexpression_p.h" - -QT_BEGIN_NAMESPACE - -QQmlAbstractExpression::QQmlAbstractExpression() -: m_prevExpression(0), m_nextExpression(0) -{ -} - -QQmlAbstractExpression::~QQmlAbstractExpression() -{ - if (m_prevExpression) { - *m_prevExpression = m_nextExpression; - if (m_nextExpression) - m_nextExpression->m_prevExpression = m_prevExpression; - } - - if (m_context.isT2()) - m_context.asT2()->_s = 0; -} - -QQmlContextData *QQmlAbstractExpression::context() const -{ - if (m_context.isT1()) return m_context.asT1(); - else return m_context.asT2()->_c; -} - -void QQmlAbstractExpression::setContext(QQmlContextData *context) -{ - if (m_prevExpression) { - *m_prevExpression = m_nextExpression; - if (m_nextExpression) - m_nextExpression->m_prevExpression = m_prevExpression; - m_prevExpression = 0; - m_nextExpression = 0; - } - - if (m_context.isT1()) m_context = context; - else m_context.asT2()->_c = context; - - if (context) { - m_nextExpression = context->expressions; - if (m_nextExpression) - m_nextExpression->m_prevExpression = &m_nextExpression; - m_prevExpression = &context->expressions; - context->expressions = this; - } -} - -void QQmlAbstractExpression::refresh() -{ -} - -bool QQmlAbstractExpression::isValid() const -{ - return context() != 0; -} - -QT_END_NAMESPACE - diff --git a/src/qml/qml/qqmlabstractexpression_p.h b/src/qml/qml/qqmlabstractexpression_p.h deleted file mode 100644 index 82ba010434..0000000000 --- a/src/qml/qml/qqmlabstractexpression_p.h +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLABSTRACTEXPRESSION_P_H -#define QQMLABSTRACTEXPRESSION_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmlcontext_p.h> -#include <private/qfieldlist_p.h> -#include <private/qflagpointer_p.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlAbstractExpression -{ -public: - QQmlAbstractExpression(); - virtual ~QQmlAbstractExpression(); - - bool isValid() const; - - QQmlContextData *context() const; - void setContext(QQmlContextData *); - - virtual void refresh(); - - class DeleteWatcher { - public: - inline DeleteWatcher(QQmlAbstractExpression *); - inline ~DeleteWatcher(); - inline bool wasDeleted() const; - private: - friend class QQmlAbstractExpression; - QQmlContextData *_c; - QQmlAbstractExpression **_w; - QQmlAbstractExpression *_s; - }; - -private: - friend class QQmlContext; - friend class QQmlContextData; - friend class QQmlContextPrivate; - - QBiPointer<QQmlContextData, DeleteWatcher> m_context; - QQmlAbstractExpression **m_prevExpression; - QQmlAbstractExpression *m_nextExpression; -}; - -QQmlAbstractExpression::DeleteWatcher::DeleteWatcher(QQmlAbstractExpression *e) -: _c(0), _w(0), _s(e) -{ - if (e->m_context.isT1()) { - _w = &_s; - _c = e->m_context.asT1(); - e->m_context = this; - } else { - // Another watcher is already registered - _w = &e->m_context.asT2()->_s; - } -} - -QQmlAbstractExpression::DeleteWatcher::~DeleteWatcher() -{ - Q_ASSERT(*_w == 0 || (*_w == _s && _s->m_context.isT2())); - if (*_w && _s->m_context.asT2() == this) - _s->m_context = _c; -} - -bool QQmlAbstractExpression::DeleteWatcher::wasDeleted() const -{ - return *_w == 0; -} - -QT_END_NAMESPACE - -#endif // QQMLABSTRACTEXPRESSION_P_H diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index f223d099e4..3613c17242 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -43,43 +43,28 @@ #include <private/qqmlscriptstring_p.h> #include <private/qqmlcontextwrapper_p.h> #include <private/qqmlbuiltinfunctions_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qqmlvaluetypewrapper_p.h> #include <QVariant> #include <QtCore/qdebug.h> QT_BEGIN_NAMESPACE -// Used in qqmlabstractbinding.cpp -QQmlAbstractBinding::VTable QQmlBinding_vtable = { - QQmlAbstractBinding::default_destroy<QQmlBinding>, - QQmlBinding::expression, - QQmlBinding::propertyIndex, - QQmlBinding::object, - QQmlBinding::setEnabled, - QQmlBinding::update, - QQmlBinding::retargetBinding -}; - -QQmlBinding::Identifier QQmlBinding::Invalid = -1; - -static QQmlJavaScriptExpression::VTable QQmlBinding_jsvtable = { - QQmlBinding::expressionIdentifier, - QQmlBinding::expressionChanged -}; - QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt) -: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding) + : QQmlJavaScriptExpression(), + QQmlAbstractBinding() { setNotifyOnValueChanged(true); - QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt)); + QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt)); setScopeObject(obj); - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine(); - v4function.set(v4, qmlBinding(context(), obj, str, QString(), 0)); + createQmlBinding(context(), obj, str, QString(), 0); } QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt) -: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding) + : QQmlJavaScriptExpression(), + QQmlAbstractBinding() { if (ctxt && !ctxt->isValid()) return; @@ -100,51 +85,52 @@ QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlConte } setNotifyOnValueChanged(true); - QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); + QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); setScopeObject(obj ? obj : scriptPrivate->scope); QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine(); if (runtimeFunction) { - v4function.set(v4, QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxtdata, scopeObject(), runtimeFunction)); + m_function.set(v4, QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxtdata, scopeObject(), runtimeFunction)); } else { QString code = scriptPrivate->script; - v4function.set(v4, qmlBinding(context(), scopeObject(), code, url, scriptPrivate->lineNumber)); + createQmlBinding(context(), scopeObject(), code, url, scriptPrivate->lineNumber); } } QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt) -: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding) + : QQmlJavaScriptExpression(), + QQmlAbstractBinding() { setNotifyOnValueChanged(true); - QQmlAbstractExpression::setContext(ctxt); + QQmlJavaScriptExpression::setContext(ctxt); setScopeObject(obj); - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine(); - v4function.set(v4, qmlBinding(ctxt, obj, str, QString(), 0)); + createQmlBinding(ctxt, obj, str, QString(), 0); } QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt, const QString &url, quint16 lineNumber, quint16 columnNumber) -: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding) + : QQmlJavaScriptExpression(), + QQmlAbstractBinding() { Q_UNUSED(columnNumber); setNotifyOnValueChanged(true); - QQmlAbstractExpression::setContext(ctxt); + QQmlJavaScriptExpression::setContext(ctxt); setScopeObject(obj); - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine(); - v4function.set(v4, qmlBinding(ctxt, obj, str, url, lineNumber)); + createQmlBinding(ctxt, obj, str, url, lineNumber); } QQmlBinding::QQmlBinding(const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt) -: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding) + : QQmlJavaScriptExpression(), + QQmlAbstractBinding() { setNotifyOnValueChanged(true); - QQmlAbstractExpression::setContext(ctxt); + QQmlJavaScriptExpression::setContext(ctxt); setScopeObject(obj); - v4function.set(functionPtr.asObject()->engine(), functionPtr); + m_function.set(functionPtr.as<QV4::Object>()->engine(), functionPtr); } QQmlBinding::~QQmlBinding() @@ -162,90 +148,243 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) return; // Check that the target has not been deleted - if (QQmlData::wasDeleted(object())) + if (QQmlData::wasDeleted(targetObject())) return; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); QV4::Scope scope(ep->v4engine()); - QV4::ScopedFunctionObject f(scope, v4function.value()); + QV4::ScopedFunctionObject f(scope, m_function.value()); Q_ASSERT(f); - if (!updatingFlag()) { - QQmlBindingProfiler prof(ep->profiler, f); - setUpdatingFlag(true); + if (updatingFlag()) { + QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), getPropertyData(), 0); + QQmlAbstractBinding::printBindingLoopError(p); + return; + } + + QQmlBindingProfiler prof(ep->profiler, f); + setUpdatingFlag(true); - QQmlAbstractExpression::DeleteWatcher watcher(this); + QQmlJavaScriptExpression::DeleteWatcher watcher(this); - if (m_core.propType == qMetaTypeId<QQmlBinding *>()) { + QQmlPropertyData pd = getPropertyData(); - int idx = m_core.coreIndex; - Q_ASSERT(idx != -1); + if (pd.propType == qMetaTypeId<QQmlBinding *>()) { - QQmlBinding *t = this; - int status = -1; - void *a[] = { &t, 0, &status, &flags }; - QMetaObject::metacall(*m_coreObject, QMetaObject::WriteProperty, idx, a); + int idx = pd.coreIndex; + Q_ASSERT(idx != -1); - } else { - ep->referenceScarceResources(); + QQmlBinding *t = this; + int status = -1; + void *a[] = { &t, 0, &status, &flags }; + QMetaObject::metacall(*m_target, QMetaObject::WriteProperty, idx, a); - bool isUndefined = false; + } else { + ep->referenceScarceResources(); - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(context(), f, &isUndefined)); + bool isUndefined = false; - bool needsErrorLocationData = false; - if (!watcher.wasDeleted() && !hasError()) - needsErrorLocationData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(), - this, result, isUndefined, flags); + QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); - if (!watcher.wasDeleted()) { + bool error = false; + if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) + error = !write(pd, result, isUndefined, flags); - if (needsErrorLocationData) - delayedError()->setErrorLocation(f->sourceLocation()); + if (!watcher.wasDeleted()) { - if (hasError()) { - if (!delayedError()->addError(ep)) ep->warning(this->error(context()->engine)); - } else { - clearError(); - } + if (error) { + delayedError()->setErrorLocation(f->sourceLocation()); + delayedError()->setErrorObject(m_target.data()); + } + if (hasError()) { + if (!delayedError()->addError(ep)) ep->warning(this->error(context()->engine)); + } else { + clearError(); } - ep->dereferenceScarceResources(); } - if (!watcher.wasDeleted()) - setUpdatingFlag(false); - } else { - QQmlProperty p = property(); - QQmlAbstractBinding::printBindingLoopError(p); + ep->dereferenceScarceResources(); } + + if (!watcher.wasDeleted()) + setUpdatingFlag(false); +} + +// Returns true if successful, false if an error description was set on expression +bool QQmlBinding::write(const QQmlPropertyData &core, + const QV4::Value &result, bool isUndefined, + QQmlPropertyPrivate::WriteFlags flags) +{ + Q_ASSERT(m_target.data()); + Q_ASSERT(core.coreIndex != -1); + + QQmlEngine *engine = context()->engine; + QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); + +#define QUICK_STORE(cpptype, conversion) \ + { \ + cpptype o = (conversion); \ + int status = -1; \ + void *argv[] = { &o, 0, &status, &flags }; \ + QMetaObject::metacall(m_target.data(), QMetaObject::WriteProperty, core.coreIndex, argv); \ + return true; \ + } \ + + + if (!isUndefined && !core.isValueTypeVirtual()) { + switch (core.propType) { + case QMetaType::Int: + if (result.isInteger()) + QUICK_STORE(int, result.integerValue()) + else if (result.isNumber()) + QUICK_STORE(int, result.doubleValue()) + break; + case QMetaType::Double: + if (result.isNumber()) + QUICK_STORE(double, result.asDouble()) + break; + case QMetaType::Float: + if (result.isNumber()) + QUICK_STORE(float, result.asDouble()) + break; + case QMetaType::QString: + if (result.isString()) + QUICK_STORE(QString, result.toQStringNoThrow()) + break; + default: + if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) { + if (vtw->d()->valueType->typeId == core.propType) { + return vtw->write(m_target.data(), core.coreIndex); + } + } + break; + } + } +#undef QUICK_STORE + + int type = core.isValueTypeVirtual() ? core.valueTypePropType : core.propType; + + QQmlJavaScriptExpression::DeleteWatcher watcher(this); + + QVariant value; + bool isVarProperty = core.isVarProperty(); + + if (isUndefined) { + } else if (core.isQList()) { + value = QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QObject *> >()); + } else if (result.isNull() && core.isQObject()) { + value = QVariant::fromValue((QObject *)0); + } else if (core.propType == qMetaTypeId<QList<QUrl> >()) { + value = QQmlPropertyPrivate::resolvedUrlSequence(QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QUrl> >()), context()); + } else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) { + value = QV8Engine::getV4(v8engine)->toVariant(result, type); + } + + if (hasError()) { + return false; + } else if (isVarProperty) { + const QV4::FunctionObject *f = result.as<QV4::FunctionObject>(); + if (f && f->isBinding()) { + // we explicitly disallow this case to avoid confusion. Users can still store one + // in an array in a var property if they need to, but the common case is user error. + delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); + return false; + } + + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_target.data()); + Q_ASSERT(vmemo); + vmemo->setVMEProperty(core.coreIndex, result); + } else if (isUndefined && core.isResettable()) { + void *args[] = { 0 }; + QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex, args); + } else if (isUndefined && type == qMetaTypeId<QVariant>()) { + QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, QVariant(), context(), flags); + } else if (type == qMetaTypeId<QJSValue>()) { + const QV4::FunctionObject *f = result.as<QV4::FunctionObject>(); + if (f && f->isBinding()) { + delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); + return false; + } + QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, QVariant::fromValue( + QJSValue(QV8Engine::getV4(v8engine), result.asReturnedValue())), + context(), flags); + } else if (isUndefined) { + QString errorStr = QLatin1String("Unable to assign [undefined] to "); + if (!QMetaType::typeName(type)) + errorStr += QLatin1String("[unknown property type]"); + else + errorStr += QLatin1String(QMetaType::typeName(type)); + delayedError()->setErrorDescription(errorStr); + return false; + } else if (const QV4::FunctionObject *f = result.as<QV4::FunctionObject>()) { + if (f->isBinding()) + delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); + else + delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var.")); + return false; + } else if (!QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, value, context(), flags)) { + + if (watcher.wasDeleted()) + return true; + + const char *valueType = 0; + const char *propertyType = 0; + + if (value.userType() == QMetaType::QObjectStar) { + if (QObject *o = *(QObject *const *)value.constData()) { + valueType = o->metaObject()->className(); + + QQmlMetaObject propertyMetaObject = QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate::get(engine), type); + if (!propertyMetaObject.isNull()) + propertyType = propertyMetaObject.className(); + } + } else if (value.userType() != QVariant::Invalid) { + if (value.userType() == QMetaType::VoidStar) + valueType = "null"; + else + valueType = QMetaType::typeName(value.userType()); + } + + if (!valueType) + valueType = "undefined"; + if (!propertyType) + propertyType = QMetaType::typeName(type); + if (!propertyType) + propertyType = "[unknown property type]"; + + delayedError()->setErrorDescription(QLatin1String("Unable to assign ") + + QLatin1String(valueType) + + QLatin1String(" to ") + + QLatin1String(propertyType)); + return false; + } + + return true; } QVariant QQmlBinding::evaluate() { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); - QV4::Scope scope(ep->v4engine()); ep->referenceScarceResources(); bool isUndefined = false; - QV4::ScopedValue f(scope, v4function.value()); - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(context(), f, &isUndefined)); + QV4::Scope scope(ep->v4engine()); + QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); ep->dereferenceScarceResources(); return scope.engine->toVariant(result, qMetaTypeId<QList<QObject*> >()); } -QString QQmlBinding::expressionIdentifier(QQmlJavaScriptExpression *e) +QString QQmlBinding::expressionIdentifier() { - QQmlBinding *This = static_cast<QQmlBinding *>(e); - - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(This->context()->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); QV4::Scope scope(ep->v4engine()); - QV4::ScopedValue f(scope, This->v4function.value()); - QV4::Function *function = f->asFunctionObject()->function(); + QV4::ScopedValue f(scope, m_function.value()); + QV4::Function *function = f->as<QV4::FunctionObject>()->function(); QString url = function->sourceFile(); quint16 lineNumber = function->compiledFunction->location.line; @@ -254,10 +393,9 @@ QString QQmlBinding::expressionIdentifier(QQmlJavaScriptExpression *e) return url + QLatin1Char(':') + QString::number(lineNumber) + QLatin1Char(':') + QString::number(columnNumber); } -void QQmlBinding::expressionChanged(QQmlJavaScriptExpression *e) +void QQmlBinding::expressionChanged() { - QQmlBinding *This = static_cast<QQmlBinding *>(e); - This->update(); + update(); } void QQmlBinding::refresh() @@ -265,36 +403,6 @@ void QQmlBinding::refresh() update(); } -QString QQmlBinding::expression(const QQmlAbstractBinding *This) -{ - return static_cast<const QQmlBinding *>(This)->expression(); -} - -int QQmlBinding::propertyIndex(const QQmlAbstractBinding *This) -{ - return static_cast<const QQmlBinding *>(This)->propertyIndex(); -} - -QObject *QQmlBinding::object(const QQmlAbstractBinding *This) -{ - return static_cast<const QQmlBinding *>(This)->object(); -} - -void QQmlBinding::setEnabled(QQmlAbstractBinding *This, bool e, QQmlPropertyPrivate::WriteFlags f) -{ - static_cast<QQmlBinding *>(This)->setEnabled(e, f); -} - -void QQmlBinding::update(QQmlAbstractBinding *This , QQmlPropertyPrivate::WriteFlags f) -{ - static_cast<QQmlBinding *>(This)->update(f); -} - -void QQmlBinding::retargetBinding(QQmlAbstractBinding *This, QObject *o, int i) -{ - static_cast<QQmlBinding *>(This)->retargetBinding(o, i); -} - void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) { setEnabledFlag(e); @@ -307,44 +415,92 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) QString QQmlBinding::expression() const { QV4::Scope scope(QQmlEnginePrivate::get(context()->engine)->v4engine()); - QV4::ScopedValue v(scope, v4function.value()); + QV4::ScopedValue v(scope, m_function.value()); return v->toQStringNoThrow(); } -QObject *QQmlBinding::object() const +void QQmlBinding::setTarget(const QQmlProperty &prop) { - if (m_coreObject.hasValue()) return m_coreObject.constValue()->target; - else return *m_coreObject; + setTarget(prop.object(), QQmlPropertyPrivate::get(prop)->core); } -int QQmlBinding::propertyIndex() const +void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core) { - if (m_coreObject.hasValue()) return m_coreObject.constValue()->targetProperty; - else return m_core.encodedIndex(); -} + m_target = object; -void QQmlBinding::retargetBinding(QObject *t, int i) -{ - m_coreObject.value().target = t; - m_coreObject.value().targetProperty = i; -} + if (!object) { + m_targetIndex = -1; + return; + } -void QQmlBinding::setTarget(const QQmlProperty &prop) -{ - setTarget(prop.object(), QQmlPropertyPrivate::get(prop)->core, - QQmlPropertyPrivate::get(prop)->context); -} + QQmlPropertyData pd = core; -void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, QQmlContextData *ctxt) -{ - m_coreObject = object; - m_core = core; - m_ctxt = ctxt; + while (pd.isAlias()) { + int coreIndex = pd.coreIndex; + int valueTypeIndex = pd.getValueTypeCoreIndex(); + QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); + + int aValueTypeIndex; + if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) { + m_target = 0; + m_targetIndex = -1; + return; + } + if (valueTypeIndex == -1) + valueTypeIndex = aValueTypeIndex; + + QQmlData *data = QQmlData::get(object, false); + if (!data || !data->propertyCache) { + m_target = 0; + m_targetIndex = -1; + return; + } + QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); + Q_ASSERT(propertyData); + + m_target = object; + pd = *propertyData; + if (valueTypeIndex != -1) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(pd.propType); + Q_ASSERT(valueTypeMetaObject); + QMetaProperty vtProp = valueTypeMetaObject->property(valueTypeIndex); + pd.setFlags(pd.getFlags() | QQmlPropertyData::IsValueTypeVirtual); + pd.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); + pd.valueTypePropType = vtProp.userType(); + pd.valueTypeCoreIndex = valueTypeIndex; + } + } + m_targetIndex = pd.encodedIndex(); + + QQmlData *data = QQmlData::get(*m_target, true); + if (!data->propertyCache) { + data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject()); + data->propertyCache->addref(); + } } -QQmlProperty QQmlBinding::property() const +QQmlPropertyData QQmlBinding::getPropertyData() const { - return QQmlPropertyPrivate::restore(object(), m_core, *m_ctxt); + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(m_targetIndex, &coreIndex); + + QQmlData *data = QQmlData::get(*m_target, false); + Q_ASSERT(data && data->propertyCache); + + QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); + Q_ASSERT(propertyData); + + QQmlPropertyData d = *propertyData; + if (valueTypeIndex != -1) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d.propType); + Q_ASSERT(valueTypeMetaObject); + QMetaProperty vtProp = valueTypeMetaObject->property(valueTypeIndex); + d.setFlags(d.getFlags() | QQmlPropertyData::IsValueTypeVirtual); + d.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); + d.valueTypePropType = vtProp.userType(); + d.valueTypeCoreIndex = valueTypeIndex; + } + return d; } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 1e440b2e86..a435847819 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -57,16 +57,15 @@ #include <private/qpointervaluepair_p.h> #include <private/qqmlabstractbinding_p.h> -#include <private/qqmlabstractexpression_p.h> #include <private/qqmljavascriptexpression_p.h> QT_BEGIN_NAMESPACE class QQmlContext; class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression, - public QQmlAbstractExpression, public QQmlAbstractBinding { + friend class QQmlAbstractBinding; public: QQmlBinding(const QString &, QObject *, QQmlContext *); QQmlBinding(const QQmlScriptString &, QObject *, QQmlContext *); @@ -74,84 +73,62 @@ public: QQmlBinding(const QString &, QObject *, QQmlContextData *, const QString &url, quint16 lineNumber, quint16 columnNumber); QQmlBinding(const QV4::Value &, QObject *, QQmlContextData *); + ~QQmlBinding(); void setTarget(const QQmlProperty &); - void setTarget(QObject *, const QQmlPropertyData &, QQmlContextData *); - QQmlProperty property() const; + void setTarget(QObject *, const QQmlPropertyData &); void setNotifyOnValueChanged(bool); - // Inherited from QQmlAbstractExpression + // Inherited from QQmlJavaScriptExpression virtual void refresh(); - // "Inherited" from QQmlAbstractBinding - static QString expression(const QQmlAbstractBinding *); - static int propertyIndex(const QQmlAbstractBinding *); - static QObject *object(const QQmlAbstractBinding *); - static void setEnabled(QQmlAbstractBinding *, bool, QQmlPropertyPrivate::WriteFlags); - static void update(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); - static void retargetBinding(QQmlAbstractBinding *, QObject *, int); - - void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags); - void update(QQmlPropertyPrivate::WriteFlags flags); - void update() { update(QQmlPropertyPrivate::DontRemoveBinding); } - - QString expression() const; - QObject *object() const; - int propertyIndex() const; - void retargetBinding(QObject *, int); + // Inherited from QQmlAbstractBinding + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding); + virtual QString expression() const; + void update(QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding); typedef int Identifier; - static Identifier Invalid; + enum { + Invalid = -1 + }; QVariant evaluate(); - static QString expressionIdentifier(QQmlJavaScriptExpression *); - static void expressionChanged(QQmlJavaScriptExpression *); - -protected: - friend class QQmlAbstractBinding; - ~QQmlBinding(); + virtual QString expressionIdentifier(); + virtual void expressionChanged(); private: - QV4::PersistentValue v4function; - inline bool updatingFlag() const; inline void setUpdatingFlag(bool); inline bool enabledFlag() const; inline void setEnabledFlag(bool); + QQmlPropertyData getPropertyData() const; - struct Retarget { - QObject *target; - int targetProperty; - }; + bool write(const QQmlPropertyData &core, + const QV4::Value &result, bool isUndefined, + QQmlPropertyPrivate::WriteFlags flags); - QPointerValuePair<QObject, Retarget> m_coreObject; - QQmlPropertyData m_core; - // We store some flag bits in the following flag pointers. - // m_ctxt:flag1 - updatingFlag - // m_ctxt:flag2 - enabledFlag - QFlagPointer<QQmlContextData> m_ctxt; }; bool QQmlBinding::updatingFlag() const { - return m_ctxt.flag(); + return m_target.flag(); } void QQmlBinding::setUpdatingFlag(bool v) { - m_ctxt.setFlagValue(v); + m_target.setFlagValue(v); } bool QQmlBinding::enabledFlag() const { - return m_ctxt.flag2(); + return m_target.flag2(); } void QQmlBinding::setEnabledFlag(bool v) { - m_ctxt.setFlag2Value(v); + m_target.setFlag2Value(v); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 766e657c59..3d1a9f8a88 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -43,11 +43,12 @@ #include "qqmlcontext.h" #include "qqmlglobal_p.h" #include <private/qqmlprofiler_p.h> -#include <private/qv4debugservice_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmlcompiler_p.h> #include "qqmlinfo.h" -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <QtCore/qstringbuilder.h> #include <QtCore/qdebug.h> @@ -55,59 +56,68 @@ QT_BEGIN_NAMESPACE -static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = { - QQmlBoundSignalExpression::expressionIdentifier, - QQmlBoundSignalExpression::expressionChanged -}; - -QQmlBoundSignalExpression::ExtraData::ExtraData(const QString &handlerName, const QString ¶meterString, - const QString &expression, const QString &fileName, - quint16 line, quint16 column) - : m_handlerName(handlerName), - m_parameterString(parameterString), - m_expression(expression), - m_sourceLocation(fileName, line, column) -{ -} - QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, const QString &expression, const QString &fileName, quint16 line, quint16 column, const QString &handlerName, const QString ¶meterString) - : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), + : QQmlJavaScriptExpression(), m_index(index), - m_target(target), - m_extra(new ExtraData(handlerName, parameterString, expression, fileName, line, column)) + m_target(target) { - setExpressionFunctionValid(false); - setInvalidParameterName(false); - init(ctxt, scope); + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine()); + QV4::ExecutionEngine *v4 = ep->v4engine(); + + QString function; + + // Add some leading whitespace to account for the binding's column offset. + // It's 2 off because a, we start counting at 1 and b, the '(' below is not counted. + function.fill(QChar(QChar::Space), qMax(column, (quint16)2) - 2); + function += QStringLiteral("(function "); + function += handlerName; + function += QLatin1Char('('); + + if (parameterString.isEmpty()) { + QString error; + //TODO: look at using the property cache here (as in the compiler) + // for further optimization + QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index); + function += QQmlPropertyCache::signalParameterStringForJS(v4, signal.parameterNames(), &error); + + if (!error.isEmpty()) { + qmlInfo(scopeObject()) << error; + return; + } + } else + function += parameterString; + + function += QStringLiteral(") { "); + function += expression; + function += QStringLiteral(" })"); + + m_function.set(v4, evalFunction(context(), scopeObject(), function, fileName, line)); + + if (m_function.isNullOrUndefined()) + return; // could not evaluate function. Not valid. + } QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, const QV4::Value &function) - : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), + : QQmlJavaScriptExpression(), m_index(index), - m_function(function.asObject()->engine(), function), - m_target(target), - m_extra(0) + m_target(target) { - setExpressionFunctionValid(true); - setInvalidParameterName(false); - + m_function.set(function.as<QV4::Object>()->engine(), function); init(ctxt, scope); } QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, QV4::Function *runtimeFunction) - : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable), + : QQmlJavaScriptExpression(), m_index(index), - m_target(target), - m_extra(0) + m_target(target) { - setExpressionFunctionValid(true); - setInvalidParameterName(false); - // It's important to call init first, because m_index gets remapped in case of cloned signals. init(ctxt, scope); @@ -117,9 +127,8 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, m_function.set(engine, QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxt, scope, runtimeFunction, signal.parameterNames(), &error)); if (!error.isEmpty()) { qmlInfo(scopeObject()) << error; - setInvalidParameterName(true); - } else - setInvalidParameterName(false); + m_function.clear(); + } } void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope) @@ -134,34 +143,30 @@ void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope) QQmlBoundSignalExpression::~QQmlBoundSignalExpression() { - delete m_extra.data(); } -QString QQmlBoundSignalExpression::expressionIdentifier(QQmlJavaScriptExpression *e) +QString QQmlBoundSignalExpression::expressionIdentifier() { - QQmlBoundSignalExpression *This = static_cast<QQmlBoundSignalExpression *>(e); - QQmlSourceLocation loc = This->sourceLocation(); + QQmlSourceLocation loc = sourceLocation(); return loc.sourceFile + QLatin1Char(':') + QString::number(loc.line); } -void QQmlBoundSignalExpression::expressionChanged(QQmlJavaScriptExpression *) +void QQmlBoundSignalExpression::expressionChanged() { // bound signals do not notify on change. } QQmlSourceLocation QQmlBoundSignalExpression::sourceLocation() const { - if (expressionFunctionValid()) { - QV4::Function *f = function(); - Q_ASSERT(f); + QV4::Function *f = function(); + if (f) { QQmlSourceLocation loc; loc.sourceFile = f->sourceFile(); loc.line = f->compiledFunction->location.line; loc.column = f->compiledFunction->location.column; return loc; } - Q_ASSERT(!m_extra.isNull()); - return m_extra->m_sourceLocation; + return QQmlSourceLocation(); } QString QQmlBoundSignalExpression::expression() const @@ -171,10 +176,8 @@ QString QQmlBoundSignalExpression::expression() const QV4::Scope scope(QQmlEnginePrivate::get(engine())->v4engine()); QV4::ScopedValue v(scope, m_function.value()); return v->toQStringNoThrow(); - } else { - Q_ASSERT(!m_extra.isNull()); - return m_extra->m_expression; } + return QString(); } QV4::Function *QQmlBoundSignalExpression::function() const @@ -194,108 +197,79 @@ void QQmlBoundSignalExpression::evaluate(void **a) { Q_ASSERT (context() && engine()); - if (invalidParameterName()) + if (!expressionFunctionValid()) return; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine()); QV4::Scope scope(ep->v4engine()); ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. - { - if (!expressionFunctionValid()) { - Q_ASSERT(!m_extra.isNull()); - QString expression; - - // Add some leading whitespace to account for the binding's column offset. - // It's 2 off because a, we start counting at 1 and b, the '(' below is not counted. - expression.fill(QChar(QChar::Space), qMax(m_extra->m_sourceLocation.column, (quint16)2) - 2); - expression += QStringLiteral("(function "); - expression += m_extra->m_handlerName; - expression += QLatin1Char('('); - - if (m_extra->m_parameterString.isEmpty()) { - QString error; - //TODO: look at using the property cache here (as in the compiler) - // for further optimization - QMetaMethod signal = QMetaObjectPrivate::signal(m_target->metaObject(), m_index); - expression += QQmlPropertyCache::signalParameterStringForJS(scope.engine, signal.parameterNames(), &error); - - if (!error.isEmpty()) { - qmlInfo(scopeObject()) << error; - setInvalidParameterName(true); - ep->dereferenceScarceResources(); - return; - } - } else - expression += m_extra->m_parameterString; - - expression += QStringLiteral(") { "); - expression += m_extra->m_expression; - expression += QStringLiteral(" })"); - - m_extra->m_expression.clear(); - m_extra->m_handlerName.clear(); - m_extra->m_parameterString.clear(); - - m_function.set(scope.engine, evalFunction(context(), scopeObject(), expression, - m_extra->m_sourceLocation.sourceFile, m_extra->m_sourceLocation.line, &m_extra->m_v8qmlscope)); - - if (m_function.isNullOrUndefined()) { - ep->dereferenceScarceResources(); - return; // could not evaluate function. Not valid. - } - - setExpressionFunctionValid(true); - } - QVarLengthArray<int, 9> dummy; - //TODO: lookup via signal index rather than method index as an optimization - int methodIndex = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).methodIndex(); - int *argsTypes = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, dummy, 0); - int argCount = argsTypes ? *argsTypes : 0; - - QV4::ScopedValue f(scope, m_function.value()); - QV4::ScopedCallData callData(scope, argCount); - for (int ii = 0; ii < argCount; ++ii) { - int type = argsTypes[ii + 1]; - //### ideally we would use metaTypeToJS, however it currently gives different results - // for several cases (such as QVariant type and QObject-derived types) - //args[ii] = engine->metaTypeToJS(type, a[ii + 1]); - if (type == QMetaType::QVariant) { - callData->args[ii] = scope.engine->fromVariant(*((QVariant *)a[ii + 1])); - } else if (type == QMetaType::Int) { - //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise - callData->args[ii] = QV4::Primitive::fromInt32(*reinterpret_cast<const int*>(a[ii + 1])); - } else if (type == qMetaTypeId<QQmlV4Handle>()) { - callData->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]); - } else if (ep->isQObject(type)) { - if (!*reinterpret_cast<void* const *>(a[ii + 1])) - callData->args[ii] = QV4::Primitive::nullValue(); - else - callData->args[ii] = QV4::QObjectWrapper::wrap(ep->v4engine(), *reinterpret_cast<QObject* const *>(a[ii + 1])); - } else { - callData->args[ii] = scope.engine->fromVariant(QVariant(type, a[ii + 1])); - } + QVarLengthArray<int, 9> dummy; + //TODO: lookup via signal index rather than method index as an optimization + int methodIndex = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).methodIndex(); + int *argsTypes = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, dummy, 0); + int argCount = argsTypes ? *argsTypes : 0; + + QV4::ScopedCallData callData(scope, argCount); + for (int ii = 0; ii < argCount; ++ii) { + int type = argsTypes[ii + 1]; + //### ideally we would use metaTypeToJS, however it currently gives different results + // for several cases (such as QVariant type and QObject-derived types) + //args[ii] = engine->metaTypeToJS(type, a[ii + 1]); + if (type == QMetaType::QVariant) { + callData->args[ii] = scope.engine->fromVariant(*((QVariant *)a[ii + 1])); + } else if (type == QMetaType::Int) { + //### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise + callData->args[ii] = QV4::Primitive::fromInt32(*reinterpret_cast<const int*>(a[ii + 1])); + } else if (type == qMetaTypeId<QQmlV4Handle>()) { + callData->args[ii] = *reinterpret_cast<QQmlV4Handle *>(a[ii + 1]); + } else if (ep->isQObject(type)) { + if (!*reinterpret_cast<void* const *>(a[ii + 1])) + callData->args[ii] = QV4::Primitive::nullValue(); + else + callData->args[ii] = QV4::QObjectWrapper::wrap(ep->v4engine(), *reinterpret_cast<QObject* const *>(a[ii + 1])); + } else { + callData->args[ii] = scope.engine->fromVariant(QVariant(type, a[ii + 1])); } - - QQmlJavaScriptExpression::evaluate(context(), f, callData, 0); } + + QQmlJavaScriptExpression::evaluate(callData, 0); + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } //////////////////////////////////////////////////////////////////////// -QQmlAbstractBoundSignal::QQmlAbstractBoundSignal() -: m_prevSignal(0), m_nextSignal(0) + +/*! \internal + \a signal MUST be in the signal index range (see QObjectPrivate::signalIndex()). + This is different from QMetaMethod::methodIndex(). +*/ +QQmlBoundSignal::QQmlBoundSignal(QObject *target, int signal, QObject *owner, + QQmlEngine *engine) + : QQmlNotifierEndpoint(QQmlNotifierEndpoint::QQmlBoundSignal), + m_prevSignal(0), m_nextSignal(0), + m_expression(0) { + addToObject(owner); + + /* + If this is a cloned method, connect to the 'original'. For example, + for the signal 'void aSignal(int parameter = 0)', if the method + index refers to 'aSignal()', get the index of 'aSignal(int)'. + This ensures that 'parameter' will be available from QML. + */ + signal = QQmlPropertyCache::originalClone(target, signal); + QQmlNotifierEndpoint::connect(target, signal, engine); } -QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal() +QQmlBoundSignal::~QQmlBoundSignal() { removeFromObject(); } -void QQmlAbstractBoundSignal::addToObject(QObject *obj) +void QQmlBoundSignal::addToObject(QObject *obj) { Q_ASSERT(!m_prevSignal); Q_ASSERT(obj); @@ -308,7 +282,7 @@ void QQmlAbstractBoundSignal::addToObject(QObject *obj) data->signalHandlers = this; } -void QQmlAbstractBoundSignal::removeFromObject() +void QQmlBoundSignal::removeFromObject() { if (m_prevSignal) { *m_prevSignal = m_nextSignal; @@ -318,40 +292,6 @@ void QQmlAbstractBoundSignal::removeFromObject() } } -/*! \internal - \a signal MUST be in the signal index range (see QObjectPrivate::signalIndex()). - This is different from QMetaMethod::methodIndex(). -*/ -QQmlBoundSignal::QQmlBoundSignal(QObject *target, int signal, QObject *owner, - QQmlEngine *engine) -: m_expression(0), m_index(signal), m_isEvaluating(false) -{ - addToObject(owner); - setCallback(QQmlNotifierEndpoint::QQmlBoundSignal); - - /* - If this is a cloned method, connect to the 'original'. For example, - for the signal 'void aSignal(int parameter = 0)', if the method - index refers to 'aSignal()', get the index of 'aSignal(int)'. - This ensures that 'parameter' will be available from QML. - */ - m_index = QQmlPropertyCache::originalClone(target, m_index); - QQmlNotifierEndpoint::connect(target, m_index, engine); -} - -QQmlBoundSignal::~QQmlBoundSignal() -{ - m_expression = 0; -} - -/*! - Returns the signal index in the range returned by QObjectPrivate::signalIndex(). - This is different from QMetaMethod::methodIndex(). -*/ -int QQmlBoundSignal::index() const -{ - return m_index; -} /*! Returns the signal expression. @@ -362,45 +302,29 @@ QQmlBoundSignalExpression *QQmlBoundSignal::expression() const } /*! - Sets the signal expression to \a e. Returns the current signal expression, - or null if there is no signal expression. + Sets the signal expression to \a e. - The QQmlBoundSignal instance adds a reference to \a e. The caller - assumes ownership of the returned QQmlBoundSignalExpression reference. + The QQmlBoundSignal instance takes ownership of \a e (and does not add a reference). */ -QQmlBoundSignalExpressionPointer QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e) +void QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e) { - QQmlBoundSignalExpressionPointer rv = m_expression; - m_expression = e; - if (m_expression) m_expression->setNotifyOnValueChanged(false); - return rv; -} - -/*! - Sets the signal expression to \a e. Returns the current signal expression, - or null if there is no signal expression. - - The QQmlBoundSignal instance takes ownership of \a e (and does not add a reference). The caller - assumes ownership of the returned QQmlBoundSignalExpression reference. -*/ -QQmlBoundSignalExpressionPointer QQmlBoundSignal::takeExpression(QQmlBoundSignalExpression *e) -{ - QQmlBoundSignalExpressionPointer rv = m_expression; m_expression.take(e); - if (m_expression) m_expression->setNotifyOnValueChanged(false); - return rv; + if (m_expression) + m_expression->setNotifyOnValueChanged(false); } void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a) { QQmlBoundSignal *s = static_cast<QQmlBoundSignal*>(e); + if (!s->m_expression) return; - if (QQmlDebugService::isDebuggingEnabled()) - QV4DebugService::instance()->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal(s->m_expression->target()->metaObject(), s->m_index).methodSignature())); - - s->m_isEvaluating = true; + QV4DebugService *service = QQmlDebugConnector::service<QV4DebugService>(); + if (service) + service->signalEmitted(QString::fromLatin1(QMetaObjectPrivate::signal( + s->m_expression->target()->metaObject(), + s->signalIndex()).methodSignature())); QQmlEngine *engine; if (s->m_expression && (engine = s->m_expression->engine())) { @@ -410,8 +334,6 @@ void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a) QQmlEnginePrivate::warning(engine, s->m_expression->error(engine)); } } - - s->m_isEvaluating = false; } //////////////////////////////////////////////////////////////////////// diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h index 8d677ea039..3742317484 100644 --- a/src/qml/qml/qqmlboundsignal_p.h +++ b/src/qml/qml/qqmlboundsignal_p.h @@ -47,7 +47,6 @@ #include <QtCore/qmetaobject.h> -#include <private/qqmlabstractexpression_p.h> #include <private/qqmljavascriptexpression_p.h> #include <private/qqmlboundsignalexpressionpointer_p.h> #include <private/qqmlnotifier_p.h> @@ -58,7 +57,7 @@ QT_BEGIN_NAMESPACE -class Q_QML_PRIVATE_EXPORT QQmlBoundSignalExpression : public QQmlAbstractExpression, public QQmlJavaScriptExpression, public QQmlRefCount +class Q_QML_PRIVATE_EXPORT QQmlBoundSignalExpression : public QQmlJavaScriptExpression, public QQmlRefCount { public: QQmlBoundSignalExpression(QObject *target, int index, @@ -73,9 +72,9 @@ public: QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, QV4::Function *runtimeFunction); - // "inherited" from QQmlJavaScriptExpression. - static QString expressionIdentifier(QQmlJavaScriptExpression *); - static void expressionChanged(QQmlJavaScriptExpression *); + // inherited from QQmlJavaScriptExpression. + virtual QString expressionIdentifier(); + virtual void expressionChanged(); // evaluation of a bound signal expression doesn't return any value void evaluate(void **a); @@ -92,80 +91,35 @@ private: void init(QQmlContextData *ctxt, QObject *scope); - bool expressionFunctionValid() const { return m_extra.flag(); } - void setExpressionFunctionValid(bool v) { m_extra.setFlagValue(v); } - - bool invalidParameterName() const { return m_extra.flag2(); } - void setInvalidParameterName(bool v) { m_extra.setFlag2Value(v); } + bool expressionFunctionValid() const { return !m_function.isNullOrUndefined(); } int m_index; - QV4::PersistentValue m_function; - QObject *m_target; - - // only needed when !expressionFunctionValid() - struct ExtraData { - ExtraData(const QString &handlerName, const QString ¶meterString, - const QString &expression, const QString &fileName, - quint16 line, quint16 column); - QString m_handlerName; - QString m_parameterString; - QString m_expression; - QQmlSourceLocation m_sourceLocation; - QV4::PersistentValue m_v8qmlscope; - }; - - // We store some flag bits in the following flag pointers. - // flag - expressionFunctionValid - // flag2 - invalidParameterName - QFlagPointer<ExtraData> m_extra; }; -class Q_QML_PRIVATE_EXPORT QQmlAbstractBoundSignal +class Q_QML_PRIVATE_EXPORT QQmlBoundSignal : public QQmlNotifierEndpoint { public: - QQmlAbstractBoundSignal(); - virtual ~QQmlAbstractBoundSignal(); - - virtual int index() const = 0; - virtual QQmlBoundSignalExpression *expression() const = 0; - virtual QQmlBoundSignalExpressionPointer setExpression(QQmlBoundSignalExpression *) = 0; - virtual QQmlBoundSignalExpressionPointer takeExpression(QQmlBoundSignalExpression *) = 0; - virtual bool isEvaluating() const = 0; + QQmlBoundSignal(QObject *target, int signal, QObject *owner, QQmlEngine *engine); + ~QQmlBoundSignal(); void removeFromObject(); -protected: - void addToObject(QObject *owner); + + QQmlBoundSignalExpression *expression() const; + void takeExpression(QQmlBoundSignalExpression *); private: - friend class QQmlData; + friend void QQmlBoundSignal_callback(QQmlNotifierEndpoint *, void **); friend class QQmlPropertyPrivate; + friend class QQmlData; friend class QQmlEngineDebugService; - QQmlAbstractBoundSignal **m_prevSignal; - QQmlAbstractBoundSignal *m_nextSignal; -}; -class Q_QML_PRIVATE_EXPORT QQmlBoundSignal : public QQmlAbstractBoundSignal, - public QQmlNotifierEndpoint -{ -public: - QQmlBoundSignal(QObject *target, int signal, QObject *owner, QQmlEngine *engine); - virtual ~QQmlBoundSignal(); - - int index() const; - - QQmlBoundSignalExpression *expression() const; - QQmlBoundSignalExpressionPointer setExpression(QQmlBoundSignalExpression *); - QQmlBoundSignalExpressionPointer takeExpression(QQmlBoundSignalExpression *); + void addToObject(QObject *owner); - bool isEvaluating() const { return m_isEvaluating; } - -private: - friend void QQmlBoundSignal_callback(QQmlNotifierEndpoint *, void **); + QQmlBoundSignal **m_prevSignal; + QQmlBoundSignal *m_nextSignal; QQmlBoundSignalExpressionPointer m_expression; - int m_index; - bool m_isEvaluating; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 9210610cec..d62aada9c6 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -43,7 +43,8 @@ #include "qqmlengine.h" #include "qqmlbinding_p.h" #include "qqmlglobal_p.h" -#include <private/qqmlenginedebugservice_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include "qqmlincubator.h" #include "qqmlincubator_p.h" #include <private/qqmljavascriptexpression_p.h> @@ -53,6 +54,7 @@ #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4objectiterator_p.h> #include <QStack> #include <QStringList> @@ -61,23 +63,6 @@ #include <qqmlinfo.h> #include "qqmlmemoryprofiler_p.h" -#define INITIALPROPERTIES_SOURCE \ - "(function(object, values) {"\ - "try {"\ - "for (var property in values) {" \ - "try {"\ - "var properties = property.split(\".\");"\ - "var o = object;"\ - "for (var ii = 0; ii < properties.length - 1; ++ii) {"\ - "o = o[properties[ii]];"\ - "}"\ - "o[properties[properties.length - 1]] = values[property];"\ - "} catch(e) {}"\ - "}"\ - "} catch(e) {}"\ - "})" - - namespace { QThreadStorage<int> creationDepth; } @@ -98,7 +83,6 @@ V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension); \class QQmlComponent \since 5.0 \inmodule QtQml - \mainclass \brief The QQmlComponent class encapsulates a QML component definition @@ -896,10 +880,11 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) depthIncreased = false; } - if (enginePriv->isDebugging && rv) { + QQmlEngineDebugService *service = QQmlDebugConnector::service<QQmlEngineDebugService>(); + if (service && rv) { if (!context->isInternal) context->asQQmlContextPrivate()->instances.append(rv); - QQmlEngineDebugService::instance()->objectCreated(engine, rv); + service->objectCreated(engine, rv); } return rv; @@ -1194,6 +1179,46 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent) \sa incubateObject() */ + +static void setInitialProperties(QV4::ExecutionEngine *engine, const QV4::Value &o, const QV4::Value &v) +{ + QV4::Scope scope(engine); + QV4::ScopedObject object(scope); + QV4::ScopedObject valueMap(scope, v); + QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly|QV4::ObjectIterator::WithProtoChain); + QV4::ScopedString name(scope); + QV4::ScopedValue val(scope); + if (engine->hasException) + return; + + while (1) { + name = it.nextPropertyNameAsString(val); + if (!name) + break; + object = o; + const QStringList properties = name->toQString().split(QLatin1Char('.')); + for (int i = 0; i < properties.length() - 1; ++i) { + name = engine->newString(properties.at(i)); + object = object->get(name); + if (engine->hasException || !object) { + break; + } + } + if (engine->hasException || !object) { + engine->hasException = false; + continue; + } + name = engine->newString(properties.last()); + object->put(name, val); + if (engine->hasException) { + engine->hasException = false; + continue; + } + } + + engine->hasException = false; +} + /*! \internal */ @@ -1216,7 +1241,7 @@ void QQmlComponent::createObject(QQmlV4Function *args) if (args->length() >= 2) { QV4::ScopedValue v(scope, (*args)[1]); - if (!v->asObject() || v->asArrayObject()) { + if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) { qmlInfo(this) << tr("createObject: value is not an object"); args->setReturnValue(QV4::Encode::null()); return; @@ -1239,16 +1264,8 @@ void QQmlComponent::createObject(QQmlV4Function *args) QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4, rv)); Q_ASSERT(object->isObject()); - if (!valuemap->isUndefined()) { - QV4::ScopedObject qmlglobal(scope, args->qmlGlobal()); - QV4::ScopedValue f(scope, QV4::Script::evaluate(v4, QString::fromLatin1(INITIALPROPERTIES_SOURCE), qmlglobal)); - Q_ASSERT(f->asFunctionObject()); - QV4::ScopedCallData callData(scope, 2); - callData->thisObject = v4->globalObject(); - callData->args[0] = object; - callData->args[1] = valuemap; - f->asFunctionObject()->call(callData); - } + if (!valuemap->isUndefined()) + setInitialProperties(v4, object, valuemap); d->completeCreate(); @@ -1256,10 +1273,7 @@ void QQmlComponent::createObject(QQmlV4Function *args) QQmlData::get(rv)->explicitIndestructibleSet = false; QQmlData::get(rv)->indestructible = false; - if (!rv) - args->setReturnValue(QV4::Encode::null()); - else - args->setReturnValue(object->asReturnedValue()); + args->setReturnValue(object->asReturnedValue()); } /*! @@ -1342,7 +1356,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args) if (args->length() >= 2) { QV4::ScopedValue v(scope, (*args)[1]); if (v->isNull()) { - } else if (!v->asObject() || v->asArrayObject()) { + } else if (!v->as<QV4::Object>() || v->as<QV4::ArrayObject>()) { qmlInfo(this) << tr("createObject: value is not an object"); args->setReturnValue(QV4::Encode::null()); return; @@ -1383,25 +1397,17 @@ void QQmlComponent::incubateObject(QQmlV4Function *args) } // XXX used by QSGLoader -void QQmlComponentPrivate::initializeObjectWithInitialProperties(const QV4::Value &qmlGlobal, const QV4::Value &valuemap, QObject *toCreate) +void QQmlComponentPrivate::initializeObjectWithInitialProperties(const QV4::Value &valuemap, QObject *toCreate) { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); QV4::ExecutionEngine *v4engine = QV8Engine::getV4(ep->v8engine()); QV4::Scope scope(v4engine); QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4engine, toCreate)); - Q_ASSERT(object->asObject()); - - if (!valuemap.isUndefined()) { - QV4::ScopedObject qmlGlobalObj(scope, qmlGlobal); - QV4::ScopedFunctionObject f(scope, QV4::Script::evaluate(v4engine, - QString::fromLatin1(INITIALPROPERTIES_SOURCE), qmlGlobalObj)); - QV4::ScopedCallData callData(scope, 2); - callData->thisObject = v4engine->globalObject(); - callData->args[0] = object; - callData->args[1] = valuemap; - f->call(callData); - } + Q_ASSERT(object->as<QV4::Object>()); + + if (!valuemap.isUndefined()) + setInitialProperties(v4engine, object, valuemap); } QQmlComponentExtension::QQmlComponentExtension(QV4::ExecutionEngine *v4) @@ -1491,13 +1497,8 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o) if (!d()->valuemap.isUndefined()) { QV4::ExecutionEngine *v4 = engine(); QV4::Scope scope(v4); - - QV4::ScopedFunctionObject f(scope, QV4::Script::evaluate(v4, QString::fromLatin1(INITIALPROPERTIES_SOURCE), d()->qmlGlobal.asObject())); - QV4::ScopedCallData callData(scope, 2); - callData->thisObject = v4->globalObject(); - callData->args[0] = QV4::QObjectWrapper::wrap(v4, o); - callData->args[1] = d()->valuemap; - f->call(callData); + QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o)); + setInitialProperties(v4, obj, d()->valuemap); } } diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h index 8c866c585a..121c83db5c 100644 --- a/src/qml/qml/qqmlcomponent.h +++ b/src/qml/qml/qqmlcomponent.h @@ -63,8 +63,8 @@ class Q_QML_EXPORT QQmlComponent : public QObject Q_PROPERTY(QUrl url READ url CONSTANT) public: - Q_ENUMS(CompilationMode) enum CompilationMode { PreferSynchronous, Asynchronous }; + Q_ENUM(CompilationMode) QQmlComponent(QObject *parent = 0); QQmlComponent(QQmlEngine *, QObject *parent=0); @@ -74,8 +74,8 @@ public: QQmlComponent(QQmlEngine *, const QUrl &url, CompilationMode mode, QObject *parent = 0); virtual ~QQmlComponent(); - Q_ENUMS(Status) enum Status { Null, Ready, Loading, Error }; + Q_ENUM(Status) Status status() const; bool isNull() const; @@ -125,7 +125,6 @@ private: QT_END_NAMESPACE -Q_DECLARE_METATYPE(QQmlComponent::Status) QML_DECLARE_TYPE(QQmlComponent) QML_DECLARE_TYPEINFO(QQmlComponent, QML_HAS_ATTACHED_PROPERTIES) diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index adc6e173d2..15ec88dd52 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -82,7 +82,7 @@ public: QObject *beginCreate(QQmlContextData *); void completeCreate(); - void initializeObjectWithInitialProperties(const QV4::Value &qmlGlobal, const QV4::Value &valuemap, QObject *toCreate); + void initializeObjectWithInitialProperties(const QV4::Value &valuemap, QObject *toCreate); QQmlTypeData *typeData; virtual void typeDataReady(QQmlTypeData *); diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index fb51bad3a7..b056731e96 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -57,7 +57,6 @@ QQmlContextPrivate::QQmlContextPrivate() /*! \class QQmlContext \brief The QQmlContext class defines a context within a QML engine. - \mainclass \inmodule QtQml Contexts allow data to be exposed to the QML components instantiated by the @@ -585,9 +584,9 @@ void QQmlContextData::clearContext() { emitDestruction(); - QQmlAbstractExpression *expression = expressions; + QQmlJavaScriptExpression *expression = expressions; while (expression) { - QQmlAbstractExpression *nextExpression = expression->m_nextExpression; + QQmlJavaScriptExpression *nextExpression = expression->m_nextExpression; expression->m_prevExpression = 0; expression->m_nextExpression = 0; @@ -652,9 +651,9 @@ void QQmlContextData::setParent(QQmlContextData *p, bool parentTakesOwnership) } } -void QQmlContextData::refreshExpressionsRecursive(QQmlAbstractExpression *expression) +void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expression) { - QQmlAbstractExpression::DeleteWatcher w(expression); + QQmlJavaScriptExpression::DeleteWatcher w(expression); if (expression->m_nextExpression) refreshExpressionsRecursive(expression->m_nextExpression); @@ -808,7 +807,7 @@ QV4::IdentifierHash<int> &QQmlContextData::propertyNames() const { if (propertyNameCache.isEmpty()) { propertyNameCache = QV4::IdentifierHash<int>(QV8Engine::getV4(engine->handle())); - for (QHash<int, int>::ConstIterator it = objectIndexToId.begin(), end = objectIndexToId.end(); + for (QHash<int, int>::ConstIterator it = objectIndexToId.cbegin(), end = objectIndexToId.cend(); it != end; ++it) { const QV4::CompiledData::Object *obj = typeCompilationUnit->data->objectAt(it.key()); const QString name = typeCompilationUnit->data->stringAt(obj->idIndex); diff --git a/src/qml/qml/qqmlcontext.h b/src/qml/qml/qqmlcontext.h index c714846147..e69a2f8f69 100644 --- a/src/qml/qml/qqmlcontext.h +++ b/src/qml/qml/qqmlcontext.h @@ -72,6 +72,7 @@ public: void setContextProperty(const QString &, QObject *); void setContextProperty(const QString &, const QVariant &); + // ### Qt 6: no need for a mutable object, this should become a const QObject pointer QString nameForObject(QObject *) const; QUrl resolvedUrl(const QUrl &); diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index f5fd7d0a5c..95254d4baa 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -69,7 +69,7 @@ class QQmlExpression; class QQmlEngine; class QQmlExpression; class QQmlExpressionPrivate; -class QQmlAbstractExpression; +class QQmlJavaScriptExpression; class QQmlContextData; class QQmlContextPrivate : public QObjectPrivate @@ -171,7 +171,7 @@ public: QQmlContextData **prevChild; // Expressions that use this context - QQmlAbstractExpression *expressions; + QQmlJavaScriptExpression *expressions; // Doubly-linked list of objects that are owned by this context QQmlData *contextObjects; @@ -212,7 +212,7 @@ public: private: void refreshExpressionsRecursive(bool isGlobal); - void refreshExpressionsRecursive(QQmlAbstractExpression *); + void refreshExpressionsRecursive(QQmlJavaScriptExpression *); ~QQmlContextData() {} }; diff --git a/src/qml/qml/qqmlcontextwrapper.cpp b/src/qml/qml/qqmlcontextwrapper.cpp index 5844eab54f..23084fb202 100644 --- a/src/qml/qml/qqmlcontextwrapper.cpp +++ b/src/qml/qml/qqmlcontextwrapper.cpp @@ -38,13 +38,14 @@ #include <private/qqmlcontext_p.h> #include <private/qv4engine_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4objectproto_p.h> #include <private/qv4mm_p.h> #include <private/qv4function_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmltypewrapper_p.h> #include <private/qqmllistwrapper_p.h> +#include <private/qqmljavascriptexpression_p.h> #include <private/qjsvalue_p.h> QT_BEGIN_NAMESPACE @@ -60,7 +61,6 @@ Heap::QmlContextWrapper::QmlContextWrapper(QV4::ExecutionEngine *engine, QQmlCon , isNullWrapper(false) , context(context) , scopeObject(scopeObject) - , idObjectsWrapper(Q_NULLPTR) { } @@ -93,49 +93,30 @@ ReturnedValue QmlContextWrapper::urlScope(ExecutionEngine *v4, const QUrl &url) return w.asReturnedValue(); } -QQmlContextData *QmlContextWrapper::callingContext(ExecutionEngine *v4) -{ - Scope scope(v4); - QV4::Scoped<QmlContextWrapper> c(scope, v4->qmlContextObject()); - - return !!c ? c->getContext() : 0; -} - QQmlContextData *QmlContextWrapper::getContext(const Value &value) { if (!value.isObject()) return 0; - QV4::ExecutionEngine *v4 = value.asObject()->engine(); + QV4::ExecutionEngine *v4 = value.as<Object>()->engine(); Scope scope(v4); QV4::Scoped<QmlContextWrapper> c(scope, value); return c ? c->getContext() : 0; } -void QmlContextWrapper::takeContextOwnership(const Value &qmlglobal) -{ - Q_ASSERT(qmlglobal.isObject()); - - QV4::ExecutionEngine *v4 = qmlglobal.asObject()->engine(); - Scope scope(v4); - QV4::Scoped<QmlContextWrapper> c(scope, qmlglobal); - Q_ASSERT(c); - c->d()->ownsContext = true; -} - -ReturnedValue QmlContextWrapper::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<QmlContextWrapper>()); - QmlContextWrapper *resource = static_cast<QmlContextWrapper *>(m); + const QmlContextWrapper *resource = static_cast<const QmlContextWrapper *>(m); QV4::ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); // In V8 the JS global object would come _before_ the QML global object, // so simulate that here. bool hasProp; - QV4::ScopedValue result(scope, v4->globalObject()->get(name, &hasProp)); + QV4::ScopedValue result(scope, v4->globalObject->get(name, &hasProp)); if (hasProp) { if (hasProperty) *hasProperty = hasProp; @@ -145,7 +126,7 @@ ReturnedValue QmlContextWrapper::get(Managed *m, String *name, bool *hasProperty if (resource->d()->isNullWrapper) return Object::get(m, name, hasProperty); - if (QV4::QmlContextWrapper::callingContext(v4) != resource->d()->context) + if (v4->callingQmlContext() != resource->d()->context) return Object::get(m, name, hasProperty); result = Object::get(m, name, &hasProp); @@ -209,7 +190,8 @@ ReturnedValue QmlContextWrapper::get(Managed *m, String *name, bool *hasProperty if (propertyIdx < context->idValueCount) { - ep->captureProperty(&context->idValues[propertyIdx].bindings); + if (ep->propertyCapture) + ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings); if (hasProperty) *hasProperty = true; return QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]); @@ -217,8 +199,8 @@ ReturnedValue QmlContextWrapper::get(Managed *m, String *name, bool *hasProperty QQmlContextPrivate *cp = context->asQQmlContextPrivate(); - ep->captureProperty(context->asQQmlContext(), -1, - propertyIdx + cp->notifyIndex); + if (ep->propertyCapture) + ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex); const QVariant &value = cp->propertyValues.at(propertyIdx); if (hasProperty) @@ -278,10 +260,9 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) return; QV4::Scoped<QmlContextWrapper> wrapper(scope, resource); - PropertyAttributes attrs; - Property *pd = wrapper->__getOwnProperty__(name, &attrs); - if (pd) { - wrapper->putValue(pd, attrs, value); + uint member = wrapper->internalClass()->find(name); + if (member < UINT_MAX) { + wrapper->putValue(wrapper->propertyAt(member), wrapper->internalClass()->propertyData[member], value); return; } @@ -342,14 +323,6 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) Object::put(m, name, value); } -void QmlContextWrapper::markObjects(Heap::Base *m, ExecutionEngine *engine) -{ - QmlContextWrapper::Data *This = static_cast<QmlContextWrapper::Data *>(m); - if (This->idObjectsWrapper) - This->idObjectsWrapper->mark(engine); - Object::markObjects(m, engine); -} - void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const CompiledData::Function *compiledFunction) { // Let the caller check and avoid the function call :) @@ -358,13 +331,13 @@ void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const C QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0; if (!ep) return; - QQmlEnginePrivate::PropertyCapture *capture = ep->propertyCapture; + QQmlPropertyCapture *capture = ep->propertyCapture; if (!capture) return; QV4::Scope scope(engine); - QV4::Scoped<QmlContextWrapper> contextWrapper(scope, engine->qmlContextObject()); - QQmlContextData *qmlContext = contextWrapper->getContext(); + QV4::Scoped<QmlContext> context(scope, engine->qmlContext()); + QQmlContextData *qmlContext = context->qmlContext(); const quint32 *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; @@ -382,7 +355,7 @@ void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const C capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex); } - QObject *scopeObject = contextWrapper->getScopeObject(); + QObject *scopeObject = context->qmlScope(); const quint32 *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties; for (int i = 0; i < scopePropertyDependencyCount; ++i) { @@ -393,15 +366,6 @@ void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const C } -ReturnedValue QmlContextWrapper::idObjectsArray() -{ - if (!d()->idObjectsWrapper) { - ExecutionEngine *v4 = engine(); - d()->idObjectsWrapper = v4->memoryManager->alloc<QQmlIdObjectsArray>(v4, this); - } - return d()->idObjectsWrapper->asReturnedValue(); -} - ReturnedValue QmlContextWrapper::qmlSingletonWrapper(ExecutionEngine *v4, String *name) { if (!d()->context->imports) @@ -423,46 +387,4 @@ ReturnedValue QmlContextWrapper::qmlSingletonWrapper(ExecutionEngine *v4, String return QJSValuePrivate::convertedToValue(engine(), siinfo->scriptApi(e)); } -DEFINE_OBJECT_VTABLE(QQmlIdObjectsArray); - -Heap::QQmlIdObjectsArray::QQmlIdObjectsArray(ExecutionEngine *engine, QV4::QmlContextWrapper *contextWrapper) - : Heap::Object(engine) - , contextWrapper(contextWrapper->d()) -{ -} - -ReturnedValue QQmlIdObjectsArray::getIndexed(Managed *m, uint index, bool *hasProperty) -{ - Scope scope(static_cast<QV4::QQmlIdObjectsArray*>(m)->engine()); - Scoped<QQmlIdObjectsArray> This(scope, static_cast<QV4::QQmlIdObjectsArray*>(m)); - Scoped<QmlContextWrapper> contextWrapper(scope, This->d()->contextWrapper); - QQmlContextData *context = contextWrapper->getContext(); - if (!context) { - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); - } - if (index >= (uint)context->idValueCount) { - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); - } - - if (hasProperty) - *hasProperty = true; - - QQmlEnginePrivate *ep = scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : 0; - if (ep) - ep->captureProperty(&context->idValues[index].bindings); - - return QObjectWrapper::wrap(This->engine(), context->idValues[index].data()); -} - -void QQmlIdObjectsArray::markObjects(Heap::Base *that, ExecutionEngine *engine) -{ - QQmlIdObjectsArray::Data *This = static_cast<QQmlIdObjectsArray::Data *>(that); - This->contextWrapper->mark(engine); - Object::markObjects(that, engine); -} - QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontextwrapper_p.h b/src/qml/qml/qqmlcontextwrapper_p.h index 52d8677103..7b5d3216df 100644 --- a/src/qml/qml/qqmlcontextwrapper_p.h +++ b/src/qml/qml/qqmlcontextwrapper_p.h @@ -48,7 +48,7 @@ #include <QtCore/qglobal.h> #include <private/qtqmlglobal_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> #include <private/qqmlcontext_p.h> #include <private/qv4functionobject_p.h> @@ -65,8 +65,6 @@ struct QmlContextWrapper; namespace Heap { -struct QQmlIdObjectsArray; - struct QmlContextWrapper : Object { QmlContextWrapper(ExecutionEngine *engine, QQmlContextData *context, QObject *scopeObject, bool ownsContext = false); ~QmlContextWrapper(); @@ -76,12 +74,6 @@ struct QmlContextWrapper : Object { QQmlGuardedContextData context; QPointer<QObject> scopeObject; - QQmlIdObjectsArray *idObjectsWrapper; -}; - -struct QQmlIdObjectsArray : Object { - QQmlIdObjectsArray(QV4::ExecutionEngine *engine, QV4::QmlContextWrapper *contextWrapper); - QmlContextWrapper *contextWrapper; }; } @@ -94,8 +86,9 @@ struct Q_QML_EXPORT QmlContextWrapper : Object static ReturnedValue qmlScope(ExecutionEngine *e, QQmlContextData *ctxt, QObject *scope); static ReturnedValue urlScope(ExecutionEngine *v4, const QUrl &); - static QQmlContextData *callingContext(ExecutionEngine *v4); - static void takeContextOwnership(const Value &qmlglobal); + void takeContextOwnership() { + d()->ownsContext = true; + } inline QObject *getScopeObject() const { return d()->scopeObject; } inline QQmlContextData *getContext() const { return d()->context; } @@ -103,24 +96,12 @@ struct Q_QML_EXPORT QmlContextWrapper : Object void setReadOnly(bool b) { d()->readOnly = b; } - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void put(Managed *m, String *name, const Value &value); - static void markObjects(Heap::Base *m, ExecutionEngine *engine); static void registerQmlDependencies(ExecutionEngine *context, const CompiledData::Function *compiledFunction); - ReturnedValue idObjectsArray(); ReturnedValue qmlSingletonWrapper(ExecutionEngine *e, String *name); - -}; - -struct QQmlIdObjectsArray : public Object -{ - V4_OBJECT2(QQmlIdObjectsArray, Object) - - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); - static void markObjects(Heap::Base *that, ExecutionEngine *engine); - }; } diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 04c42b638d..3d2a76693a 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -48,8 +48,9 @@ #include <private/qtqmlglobal_p.h> #include <private/qobject_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4persistent_p.h> +#include <qjsengine.h> QT_BEGIN_NAMESPACE @@ -58,7 +59,7 @@ class QQmlEngine; class QQmlGuardImpl; class QQmlCompiledData; class QQmlAbstractBinding; -class QQmlAbstractBoundSignal; +class QQmlBoundSignal; class QQmlContext; class QQmlPropertyCache; class QQmlContextData; @@ -72,15 +73,7 @@ class QQmlNotifierEndpoint; class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData { public: - QQmlData() - : ownedByQml1(false), ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false), - hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), - hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(0), notifyList(0), context(0), outerContext(0), - bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), - lineNumber(0), columnNumber(0), jsEngineId(0), compiledData(0), deferredData(0), - propertyCache(0), guards(0), extendedData(0) { - init(); - } + QQmlData(); static inline void init() { static bool initialized = false; @@ -158,7 +151,7 @@ public: QQmlContextData *outerContext; QQmlAbstractBinding *bindings; - QQmlAbstractBoundSignal *signalHandlers; + QQmlBoundSignal *signalHandlers; // Linked list for QQmlContext::contextObjects QQmlData *nextContextObject; diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qml/qqmldirparser.cpp index 7f6310d58e..57b50733ea 100644 --- a/src/qml/qml/qqmldirparser.cpp +++ b/src/qml/qml/qqmldirparser.cpp @@ -146,7 +146,7 @@ bool QQmlDirParser::parse(const QString &source) if (invalidLine) { reportError(lineNumber, 0, - QString::fromLatin1("invalid qmldir directive contains too many tokens")); + QStringLiteral("invalid qmldir directive contains too many tokens")); continue; } else if (sectionCount == 0) { continue; // no sections, no party. @@ -154,17 +154,17 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("module")) { if (sectionCount != 2) { reportError(lineNumber, 0, - QString::fromLatin1("module identifier directive requires one argument, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("module identifier directive requires one argument, but %1 were provided").arg(sectionCount - 1)); continue; } if (!_typeNamespace.isEmpty()) { reportError(lineNumber, 0, - QString::fromLatin1("only one module identifier directive may be defined in a qmldir file")); + QStringLiteral("only one module identifier directive may be defined in a qmldir file")); continue; } if (!firstLine) { reportError(lineNumber, 0, - QString::fromLatin1("module identifier directive must be the first directive in a qmldir file")); + QStringLiteral("module identifier directive must be the first directive in a qmldir file")); continue; } @@ -173,7 +173,7 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("plugin")) { if (sectionCount < 2 || sectionCount > 3) { reportError(lineNumber, 0, - QString::fromLatin1("plugin directive requires one or two arguments, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("plugin directive requires one or two arguments, but %1 were provided").arg(sectionCount - 1)); continue; } @@ -185,7 +185,7 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("internal")) { if (sectionCount != 3) { reportError(lineNumber, 0, - QString::fromLatin1("internal types require 2 arguments, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("internal types require 2 arguments, but %1 were provided").arg(sectionCount - 1)); continue; } Component entry(sections[1], sections[2], -1, -1); @@ -194,7 +194,7 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("singleton")) { if (sectionCount < 3 || sectionCount > 4) { reportError(lineNumber, 0, - QString::fromLatin1("singleton types require 2 or 3 arguments, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("singleton types require 2 or 3 arguments, but %1 were provided").arg(sectionCount - 1)); continue; } else if (sectionCount == 3) { // handle qmldir directory listing case where singleton is defined in the following pattern: @@ -218,7 +218,7 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("typeinfo")) { if (sectionCount != 2) { reportError(lineNumber, 0, - QString::fromLatin1("typeinfo requires 1 argument, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("typeinfo requires 1 argument, but %1 were provided").arg(sectionCount - 1)); continue; } #ifdef QT_CREATOR @@ -228,13 +228,13 @@ bool QQmlDirParser::parse(const QString &source) } else if (sections[0] == QLatin1String("designersupported")) { if (sectionCount != 1) - reportError(lineNumber, 0, QString::fromLatin1("designersupported does not expect any argument")); + reportError(lineNumber, 0, QStringLiteral("designersupported does not expect any argument")); else _designerSupported = true; } else if (sections[0] == QLatin1String("depends")) { if (sectionCount != 3) { reportError(lineNumber, 0, - QString::fromLatin1("depends requires 2 arguments, but %1 were provided").arg(sectionCount - 1)); + QStringLiteral("depends requires 2 arguments, but %1 were provided").arg(sectionCount - 1)); continue; } @@ -268,7 +268,7 @@ bool QQmlDirParser::parse(const QString &source) } } else { reportError(lineNumber, 0, - QString::fromLatin1("a component declaration requires two or three arguments, but %1 were provided").arg(sectionCount)); + QStringLiteral("a component declaration requires two or three arguments, but %1 were provided").arg(sectionCount)); } firstLine = false; @@ -304,7 +304,9 @@ QList<QQmlError> QQmlDirParser::errors(const QString &uri) const { QUrl url(uri); QList<QQmlError> errors; - for (int i = 0; i < _errors.size(); ++i) { + const int numErrors = _errors.size(); + errors.reserve(numErrors); + for (int i = 0; i < numErrors; ++i) { const QQmlJS::DiagnosticMessage &msg = _errors.at(i); QQmlError e; QString description = msg.message; @@ -362,14 +364,14 @@ bool QQmlDirParser::designerSupported() const QDebug &operator<< (QDebug &debug, const QQmlDirParser::Component &component) { - const QString output = QString::fromLatin1("{%1 %2.%3}"). + const QString output = QStringLiteral("{%1 %2.%3}"). arg(component.typeName).arg(component.majorVersion).arg(component.minorVersion); return debug << qPrintable(output); } QDebug &operator<< (QDebug &debug, const QQmlDirParser::Script &script) { - const QString output = QString::fromLatin1("{%1 %2.%3}"). + const QString output = QStringLiteral("{%1 %2.%3}"). arg(script.nameSpace).arg(script.majorVersion).arg(script.minorVersion); return debug << qPrintable(output); } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index ffc890a2cf..9c5e48ae32 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -42,7 +42,6 @@ #include "qqmlexpression.h" #include "qqmlcomponent.h" #include "qqmlvme_p.h" -#include <private/qqmlenginedebugservice_p.h> #include "qqmlstringconverters_p.h" #include "qqmlxmlhttprequest_p.h" #include "qqmlscriptstring.h" @@ -54,11 +53,7 @@ #include "qqmllist_p.h" #include "qqmltypenamecache_p.h" #include "qqmlnotifier_p.h" -#include <private/qqmldebugserver_p.h> -#include <private/qqmlprofilerservice_p.h> -#include <private/qv4debugservice_p.h> -#include <private/qdebugmessageservice_p.h> -#include <private/qqmlenginecontrolservice_p.h> +#include <private/qqmldebugconnector_p.h> #include "qqmlincubator.h" #include "qqmlabstracturlinterceptor.h" #include <private/qqmlboundsignal_p.h> @@ -225,7 +220,6 @@ void QQmlEnginePrivate::activateDesignerMode() /*! \class QQmlImageProviderBase \brief The QQmlImageProviderBase class is used to register image providers in the QML engine. - \mainclass \inmodule QtQml Image providers must be registered with the QML engine. The only information the QML @@ -247,6 +241,10 @@ void QQmlEnginePrivate::activateDesignerMode() The QQuickImageProvider::requestPixmap() method will be called for all image requests. \value Texture The Image Provider provides QSGTextureProvider based images. The QQuickImageProvider::requestTexture() method will be called for all image requests. + \value ImageResponse The Image provider provides QQuickTextureFactory based images. + Should only be used in QQuickAsyncImageProvider or its subclasses. + The QQuickAsyncImageProvider::requestImageResponse() method will be called for all image requests. + Since Qt 5.6 \omitvalue Invalid */ @@ -594,7 +592,7 @@ the same object as is returned from the Qt.include() call. // Qt.include() is implemented in qv4include.cpp QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) -: propertyCapture(0), rootContext(0), isDebugging(false), +: propertyCapture(0), rootContext(0), profiler(0), outputWarningsToMsgLog(true), cleanup(0), erroredBindings(0), inProgressCreations(0), workerScriptEngine(0), @@ -607,8 +605,8 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) QQmlEnginePrivate::~QQmlEnginePrivate() { - typedef QHash<QPair<QQmlType *, int>, QQmlPropertyCache *>::Iterator TypePropertyCacheIt; - typedef QHash<int, QQmlCompiledData *>::Iterator CompositeTypesIt; + typedef QHash<QPair<QQmlType *, int>, QQmlPropertyCache *>::const_iterator TypePropertyCacheIt; + typedef QHash<int, QQmlCompiledData *>::const_iterator CompositeTypesIt; if (inProgressCreations) qWarning() << QQmlEngine::tr("There are still \"%1\" items in the process of being created at engine destruction.").arg(inProgressCreations); @@ -627,9 +625,9 @@ QQmlEnginePrivate::~QQmlEnginePrivate() if (incubationController) incubationController->d = 0; incubationController = 0; - for (TypePropertyCacheIt iter = typePropertyCache.begin(), end = typePropertyCache.end(); iter != end; ++iter) + for (TypePropertyCacheIt iter = typePropertyCache.cbegin(), end = typePropertyCache.cend(); iter != end; ++iter) (*iter)->release(); - for (CompositeTypesIt iter = m_compositeTypes.begin(), end = m_compositeTypes.end(); iter != end; ++iter) { + for (CompositeTypesIt iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) { iter.value()->isRegisteredWithEngine = false; // since unregisterInternalCompositeType() will not be called in this @@ -666,6 +664,17 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) } } +QQmlData::QQmlData() + : ownedByQml1(false), ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false), + hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), + hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(0), notifyList(0), context(0), outerContext(0), + bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), + lineNumber(0), columnNumber(0), jsEngineId(0), compiledData(0), deferredData(0), + propertyCache(0), guards(0), extendedData(0) +{ + init(); +} + void QQmlData::destroyed(QAbstractDeclarativeData *d, QObject *o) { QQmlData *ddata = static_cast<QQmlData *>(d); @@ -799,7 +808,7 @@ void QQmlData::markAsDeleted(QObject *o) QQmlData::setQueuedForDeletion(o); QObjectPrivate *p = QObjectPrivate::get(o); - for (QList<QObject *>::iterator it = p->children.begin(), end = p->children.end(); it != end; ++it) { + for (QList<QObject *>::const_iterator it = p->children.constBegin(), end = p->children.constEnd(); it != end; ++it) { QQmlData::markAsDeleted(*it); } } @@ -824,14 +833,12 @@ void QQmlData::flushPendingBindingImpl(int coreIndex) // Find the binding QQmlAbstractBinding *b = bindings; - while (b && *b->m_mePtr && b->propertyIndex() != coreIndex) + while (b && b->targetPropertyIndex() != coreIndex) b = b->nextBinding(); - if (b && b->propertyIndex() == coreIndex) { - b->clear(); + if (b && b->targetPropertyIndex() == coreIndex) b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); - } } bool QQmlEnginePrivate::baseModulesUninitialized = true; @@ -861,15 +868,9 @@ void QQmlEnginePrivate::init() rootContext = new QQmlContext(q,true); - if (QCoreApplication::instance()->thread() == q->thread() && - QQmlEngineDebugService::isDebuggingEnabled()) { - isDebugging = true; - QQmlEngineDebugService::instance(); - QV4DebugService::instance(); - QQmlProfilerService::instance(); - QDebugMessageService::instance(); - QQmlEngineControlService::instance(); - QQmlDebugServer::instance()->addEngine(q); + if (QCoreApplication::instance()->thread() == q->thread() && QQmlDebugConnector::instance()) { + QQmlDebugConnector::instance()->open(); + QQmlDebugConnector::instance()->addEngine(q); } } @@ -886,7 +887,6 @@ QQuickWorkerScriptEngine *QQmlEnginePrivate::getWorkerScriptEngine() \since 5.0 \inmodule QtQml \brief The QQmlEngine class provides an environment for instantiating QML components. - \mainclass Each QML component is instantiated in a QQmlContext. QQmlContext's are essential for passing data to QML @@ -947,8 +947,9 @@ QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent) QQmlEngine::~QQmlEngine() { Q_D(QQmlEngine); - if (d->isDebugging) - QQmlDebugServer::instance()->removeEngine(this); + QQmlDebugConnector *server = QQmlDebugConnector::instance(); + if (server) + server->removeEngine(this); d->typeLoader.invalidate(); @@ -1492,51 +1493,6 @@ Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *o #endif // QT_DEPRECATED_SINCE(5, 1) -QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning) -{ -#ifndef QQML_NO_DEBUG_PROTOCOL - if (!QQmlEnginePrivate::qml_debugging_enabled - && printWarning) { - qDebug("QML debugging is enabled. Only use this in a safe environment."); - } - QQmlEnginePrivate::qml_debugging_enabled = true; -#else - Q_UNUSED(printWarning); -#endif -} - -/*! - * \enum QQmlDebuggingEnabler::StartMode - * - * Defines the debug server's start behavior. You can interrupt QML engines starting while a debug - * client is connecting, in order to set breakpoints in or profile startup code. - * - * \value DoNotWaitForClient Run any QML engines as usual while the debug services are connecting. - * \value WaitForClient If a QML engine starts while the debug services are connecting, - * interrupt it until they are done. - */ - -/*! - * Enables debugging for QML engines created after calling this function. The debug server will - * listen on \a port at \a hostName and block the QML engine until it receives a connection if - * \a mode is \c WaitForClient. If \a mode is not specified it won't block and if \a hostName is not - * specified it will listen on all available interfaces. You can only start one debug server at a - * time. A debug server may have already been started if the -qmljsdebugger= command line argument - * was given. This method returns \c true if a new debug server was successfully started, or - * \c false otherwise. - */ -bool QQmlDebuggingEnabler::startTcpDebugServer(int port, StartMode mode, const QString &hostName) -{ -#ifndef QQML_NO_DEBUG_PROTOCOL - return QQmlDebugServer::enable(port, port, mode == WaitForClient, hostName); -#else - Q_UNUSED(port); - Q_UNUSED(block); - Q_UNUSED(hostName); - return false; -#endif -} - class QQmlDataExtended { public: QQmlDataExtended(); @@ -1678,12 +1634,11 @@ void QQmlData::destroyed(QObject *object) QQmlAbstractBinding *binding = bindings; while (binding) { - QQmlAbstractBinding *next = binding->nextBinding(); binding->setAddedToObject(false); - binding->setNextBinding(0); - binding->destroy(); - binding = next; + binding = binding->nextBinding(); } + if (bindings && !bindings->ref.deref()) + delete bindings; if (compiledData) { compiledData->release(); @@ -1696,9 +1651,9 @@ void QQmlData::destroyed(QObject *object) deferredData = 0; } - QQmlAbstractBoundSignal *signalHandler = signalHandlers; + QQmlBoundSignal *signalHandler = signalHandlers; while (signalHandler) { - if (signalHandler->isEvaluating()) { + if (signalHandler->isNotifying()) { // The object is being deleted during signal handler evaluation. // This will cause a crash due to invalid memory access when the // evaluation has completed. @@ -1710,7 +1665,7 @@ void QQmlData::destroyed(QObject *object) if (location.sourceFile.isEmpty()) location.sourceFile = QStringLiteral("<Unknown File>"); locationString.append(location.sourceFile); - locationString.append(QString::fromLatin1(":%0: ").arg(location.line)); + locationString.append(QStringLiteral(":%0: ").arg(location.line)); QString source = expr->expression(); if (source.size() > 100) { source.truncate(96); @@ -1727,7 +1682,7 @@ void QQmlData::destroyed(QObject *object) "%s", object, qPrintable(locationString)); } - QQmlAbstractBoundSignal *next = signalHandler->m_nextSignal; + QQmlBoundSignal *next = signalHandler->m_nextSignal; signalHandler->m_prevSignal = 0; signalHandler->m_nextSignal = 0; delete signalHandler; @@ -2140,8 +2095,7 @@ QString QQmlEngine::offlineStoragePath() const return d->offlineStoragePath; } -QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersion, - QQmlError &error) +QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersion) { QList<QQmlType *> types; @@ -2203,10 +2157,10 @@ QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersi // Properties override: // * other elements of the same name +#if 0 bool overloadError = false; QString overloadName; -#if 0 for (QQmlPropertyCache::StringCache::ConstIterator iter = raw->stringCache.begin(); !overloadError && iter != raw->stringCache.end(); ++iter) { @@ -2223,7 +2177,6 @@ QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersi overloadError = true; } } -#endif if (overloadError) { if (hasCopied) raw->release(); @@ -2231,6 +2184,7 @@ QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersi error.setDescription(QLatin1String("Type ") + type->qmlTypeName() + QLatin1Char(' ') + QString::number(type->majorVersion()) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1String(" contains an illegal property \"") + overloadName + QLatin1String("\". This is an error in the type's implementation.")); return 0; } +#endif if (!hasCopied) raw->addref(); typePropertyCache.insert(qMakePair(type, minorVersion), raw); @@ -2281,8 +2235,8 @@ bool QQmlEnginePrivate::isList(int t) const int QQmlEnginePrivate::listType(int t) const { Locker locker(this); - QHash<int, int>::ConstIterator iter = m_qmlLists.find(t); - if (iter != m_qmlLists.end()) + QHash<int, int>::ConstIterator iter = m_qmlLists.constFind(t); + if (iter != m_qmlLists.cend()) return *iter; else return QQmlMetaType::listType(t); @@ -2291,8 +2245,8 @@ int QQmlEnginePrivate::listType(int t) const QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const { Locker locker(this); - QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.find(t); - if (iter != m_compositeTypes.end()) { + QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.constFind(t); + if (iter != m_compositeTypes.cend()) { return QQmlMetaObject((*iter)->rootPropertyCache); } else { QQmlType *type = QQmlMetaType::qmlType(t); @@ -2303,8 +2257,8 @@ QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const { Locker locker(this); - QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.find(t); - if (iter != m_compositeTypes.end()) { + QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.constFind(t); + if (iter != m_compositeTypes.cend()) { return QQmlMetaObject((*iter)->rootPropertyCache); } else { QQmlType *type = QQmlMetaType::qmlType(t); @@ -2315,8 +2269,8 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) { Locker locker(this); - QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.find(t); - if (iter != m_compositeTypes.end()) { + QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constFind(t); + if (iter != m_compositeTypes.cend()) { return (*iter)->rootPropertyCache; } else { QQmlType *type = QQmlMetaType::qmlType(t); @@ -2328,8 +2282,8 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) { Locker locker(this); - QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.find(t); - if (iter != m_compositeTypes.end()) { + QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constFind(t); + if (iter != m_compositeTypes.cend()) { return (*iter)->rootPropertyCache; } else { QQmlType *type = QQmlMetaType::qmlType(t); diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index df673c1fd5..61a884279d 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -52,7 +52,9 @@ public: Image, Pixmap, Texture, - Invalid + Invalid, + ImageResponse + // ### Qt6: reorder these, and give Invalid a fixed large value }; enum Flag { diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index f1fbad3cf8..26ee3bd655 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -85,7 +85,6 @@ class QQmlImportDatabase; class QNetworkReply; class QNetworkAccessManager; class QQmlNetworkAccessManagerFactory; -class QQmlAbstractBinding; class QQmlTypeNameCache; class QQmlComponentAttached; class QQmlCleanup; @@ -95,6 +94,7 @@ class QQmlObjectCreator; class QDir; class QQmlIncubator; class QQmlProfiler; +class QQmlPropertyCapture; // This needs to be declared here so that the pool for it can live in QQmlEnginePrivate. // The inline method definitions are in qqmljavascriptexpression_p.h @@ -123,21 +123,11 @@ public: // is just qmlClearTypeRegistrations (which can't be called while an engine exists) static bool baseModulesUninitialized; - class PropertyCapture { - public: - inline virtual ~PropertyCapture() {} - virtual void captureProperty(QQmlNotifier *) = 0; - virtual void captureProperty(QObject *, int, int) = 0; - }; - - PropertyCapture *propertyCapture; - inline void captureProperty(QQmlNotifier *); - inline void captureProperty(QObject *, int, int); + QQmlPropertyCapture *propertyCapture; QRecyclePool<QQmlJavaScriptExpressionGuard> jsExpressionGuardPool; QQmlContext *rootContext; - bool isDebugging; QQmlProfiler *profiler; void enableProfiler(); @@ -208,7 +198,7 @@ public: inline static void deleteInEngineThread(QQmlEngine *, T *); // These methods may be called from the loader thread - inline QQmlPropertyCache *cache(QQmlType *, int, QQmlError &error); + inline QQmlPropertyCache *cache(QQmlType *, int); using QJSEnginePrivate::cache; // These methods may be called from the loader thread @@ -262,7 +252,7 @@ public: private: // Must be called locked - QQmlPropertyCache *createCache(QQmlType *, int, QQmlError &error); + QQmlPropertyCache *createCache(QQmlType *, int); // These members must be protected by a QQmlEnginePrivate::Locker as they are required by // the threaded loader. Only access them through their respective accessor methods. @@ -346,7 +336,7 @@ Returns a QQmlPropertyCache for \a type with \a minorVersion. The returned cache is not referenced, so if it is to be stored, call addref(). */ -QQmlPropertyCache *QQmlEnginePrivate::cache(QQmlType *type, int minorVersion, QQmlError &error) +QQmlPropertyCache *QQmlEnginePrivate::cache(QQmlType *type, int minorVersion) { Q_ASSERT(type); @@ -355,7 +345,7 @@ QQmlPropertyCache *QQmlEnginePrivate::cache(QQmlType *type, int minorVersion, QQ Locker locker(this); QQmlPropertyCache *rv = typePropertyCache.value(qMakePair(type, minorVersion)); - if (!rv) rv = createCache(type, minorVersion, error); + if (!rv) rv = createCache(type, minorVersion); return rv; } @@ -414,18 +404,6 @@ QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e) return get(qmlEngine); } -void QQmlEnginePrivate::captureProperty(QQmlNotifier *n) -{ - if (propertyCapture) - propertyCapture->captureProperty(n); -} - -void QQmlEnginePrivate::captureProperty(QObject *o, int c, int n) -{ - if (propertyCapture) - propertyCapture->captureProperty(o, c, n); -} - void QQmlEnginePrivate::setDebugChangesCache(const QHash<QUrl, QByteArray> &changes) { Locker locker(this); diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 35e0bc8c64..332b99ee8f 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -45,13 +45,8 @@ QT_BEGIN_NAMESPACE -static QQmlJavaScriptExpression::VTable QQmlExpressionPrivate_jsvtable = { - QQmlExpressionPrivate::expressionIdentifier, - QQmlExpressionPrivate::expressionChanged -}; - QQmlExpressionPrivate::QQmlExpressionPrivate() -: QQmlJavaScriptExpression(&QQmlExpressionPrivate_jsvtable), +: QQmlJavaScriptExpression(), expressionFunctionValid(true), line(0), column(0) { @@ -65,7 +60,7 @@ void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QOb { expression = expr; - QQmlAbstractExpression::setContext(ctxt); + QQmlJavaScriptExpression::setContext(ctxt); setScopeObject(me); expressionFunctionValid = false; } @@ -74,9 +69,9 @@ void QQmlExpressionPrivate::init(QQmlContextData *ctxt, QV4::Function *runtimeFu { expressionFunctionValid = true; QV4::ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(ctxt->engine); - function.set(engine, QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxt, me, runtimeFunction)); + m_function.set(engine, QV4::QmlBindingWrapper::createQmlCallableForFunction(ctxt, me, runtimeFunction)); - QQmlAbstractExpression::setContext(ctxt); + QQmlJavaScriptExpression::setContext(ctxt); setScopeObject(me); } @@ -246,18 +241,12 @@ void QQmlExpression::setExpression(const QString &expression) // Must be called with a valid handle scope QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined) { - Q_Q(QQmlExpression); - - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(q->engine())->v4engine(); - if (!expressionFunctionValid) { - function.set(v4, qmlBinding(context(), scopeObject(), expression, url, line, &qmlscope)); + createQmlBinding(context(), scopeObject(), expression, url, line); expressionFunctionValid = true; } - QV4::Scope scope(v4); - QV4::ScopedValue f(scope, function.value()); - return evaluate(context(), f, isUndefined); + return evaluate(isUndefined); } QVariant QQmlExpressionPrivate::value(bool *isUndefined) @@ -432,22 +421,15 @@ QQmlError QQmlExpression::error() const calling QQmlExpression::evaluate()) before this signal will be emitted. */ -void QQmlExpressionPrivate::expressionChanged(QQmlJavaScriptExpression *e) -{ - QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e); - This->expressionChanged(); -} - void QQmlExpressionPrivate::expressionChanged() { Q_Q(QQmlExpression); emit q->valueChanged(); } -QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e) +QString QQmlExpressionPrivate::expressionIdentifier() { - QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e); - return QLatin1Char('"') + This->expression + QLatin1Char('"'); + return QLatin1Char('"') + expression + QLatin1Char('"'); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index d8da387878..2303539194 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -52,7 +52,6 @@ #include <private/qflagpointer_p.h> #include <private/qdeletewatcher_p.h> #include <private/qpointervaluepair_p.h> -#include <private/qqmlabstractexpression_p.h> #include <private/qqmljavascriptexpression_p.h> QT_BEGIN_NAMESPACE @@ -60,8 +59,7 @@ QT_BEGIN_NAMESPACE class QQmlExpression; class QString; class QQmlExpressionPrivate : public QObjectPrivate, - public QQmlJavaScriptExpression, - public QQmlAbstractExpression + public QQmlJavaScriptExpression { Q_DECLARE_PUBLIC(QQmlExpression) public: @@ -82,16 +80,12 @@ public: bool expressionFunctionValid:1; - // "Inherited" from QQmlJavaScriptExpression - static QString expressionIdentifier(QQmlJavaScriptExpression *); - static void expressionChanged(QQmlJavaScriptExpression *); + // Inherited from QQmlJavaScriptExpression + virtual QString expressionIdentifier(); virtual void expressionChanged(); QString expression; - QV4::PersistentValue qmlscope; - QV4::PersistentValue function; - QString url; // This is a QString for a reason. QUrls are slooooooow... quint16 line; quint16 column; diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index d904242f93..0a2f4079c2 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -359,7 +359,7 @@ QObject *QQmlGuiProvider::inputMethod() { // We don't have any input method code by default QObject *o = new QObject(); - o->setObjectName(QString::fromLatin1("No inputMethod available")); + o->setObjectName(QStringLiteral("No inputMethod available")); QQmlEngine::setObjectOwnership(o, QQmlEngine::JavaScriptOwnership); return o; } @@ -368,7 +368,7 @@ QObject *QQmlGuiProvider::inputMethod() QObject *QQmlGuiProvider::styleHints() { QObject *o = new QObject(); - o->setObjectName(QString::fromLatin1("No styleHints available")); + o->setObjectName(QStringLiteral("No styleHints available")); QQmlEngine::setObjectOwnership(o, QQmlEngine::JavaScriptOwnership); return o; } diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index ff48a10d95..b17bb0455d 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -169,6 +169,7 @@ QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeNa } // namespace +#ifndef QT_NO_LIBRARY struct RegisteredPlugin { QString uri; QPluginLoader* loader; @@ -193,6 +194,7 @@ void qmlClearEnginePlugins() } typedef QPair<QStaticPlugin, QJsonArray> StaticPluginPair; +#endif class QQmlImportNamespace { @@ -292,9 +294,10 @@ public: const QString &uri, const QString &url, int vmaj, int vmin, QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, bool lowPrecedence = false); - +#ifndef QT_NO_LIBRARY bool populatePluginPairVector(QVector<StaticPluginPair> &result, const QString &uri, const QString &qmldirPath, QList<QQmlError> *errors); +#endif }; /*! @@ -382,7 +385,7 @@ void QQmlImports::populateCache(QQmlTypeNameCache *cache) const // We need to exclude the entry for the current baseUrl. This can happen for example // when handling qmldir files on the remote dir case and the current type is marked as // singleton. -bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString baseUrl) +bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QString &baseUrl) { if (importUrl.isEmpty()) return false; @@ -398,7 +401,7 @@ bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QSt return true; } -void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports::CompositeSingletonReference> &resultList, QUrl baseUrl) +void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports::CompositeSingletonReference> &resultList, const QUrl &baseUrl) { typedef QQmlDirComponents::const_iterator ConstIterator; @@ -826,6 +829,7 @@ QQmlImportNamespace *QQmlImportsPrivate::findQualifiedNamespace(const QHashedStr } +#ifndef QT_NO_LIBRARY /*! Get all static plugins that are QML plugins and has a meta data URI that begins with \a uri. Note that if e.g uri == "a", and different plugins have meta data "a", "a.2.1", "a.b.c", all @@ -869,6 +873,7 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res } return true; } +#endif /*! Import an extension defined by a qmldir file. @@ -995,6 +1000,13 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, } #else + Q_UNUSED(qmldirFilePath); + Q_UNUSED(uri); + Q_UNUSED(vmaj); + Q_UNUSED(vmin); + Q_UNUSED(database); + Q_UNUSED(qmldir); + Q_UNUSED(errors); return false; #endif // QT_NO_LIBRARY return true; @@ -1931,6 +1943,12 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba return true; #else + Q_UNUSED(instance); + Q_UNUSED(basePath); + Q_UNUSED(uri); + Q_UNUSED(typeNamespace); + Q_UNUSED(vmaj); + Q_UNUSED(errors); return false; #endif } @@ -2011,6 +2029,11 @@ bool QQmlImportDatabase::importDynamicPlugin(const QString &filePath, const QStr return true; #else + Q_UNUSED(filePath); + Q_UNUSED(uri); + Q_UNUSED(typeNamespace); + Q_UNUSED(vmaj); + Q_UNUSED(errors); return false; #endif } diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 02bd1c4b83..5f30eac066 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -35,7 +35,7 @@ #include <private/qqmlexpression_p.h> #include <private/qqmlcontextwrapper_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4errorobject_p.h> @@ -83,13 +83,22 @@ void QQmlDelayedError::catchJavaScriptException(QV4::ExecutionEngine *engine) } -QQmlJavaScriptExpression::QQmlJavaScriptExpression(VTable *v) -: m_vtable(v) +QQmlJavaScriptExpression::QQmlJavaScriptExpression() + : m_error(0), + m_context(0), + m_prevExpression(0), + m_nextExpression(0) { } QQmlJavaScriptExpression::~QQmlJavaScriptExpression() { + if (m_prevExpression) { + *m_prevExpression = m_nextExpression; + if (m_nextExpression) + m_nextExpression->m_prevExpression = m_prevExpression; + } + clearGuards(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = 0; @@ -106,40 +115,62 @@ void QQmlJavaScriptExpression::resetNotifyOnValueChanged() clearGuards(); } -QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, - const QV4::Value &function, bool *isUndefined) +void QQmlJavaScriptExpression::setContext(QQmlContextData *context) { - QV4::ExecutionEngine *v4 = QV8Engine::getV4(context->engine); + if (m_prevExpression) { + *m_prevExpression = m_nextExpression; + if (m_nextExpression) + m_nextExpression->m_prevExpression = m_prevExpression; + m_prevExpression = 0; + m_nextExpression = 0; + } + + m_context = context; + + if (context) { + m_nextExpression = context->expressions; + if (m_nextExpression) + m_nextExpression->m_prevExpression = &m_nextExpression; + m_prevExpression = &context->expressions; + context->expressions = this; + } +} + +void QQmlJavaScriptExpression::refresh() +{ +} + +QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(bool *isUndefined) +{ + QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_context->engine); QV4::Scope scope(v4); QV4::ScopedCallData callData(scope); - return evaluate(context, function, callData, isUndefined); + return evaluate(callData, isUndefined); } -QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, - const QV4::Value &function, - QV4::CallData *callData, - bool *isUndefined) +QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined) { - Q_ASSERT(context && context->engine); + Q_ASSERT(m_context && m_context->engine); - if (function.isUndefined()) { + QV4::Value *f = m_function.valueRef(); + if (!f || f->isUndefined()) { if (isUndefined) *isUndefined = true; return QV4::Encode::undefined(); } - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_context->engine); // All code that follows must check with watcher before it accesses data members // incase we have been deleted. DeleteWatcher watcher(this); Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty()); - GuardCapture capture(context->engine, this, &watcher); + QQmlPropertyCapture capture(m_context->engine, this, &watcher); - QQmlEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture; - ep->propertyCapture = notifyOnValueChanged()?&capture:0; + QQmlPropertyCapture *lastPropertyCapture = ep->propertyCapture; + ep->propertyCapture = notifyOnValueChanged() ? &capture : 0; if (notifyOnValueChanged()) @@ -148,14 +179,14 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); QV4::Scope scope(v4); QV4::ScopedValue result(scope, QV4::Primitive::undefinedValue()); - callData->thisObject = v4->globalObject(); + callData->thisObject = v4->globalObject; if (scopeObject()) { QV4::ScopedValue value(scope, QV4::QObjectWrapper::wrap(v4, scopeObject())); if (value->isObject()) callData->thisObject = value; } - result = function.asFunctionObject()->call(callData); + result = f->as<QV4::FunctionObject>()->call(callData); if (scope.hasException()) { if (watcher.wasDeleted()) scope.engine->catchException(); // ignore exception @@ -178,7 +209,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, capture.errorString = 0; } - while (Guard *g = capture.guards.takeFirst()) + while (QQmlJavaScriptExpressionGuard *g = capture.guards.takeFirst()) g->Delete(); ep->propertyCapture = lastPropertyCapture; @@ -186,7 +217,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QQmlContextData *context, return result->asReturnedValue(); } -void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n) +void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) { if (watcher->wasDeleted()) return; @@ -196,13 +227,13 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n) while (!guards.isEmpty() && !guards.first()->isConnected(n)) guards.takeFirst()->Delete(); - Guard *g = 0; + QQmlJavaScriptExpressionGuard *g = 0; if (!guards.isEmpty()) { g = guards.takeFirst(); g->cancelNotify(); Q_ASSERT(g->isConnected(n)); } else { - g = Guard::New(expression, engine); + g = QQmlJavaScriptExpressionGuard::New(expression, engine); g->connect(n); } @@ -213,7 +244,7 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n) \a n is in the signal index range (see QObjectPrivate::signalIndex()). */ -void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n) +void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n) { if (watcher->wasDeleted()) return; @@ -223,7 +254,7 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, if (!errorString) { errorString = new QStringList; QString preamble = QLatin1String("QQmlExpression: Expression ") + - expression->m_vtable->expressionIdentifier(expression) + + expression->expressionIdentifier() + QLatin1String(" depends on non-NOTIFYable properties:"); errorString->append(preamble); } @@ -242,13 +273,13 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, while (!guards.isEmpty() && !guards.first()->isConnected(o, n)) guards.takeFirst()->Delete(); - Guard *g = 0; + QQmlJavaScriptExpressionGuard *g = 0; if (!guards.isEmpty()) { g = guards.takeFirst(); g->cancelNotify(); Q_ASSERT(g->isConnected(o, n)); } else { - g = Guard::New(expression, engine); + g = QQmlJavaScriptExpressionGuard::New(expression, engine); g->connect(o, n, engine); } @@ -258,31 +289,31 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, void QQmlJavaScriptExpression::clearError() { - if (m_vtable.hasValue()) { - m_vtable.value().clearError(); - m_vtable.value().removeError(); - } + if (m_error) + delete m_error; + m_error = 0; } QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const { Q_UNUSED(engine); - if (m_vtable.hasValue()) - return m_vtable.constValue()->error(); + if (m_error) + return m_error->error(); else return QQmlError(); } QQmlDelayedError *QQmlJavaScriptExpression::delayedError() { - return &m_vtable.value(); + if (!m_error) + m_error = new QQmlDelayedError; + return m_error; } QV4::ReturnedValue QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scopeObject, - const QString &code, const QString &filename, quint16 line, - QV4::PersistentValue *qmlscope) + const QString &code, const QString &filename, quint16 line) { QQmlEngine *engine = ctxt->engine; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); @@ -308,14 +339,11 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scopeObje ep->warning(error); return QV4::Encode::undefined(); } - if (qmlscope) - qmlscope->set(v4, qmlScopeObject); return result->asReturnedValue(); } -QV4::ReturnedValue QQmlJavaScriptExpression::qmlBinding(QQmlContextData *ctxt, QObject *qmlScope, - const QString &code, const QString &filename, quint16 line, - QV4::PersistentValue *qmlscope) +void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject *qmlScope, + const QString &code, const QString &filename, quint16 line) { QQmlEngine *engine = ctxt->engine; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); @@ -339,17 +367,15 @@ QV4::ReturnedValue QQmlJavaScriptExpression::qmlBinding(QQmlContextData *ctxt, Q error.setUrl(QUrl::fromLocalFile(filename)); error.setObject(qmlScope); ep->warning(error); - return QV4::Encode::undefined(); + result = QV4::Encode::undefined(); } - if (qmlscope) - qmlscope->set(v4, qmlScopeObject); - return result->asReturnedValue(); + m_function.set(v4, result); } void QQmlJavaScriptExpression::clearGuards() { - while (Guard *g = activeGuards.takeFirst()) + while (QQmlJavaScriptExpressionGuard *g = activeGuards.takeFirst()) g->Delete(); } @@ -358,7 +384,7 @@ void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **) QQmlJavaScriptExpression *expression = static_cast<QQmlJavaScriptExpressionGuard *>(e)->expression; - expression->m_vtable->expressionChanged(expression); + expression->expressionChanged(); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 989d5a0b0d..dfcf8b2d68 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -89,23 +89,17 @@ private: QQmlDelayedError **prevError; }; -class QQmlJavaScriptExpression +class Q_QML_PRIVATE_EXPORT QQmlJavaScriptExpression { public: - // Although this looks crazy, we implement our own "vtable" here, rather than relying on - // C++ virtuals, to save memory. By doing it ourselves, we can overload the storage - // location that is use for the vtable to also store the rarely used delayed error. - // If we use C++ virtuals, we can't do this and it consts us an extra sizeof(void *) in - // memory for every expression. - struct VTable { - QString (*expressionIdentifier)(QQmlJavaScriptExpression *); - void (*expressionChanged)(QQmlJavaScriptExpression *); - }; + QQmlJavaScriptExpression(); + virtual ~QQmlJavaScriptExpression(); - QQmlJavaScriptExpression(VTable *vtable); + virtual QString expressionIdentifier() = 0; + virtual void expressionChanged() = 0; - QV4::ReturnedValue evaluate(QQmlContextData *, const QV4::Value &function, bool *isUndefined); - QV4::ReturnedValue evaluate(QQmlContextData *, const QV4::Value &function, QV4::CallData *callData, bool *isUndefined); + QV4::ReturnedValue evaluate(bool *isUndefined); + QV4::ReturnedValue evaluate(QV4::CallData *callData, bool *isUndefined); inline bool notifyOnValueChanged() const; @@ -115,6 +109,13 @@ public: inline QObject *scopeObject() const; inline void setScopeObject(QObject *v); + bool isValid() const { return context() != 0; } + + QQmlContextData *context() const { return m_context; } + void setContext(QQmlContextData *context); + + virtual void refresh(); + class DeleteWatcher { public: inline DeleteWatcher(QQmlJavaScriptExpression *); @@ -136,46 +137,50 @@ public: static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, - quint16 line, - QV4::PersistentValue *qmlscope = 0); - // doesn't require rewriting the expression - static QV4::ReturnedValue qmlBinding(QQmlContextData *ctxt, QObject *scope, - const QString &code, - const QString &filename, quint16 line, - QV4::PersistentValue *qmlscope = 0); + quint16 line); protected: - ~QQmlJavaScriptExpression(); + void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line); private: - typedef QQmlJavaScriptExpressionGuard Guard; + friend class QQmlContextData; + friend class QQmlPropertyCapture; friend void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **); - struct GuardCapture : public QQmlEnginePrivate::PropertyCapture { - GuardCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, DeleteWatcher *w) - : engine(engine), expression(e), watcher(w), errorString(0) { } - - ~GuardCapture() { - Q_ASSERT(guards.isEmpty()); - Q_ASSERT(errorString == 0); - } - - virtual void captureProperty(QQmlNotifier *); - virtual void captureProperty(QObject *, int, int); - - QQmlEngine *engine; - QQmlJavaScriptExpression *expression; - DeleteWatcher *watcher; - QFieldList<Guard, &Guard::next> guards; - QStringList *errorString; - }; - - QPointerValuePair<VTable, QQmlDelayedError> m_vtable; + QQmlDelayedError *m_error; // We store some flag bits in the following flag pointers. // activeGuards:flag1 - notifyOnValueChanged // activeGuards:flag2 - useSharedContext QBiPointer<QObject, DeleteWatcher> m_scopeObject; - QForwardFieldList<Guard, &Guard::next> activeGuards; + QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards; + + QQmlContextData *m_context; + QQmlJavaScriptExpression **m_prevExpression; + QQmlJavaScriptExpression *m_nextExpression; + +protected: + QV4::PersistentValue m_function; +}; + +class QQmlPropertyCapture +{ +public: + QQmlPropertyCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, QQmlJavaScriptExpression::DeleteWatcher *w) + : engine(engine), expression(e), watcher(w), errorString(0) { } + + ~QQmlPropertyCapture() { + Q_ASSERT(guards.isEmpty()); + Q_ASSERT(errorString == 0); + } + + void captureProperty(QQmlNotifier *); + void captureProperty(QObject *, int, int); + + QQmlEngine *engine; + QQmlJavaScriptExpression *expression; + QQmlJavaScriptExpression::DeleteWatcher *watcher; + QFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> guards; + QStringList *errorString; }; QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e) @@ -222,18 +227,18 @@ void QQmlJavaScriptExpression::setScopeObject(QObject *v) bool QQmlJavaScriptExpression::hasError() const { - return m_vtable.hasValue() && m_vtable.constValue()->isValid(); + return m_error && m_error->isValid(); } bool QQmlJavaScriptExpression::hasDelayedError() const { - return m_vtable.hasValue(); + return m_error; } QQmlJavaScriptExpressionGuard::QQmlJavaScriptExpressionGuard(QQmlJavaScriptExpression *e) -: expression(e), next(0) + : QQmlNotifierEndpoint(QQmlNotifierEndpoint::QQmlJavaScriptExpressionGuard), + expression(e), next(0) { - setCallback(QQmlNotifierEndpoint::QQmlJavaScriptExpressionGuard); } QQmlJavaScriptExpressionGuard * diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index bcb1e72f0b..d09f4df54c 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -92,13 +92,13 @@ QVariant QmlListWrapper::toVariant() const } -ReturnedValue QmlListWrapper::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue QmlListWrapper::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<QmlListWrapper>()); - QmlListWrapper *w = static_cast<QmlListWrapper *>(m); + const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m); QV4::ExecutionEngine *v4 = w->engine(); - if (name->equals(v4->id_length) && !w->d()->object.isNull()) { + if (name->equals(v4->id_length()) && !w->d()->object.isNull()) { quint32 count = w->d()->property.count ? w->d()->property.count(&w->d()->property) : 0; return Primitive::fromUInt32(count).asReturnedValue(); } @@ -110,12 +110,12 @@ ReturnedValue QmlListWrapper::get(Managed *m, String *name, bool *hasProperty) return Object::get(m, name, hasProperty); } -ReturnedValue QmlListWrapper::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue QmlListWrapper::getIndexed(const Managed *m, uint index, bool *hasProperty) { Q_UNUSED(hasProperty); Q_ASSERT(m->as<QmlListWrapper>()); - QmlListWrapper *w = static_cast<QmlListWrapper *>(m); + const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m); QV4::ExecutionEngine *v4 = w->engine(); quint32 count = w->d()->property.count ? w->d()->property.count(&w->d()->property) : 0; @@ -138,9 +138,9 @@ void QmlListWrapper::put(Managed *m, String *name, const Value &value) Q_UNUSED(value); } -void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attrs) +void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { - *name = (Heap::String *)0; + name->setM(0); *index = UINT_MAX; Q_ASSERT(m->as<QmlListWrapper>()); QmlListWrapper *w = static_cast<QmlListWrapper *>(m); diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h index 3590bcb1c9..ff006d4302 100644 --- a/src/qml/qml/qqmllistwrapper_p.h +++ b/src/qml/qml/qqmllistwrapper_p.h @@ -50,7 +50,7 @@ #include <QtQml/qqmllist.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE @@ -81,10 +81,10 @@ struct Q_QML_EXPORT QmlListWrapper : Object QVariant toVariant() const; - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + 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 advanceIterator(Managed *m, ObjectIterator *it, Heap::String **name, uint *index, Property *p, PropertyAttributes *attributes); + static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); }; } diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 62b5b76ede..7f49798da6 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -65,13 +65,13 @@ static bool isLocaleObject(const QV4::Value &val) void QQmlDateExtension::registerExtension(QV4::ExecutionEngine *engine) { - engine->datePrototype.asObject()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); - engine->datePrototype.asObject()->defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString); - engine->datePrototype.asObject()->defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString); - engine->dateCtor.objectValue()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); - engine->dateCtor.objectValue()->defineDefaultProperty(QStringLiteral("fromLocaleTimeString"), method_fromLocaleTimeString); - engine->dateCtor.objectValue()->defineDefaultProperty(QStringLiteral("fromLocaleDateString"), method_fromLocaleDateString); - engine->dateCtor.objectValue()->defineDefaultProperty(QStringLiteral("timeZoneUpdated"), method_timeZoneUpdated); + engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); + engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString); + engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString); + engine->dateCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); + engine->dateCtor()->defineDefaultProperty(QStringLiteral("fromLocaleTimeString"), method_fromLocaleTimeString); + engine->dateCtor()->defineDefaultProperty(QStringLiteral("fromLocaleDateString"), method_fromLocaleDateString); + engine->dateCtor()->defineDefaultProperty(QStringLiteral("timeZoneUpdated"), method_timeZoneUpdated); } QV4::ReturnedValue QQmlDateExtension::method_toLocaleString(QV4::CallContext *ctx) @@ -81,7 +81,7 @@ QV4::ReturnedValue QQmlDateExtension::method_toLocaleString(QV4::CallContext *ct QV4::Scope scope(ctx); - QV4::DateObject *date = ctx->thisObject().asDateObject(); + QV4::DateObject *date = ctx->thisObject().as<DateObject>(); if (!date) return QV4::DatePrototype::method_toLocaleString(ctx); @@ -125,7 +125,7 @@ QV4::ReturnedValue QQmlDateExtension::method_toLocaleTimeString(QV4::CallContext QV4::Scope scope(ctx); - QV4::DateObject *date = ctx->thisObject().asDateObject(); + QV4::DateObject *date = ctx->thisObject().as<DateObject>(); if (!date) return QV4::DatePrototype::method_toLocaleTimeString(ctx); @@ -170,7 +170,7 @@ QV4::ReturnedValue QQmlDateExtension::method_toLocaleDateString(QV4::CallContext QV4::Scope scope(ctx); - QV4::DateObject *dateObj = ctx->thisObject().asDateObject(); + QV4::DateObject *dateObj = ctx->thisObject().as<DateObject>(); if (!dateObj) return QV4::DatePrototype::method_toLocaleDateString(ctx); @@ -347,9 +347,9 @@ QV4::ReturnedValue QQmlDateExtension::method_timeZoneUpdated(QV4::CallContext *c void QQmlNumberExtension::registerExtension(QV4::ExecutionEngine *engine) { - engine->numberPrototype.asObject()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); - engine->numberPrototype.asObject()->defineDefaultProperty(QStringLiteral("toLocaleCurrencyString"), method_toLocaleCurrencyString); - engine->numberCtor.objectValue()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); + engine->numberPrototype()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); + engine->numberPrototype()->defineDefaultProperty(QStringLiteral("toLocaleCurrencyString"), method_toLocaleCurrencyString); + engine->numberCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); } QV4::ReturnedValue QQmlNumberExtension::method_toLocaleString(QV4::CallContext *ctx) @@ -815,15 +815,15 @@ QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale) void QQmlLocale::registerStringLocaleCompare(QV4::ExecutionEngine *engine) { - engine->stringPrototype.asObject()->defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare); + engine->stringPrototype()->defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare); } QV4::ReturnedValue QQmlLocale::method_localeCompare(QV4::CallContext *ctx) { - if (ctx->argc() != 1 || (!ctx->args()[0].isString() && !ctx->args()[0].asStringObject())) + if (ctx->argc() != 1 || (!ctx->args()[0].isString() && !ctx->args()[0].as<StringObject>())) return QV4::StringPrototype::method_localeCompare(ctx); - if (!ctx->thisObject().isString() && !ctx->thisObject().asStringObject()) + if (!ctx->thisObject().isString() && !ctx->thisObject().as<StringObject>()) return QV4::StringPrototype::method_localeCompare(ctx); QString thisString = ctx->thisObject().toQStringNoThrow(); diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h index d4436482cf..cb9fe9bbef 100644 --- a/src/qml/qml/qqmllocale_p.h +++ b/src/qml/qml/qqmllocale_p.h @@ -74,10 +74,6 @@ private: class Q_AUTOTEST_EXPORT QQmlLocale { Q_GADGET - Q_ENUMS(MeasurementSystem) - Q_ENUMS(FormatType) - Q_ENUMS(CurrencySymbolFormat) - Q_ENUMS(DayOfWeek) public: ~QQmlLocale(); @@ -88,16 +84,19 @@ public: ImperialUSSystem = QLocale::ImperialUSSystem, ImperialUKSystem = QLocale::ImperialUKSystem }; + Q_ENUM(MeasurementSystem) enum FormatType { LongFormat = QLocale::LongFormat, ShortFormat = QLocale::ShortFormat, NarrowFormat = QLocale::NarrowFormat }; + Q_ENUM(FormatType) enum CurrencySymbolFormat { CurrencyIsoCode = QLocale::CurrencyIsoCode, CurrencySymbol = QLocale::CurrencySymbol, CurrencyDisplayName = QLocale::CurrencyDisplayName }; + Q_ENUM(CurrencySymbolFormat) // Qt defines Sunday as 7, but JS Date assigns Sunday 0 enum DayOfWeek { Sunday = 0, @@ -108,6 +107,7 @@ public: Friday = Qt::Friday, Saturday = Qt::Saturday }; + Q_ENUM(DayOfWeek) static QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName); static QV4::ReturnedValue wrap(QV4::ExecutionEngine *engine, const QLocale &locale); @@ -137,7 +137,7 @@ struct QQmlLocaleData : public QV4::Object V4_NEEDS_DESTROY static QLocale *getThisLocale(QV4::CallContext *ctx) { - QV4::Object *o = ctx->thisObject().asObject(); + QV4::Object *o = ctx->thisObject().as<Object>(); QQmlLocaleData *thisObject = o ? o->as<QQmlLocaleData>() : 0; if (!thisObject) { ctx->engine()->throwTypeError(); diff --git a/src/qml/qml/qqmlmemoryprofiler.cpp b/src/qml/qml/qqmlmemoryprofiler.cpp index cdd60e2dec..531666340b 100644 --- a/src/qml/qml/qqmlmemoryprofiler.cpp +++ b/src/qml/qml/qqmlmemoryprofiler.cpp @@ -63,11 +63,13 @@ static qmlmemprofile_pop_location *memprofile_pop_location; static qmlmemprofile_save *memprofile_save; static qmlmemprofile_is_enabled *memprofile_is_enabled; +#ifndef QT_NO_LIBRARY extern QFunctionPointer qt_linux_find_symbol_sys(const char *symbol); +#endif static bool openLibrary() { -#ifdef Q_OS_LINUX +#if defined(Q_OS_LINUX) && !defined(QT_NO_LIBRARY) if (state == Unloaded) { memprofile_stats = (qmlmemprofile_stats *) qt_linux_find_symbol_sys("qmlmemprofile_stats"); memprofile_clear = (qmlmemprofile_clear *) qt_linux_find_symbol_sys("qmlmemprofile_clear"); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index cb8c2bd3b5..fbb21f4562 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -258,7 +258,7 @@ QObject *QQmlType::SingletonInstanceInfo::qobjectApi(QQmlEngine *e) const return qobjectApis.value(e); } -void QQmlType::SingletonInstanceInfo::setScriptApi(QQmlEngine *e, QJSValue v) +void QQmlType::SingletonInstanceInfo::setScriptApi(QQmlEngine *e, const QJSValue &v) { scriptApis.insert(e, v); } @@ -1110,7 +1110,9 @@ void qmlClearTypeRegistrations() // Declared in qqml.h data->uriToModule.clear(); QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types +#ifndef QT_NO_LIBRARY qmlClearEnginePlugins(); +#endif } int registerAutoParentFunction(QQmlPrivate::RegisterAutoParent &autoparent) @@ -1440,8 +1442,8 @@ bool QQmlMetaType::isAnyModule(const QString &uri) QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - for (QQmlMetaTypeData::TypeModules::ConstIterator iter = data->uriToModule.begin(); - iter != data->uriToModule.end(); ++iter) { + for (QQmlMetaTypeData::TypeModules::ConstIterator iter = data->uriToModule.cbegin(); + iter != data->uriToModule.cend(); ++iter) { if ((*iter)->module() == uri) return true; } @@ -1718,7 +1720,7 @@ QQmlType *QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStrin QQmlMetaTypeData *data = metaTypeData(); QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(name); - while (it != data->nameToType.end() && it.key() == name) { + while (it != data->nameToType.cend() && it.key() == name) { // XXX version_major<0 just a kludge for QQmlPropertyPrivate::initProperty if (version_major < 0 || module.isEmpty() || (*it)->availableInVersion(module, version_major,version_minor)) return (*it); @@ -1752,7 +1754,7 @@ QQmlType *QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStri QQmlMetaTypeData *data = metaTypeData(); QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.constFind(metaObject); - while (it != data->metaObjectToType.end() && it.key() == metaObject) { + while (it != data->metaObjectToType.cend() && it.key() == metaObject) { QQmlType *t = *it; if (version_major < 0 || module.isEmpty() || t->availableInVersion(module, version_major,version_minor)) return t; @@ -1823,8 +1825,9 @@ QList<QString> QQmlMetaType::qmlTypeNames() QQmlMetaTypeData *data = metaTypeData(); QList<QString> names; - QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.begin(); - while (it != data->nameToType.end()) { + names.reserve(data->nameToType.count()); + QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.cbegin(); + while (it != data->nameToType.cend()) { names += (*it)->qmlTypeName(); ++it; } diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index e5ac20d314..61a6567f1d 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -191,7 +191,7 @@ public: void setQObjectApi(QQmlEngine *, QObject *); QObject *qobjectApi(QQmlEngine *) const; - void setScriptApi(QQmlEngine *, QJSValue); + void setScriptApi(QQmlEngine *, const QJSValue &); QJSValue scriptApi(QQmlEngine *) const; void init(QQmlEngine *); diff --git a/src/qml/qml/qqmlnotifier_p.h b/src/qml/qml/qqmlnotifier_p.h index 2742bfc84b..90902c089d 100644 --- a/src/qml/qml/qqmlnotifier_p.h +++ b/src/qml/qml/qqmlnotifier_p.h @@ -63,9 +63,6 @@ class QQmlNotifierEndpoint QQmlNotifierEndpoint *next; QQmlNotifierEndpoint **prev; public: - inline QQmlNotifierEndpoint(); - inline ~QQmlNotifierEndpoint(); - // QQmlNotifierEndpoint can only invoke one of a set of pre-defined callbacks. // To add another callback, extend this enum and add the callback to the top // of qqmlnotifier.cpp. Four bits are reserved for the callback, so there can @@ -77,7 +74,8 @@ public: QQmlVMEMetaObjectEndpoint = 3 }; - inline void setCallback(Callback c) { callback = c; } + inline QQmlNotifierEndpoint(Callback callback); + inline ~QQmlNotifierEndpoint(); inline bool isConnected(); inline bool isConnected(QObject *source, int sourceSignal); @@ -90,6 +88,8 @@ public: inline bool isNotifying() const; inline void cancelNotify(); + inline int signalIndex() const { return sourceSignal; } + private: friend class QQmlData; friend class QQmlNotifier; @@ -135,8 +135,8 @@ void QQmlNotifier::notify() if (endpoints) emitNotify(endpoints, args); } -QQmlNotifierEndpoint::QQmlNotifierEndpoint() -: next(0), prev(0), senderPtr(0), callback(None), sourceSignal(-1) +QQmlNotifierEndpoint::QQmlNotifierEndpoint(Callback callback) +: next(0), prev(0), senderPtr(0), callback(callback), sourceSignal(-1) { } diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 3c2f3690b9..0b977f2551 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -62,14 +62,6 @@ struct ActiveOCRestorer }; } -static void removeBindingOnProperty(QObject *o, int index) -{ - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); - QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, coreIndex, valueTypeIndex, 0); - if (binding) binding->destroy(); -} - QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlContextData *creationContext, void *activeVMEDataForRootContext) : phase(Startup) , compiledData(compiledData) @@ -137,11 +129,6 @@ QQmlObjectCreator::~QQmlObjectCreator() { QQmlObjectCreatorRecursionWatcher watcher(this); } - for (int i = 0; i < sharedState->allCreatedBindings.count(); ++i) { - QQmlAbstractBinding *b = sharedState->allCreatedBindings.at(i); - if (b) - b->m_mePtr = 0; - } for (int i = 0; i < sharedState->allParserStatusCallbacks.count(); ++i) { QQmlParserStatus *ps = sharedState->allParserStatusCallbacks.at(i); if (ps) @@ -660,15 +647,12 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) // ### this is best done through type-compile-time binding skip lists. if (_valueTypeProperty) { - QQmlAbstractBinding *binding = - QQmlPropertyPrivate::binding(_bindingTarget, _valueTypeProperty->coreIndex, -1); + QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, _valueTypeProperty->coreIndex); - if (binding && binding->bindingType() != QQmlAbstractBinding::ValueTypeProxy) { - QQmlPropertyPrivate::setBinding(_bindingTarget, _valueTypeProperty->coreIndex, -1, 0); - binding->destroy(); + if (binding && !binding->isValueTypeProxy()) { + QQmlPropertyPrivate::removeBinding(_bindingTarget, _valueTypeProperty->coreIndex); } else if (binding) { - QQmlValueTypeProxyBinding *proxy = - static_cast<QQmlValueTypeProxyBinding *>(binding); + QQmlValueTypeProxyBinding *proxy = static_cast<QQmlValueTypeProxyBinding *>(binding); if (qmlTypeForObject(_bindingTarget)) { quint32 bindingSkipList = 0; @@ -731,7 +715,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // ### resolve this at compile time if (property && property->propType == qMetaTypeId<QQmlScriptString>()) { QQmlScriptString ss(binding->valueAsScriptString(qmlUnit), context->asQQmlContext(), _scopeObject); - ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : QQmlBinding::Invalid; + ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line; ss.d.data()->columnNumber = binding->location.column; ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String; @@ -800,7 +784,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con if (_ddata->hasBindingBit(property->coreIndex) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) && !_valueTypeProperty) - removeBindingOnProperty(_bindingTarget, property->coreIndex); + QQmlPropertyPrivate::removeBinding(_bindingTarget, property->coreIndex); if (binding->type == QV4::CompiledData::Binding::Type_Script) { QV4::Function *runtimeFunction = compiledData->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; @@ -828,18 +812,12 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con if (_valueTypeProperty) targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine); - sharedState->allCreatedBindings.push(qmlBinding); - qmlBinding->m_mePtr = &sharedState->allCreatedBindings.top(); + sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); - qmlBinding->setTarget(_bindingTarget, targetCorePropertyData, context); + qmlBinding->setTarget(_bindingTarget, targetCorePropertyData); if (targetCorePropertyData.isAlias()) { - QQmlAbstractBinding *old = - QQmlPropertyPrivate::setBindingNoEnable(_bindingTarget, - targetCorePropertyData.coreIndex, - targetCorePropertyData.getValueTypeCoreIndex(), - qmlBinding); - if (old) { old->destroy(); } + QQmlPropertyPrivate::setBinding(qmlBinding, QQmlPropertyPrivate::DontEnable); } else { qmlBinding->addToObject(); @@ -1011,7 +989,7 @@ QV4::Heap::ExecutionContext *QQmlObjectCreator::currentQmlContext() { if (!_qmlBindingWrapper->objectValue()) { QV4::Scope valueScope(v4); - QV4::ScopedObject qmlScope(valueScope, QV4::QmlContextWrapper::qmlScope(v4, context, _scopeObject)); + QV4::Scoped<QV4::QmlContextWrapper> qmlScope(valueScope, QV4::QmlContextWrapper::qmlScope(v4, context, _scopeObject)); QV4::ScopedContext global(valueScope, v4->rootContext()); *_qmlBindingWrapper = v4->memoryManager->alloc<QV4::QmlBindingWrapper>(global, qmlScope); } @@ -1124,7 +1102,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo QBitArray bindingsToSkip; if (customParser) { - QHash<int, QBitArray>::ConstIterator customParserBindings = compiledData->customParserBindings.find(index); + QHash<int, QBitArray>::ConstIterator customParserBindings = compiledData->customParserBindings.constFind(index); if (customParserBindings != compiledData->customParserBindings.constEnd()) { customParser->imports = compiledData->importCache; @@ -1183,13 +1161,14 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine)); while (!sharedState->allCreatedBindings.isEmpty()) { - QQmlAbstractBinding *b = sharedState->allCreatedBindings.pop(); - if (!b) + QQmlAbstractBinding::Ptr b = sharedState->allCreatedBindings.pop(); + Q_ASSERT(b); + // skip, if b is not added to an object + if (!b->isAddedToObject()) continue; - b->m_mePtr = 0; - QQmlData *data = QQmlData::get(b->object()); + QQmlData *data = QQmlData::get(b->targetObject()); Q_ASSERT(data); - data->clearPendingBindingBit(b->propertyIndex()); + data->clearPendingBindingBit(b->targetPropertyIndex()); b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); @@ -1299,7 +1278,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * QBitArray bindingSkipList = bindingsToSkip; { - QHash<int, QBitArray>::ConstIterator deferredBindings = compiledData->deferredBindingsPerObject.find(_compiledObjectIndex); + QHash<int, QBitArray>::ConstIterator deferredBindings = compiledData->deferredBindingsPerObject.constFind(_compiledObjectIndex); if (deferredBindings != compiledData->deferredBindingsPerObject.constEnd()) { if (bindingSkipList.isEmpty()) bindingSkipList.resize(deferredBindings->count()); diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 60fefe494f..c88c15b525 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -55,7 +55,7 @@ struct QQmlObjectCreatorSharedState : public QSharedData { QQmlContextData *rootContext; QQmlContextData *creationContext; - QFiniteStack<QQmlAbstractBinding*> allCreatedBindings; + QFiniteStack<QQmlAbstractBinding::Ptr> allCreatedBindings; QFiniteStack<QQmlParserStatus*> allParserStatusCallbacks; QFiniteStack<QPointer<QObject> > allCreatedObjects; QV4::Value *allJavaScriptObjects; // pointer to vector on JS stack to reference JS wrappers during creation phase. diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp index fc24b15fd2..c6d2d44ee1 100644 --- a/src/qml/qml/qqmlopenmetaobject.cpp +++ b/src/qml/qml/qqmlopenmetaobject.cpp @@ -277,8 +277,8 @@ void QQmlOpenMetaObject::setValue(int id, const QVariant &value) QVariant QQmlOpenMetaObject::value(const QByteArray &name) const { - QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); - if (iter == d->type->d->names.end()) + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name); + if (iter == d->type->d->names.cend()) return QVariant(); return d->getData(*iter); @@ -286,8 +286,8 @@ QVariant QQmlOpenMetaObject::value(const QByteArray &name) const QVariant &QQmlOpenMetaObject::operator[](const QByteArray &name) { - QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); - Q_ASSERT(iter != d->type->d->names.end()); + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name); + Q_ASSERT(iter != d->type->d->names.cend()); return d->getData(*iter); } @@ -299,10 +299,10 @@ QVariant &QQmlOpenMetaObject::operator[](int id) bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val) { - QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name); int id = -1; - if (iter == d->type->d->names.end()) { + if (iter == d->type->d->names.cend()) { id = createProperty(name.constData(), "") - d->type->d->propertyOffset; } else { id = *iter; diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 800f650075..c1120b4542 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -55,6 +55,7 @@ #include <private/qmetaobject_p.h> #include <private/qqmlvaluetypewrapper_p.h> #include <QtCore/qdebug.h> +#include <cmath> Q_DECLARE_METATYPE(QList<int>) Q_DECLARE_METATYPE(QList<qreal>) @@ -699,8 +700,7 @@ QQmlPropertyPrivate::binding(const QQmlProperty &that) if (!that.d || !that.isProperty() || !that.d->object) return 0; - return binding(that.d->object, that.d->core.coreIndex, - that.d->core.getValueTypeCoreIndex()); + return binding(that.d->object, that.d->core.encodedIndex()); } /*! @@ -716,65 +716,96 @@ QQmlPropertyPrivate::binding(const QQmlProperty &that) \a flags is passed through to the binding and is used for the initial update (when the binding sets the initial value, it will use these flags for the write). */ -QQmlAbstractBinding * -QQmlPropertyPrivate::setBinding(const QQmlProperty &that, - QQmlAbstractBinding *newBinding, - WriteFlags flags) +void +QQmlPropertyPrivate::setBinding(const QQmlProperty &that, QQmlAbstractBinding *newBinding) { + if (!newBinding) { + removeBinding(that); + return; + } + if (!that.d || !that.isProperty() || !that.d->object) { - if (newBinding) - newBinding->destroy(); - return 0; + if (!newBinding->ref) + delete newBinding; + return; } + setBinding(newBinding); +} + +static void removeOldBinding(QObject *object, int index, QQmlPropertyPrivate::BindingFlags flags = QQmlPropertyPrivate::None) +{ + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); - if (newBinding) { - // In the case that the new binding is provided, we must target the property it - // is associated with. If we don't do this, retargetBinding() can fail. - QObject *object = newBinding->object(); - int pi = newBinding->propertyIndex(); + QQmlData *data = QQmlData::get(object, false); - int core; - int vt = QQmlPropertyData::decodeValueTypePropertyIndex(pi, &core); + if (!data || !data->hasBindingBit(coreIndex)) + return; - return setBinding(object, core, vt, newBinding, flags); - } else { - return setBinding(that.d->object, that.d->core.coreIndex, - that.d->core.getValueTypeCoreIndex(), - newBinding, flags); - } + QQmlAbstractBinding::Ptr oldBinding; + oldBinding = data->bindings; + + while (oldBinding && oldBinding->targetPropertyIndex() != coreIndex) + oldBinding = oldBinding->nextBinding(); + + if (!oldBinding) + return; + + if (valueTypeIndex != -1 && oldBinding->isValueTypeProxy()) + oldBinding = static_cast<QQmlValueTypeProxyBinding *>(oldBinding.data())->binding(index); + + if (!oldBinding) + return; + + if (!(flags & QQmlPropertyPrivate::DontEnable)) + oldBinding->setEnabled(false, 0); + oldBinding->removeFromObject(); +} + +void QQmlPropertyPrivate::removeBinding(QQmlAbstractBinding *b) +{ + removeBinding(b->targetObject(), b->targetPropertyIndex()); +} + +void QQmlPropertyPrivate::removeBinding(QObject *o, int index) +{ + Q_ASSERT(o); + + QObject *target; + int targetIndex; + findAliasTarget(o, index, &target, &targetIndex); + removeOldBinding(target, targetIndex); +} + +void QQmlPropertyPrivate::removeBinding(const QQmlProperty &that) +{ + if (!that.d || !that.isProperty() || !that.d->object) + return; + + removeBinding(that.d->object, that.d->core.encodedIndex()); } QQmlAbstractBinding * -QQmlPropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex) +QQmlPropertyPrivate::binding(QObject *object, int index) { QQmlData *data = QQmlData::get(object); if (!data) return 0; - QQmlPropertyData *propertyData = - data->propertyCache?data->propertyCache->property(coreIndex):0; - if (propertyData && propertyData->isAlias()) { - QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); + findAliasTarget(object, index, &object, &index); - QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; - if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex) || aCoreIndex == -1) - return 0; - - // This will either be a value type sub-reference or an alias to a value-type sub-reference not both - Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); - aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex; - return binding(aObject, aCoreIndex, aValueTypeIndex); - } + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); if (!data->hasBindingBit(coreIndex)) return 0; QQmlAbstractBinding *binding = data->bindings; - while (binding && binding->propertyIndex() != coreIndex) + while (binding && binding->targetPropertyIndex() != coreIndex) binding = binding->nextBinding(); if (binding && valueTypeIndex != -1) { - if (binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) { + if (binding->isValueTypeProxy()) { int index = QQmlPropertyData::encodeValueTypePropertyIndex(coreIndex, valueTypeIndex); binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index); } @@ -786,11 +817,11 @@ QQmlPropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex) void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, QObject **targetObject, int *targetBindingIndex) { - int coreIndex; - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(bindingIndex, &coreIndex); - QQmlData *data = QQmlData::get(object, false); if (data) { + int coreIndex; + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(bindingIndex, &coreIndex); + QQmlPropertyData *propertyData = data->propertyCache?data->propertyCache->property(coreIndex):0; if (propertyData && propertyData->isAlias()) { @@ -817,119 +848,30 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, *targetBindingIndex = bindingIndex; } -QQmlAbstractBinding * -QQmlPropertyPrivate::setBinding(QObject *object, int coreIndex, int valueTypeIndex, - QQmlAbstractBinding *newBinding, WriteFlags flags) -{ - QQmlData *data = QQmlData::get(object, 0 != newBinding); - QQmlAbstractBinding *binding = 0; - - if (data) { - QQmlPropertyData *propertyData = - data->propertyCache?data->propertyCache->property(coreIndex):0; - if (propertyData && propertyData->isAlias()) { - QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); - - QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; - if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { - if (newBinding) newBinding->destroy(); - return 0; - } - - // This will either be a value type sub-reference or an alias to a value-type sub-reference not both - Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); - aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex; - return setBinding(aObject, aCoreIndex, aValueTypeIndex, newBinding, flags); - } - } - - if (data && data->hasBindingBit(coreIndex)) { - binding = data->bindings; - - while (binding && binding->propertyIndex() != coreIndex) - binding = binding->nextBinding(); - } - - int index = coreIndex; - if (valueTypeIndex != -1) - index = QQmlPropertyData::encodeValueTypePropertyIndex(index, valueTypeIndex); - - if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) - binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index); - - if (binding) { - binding->removeFromObject(); - binding->setEnabled(false, 0); - } - - if (newBinding) { - if (newBinding->propertyIndex() != index || newBinding->object() != object) - newBinding->retargetBinding(object, index); - - Q_ASSERT(newBinding->propertyIndex() == index); - Q_ASSERT(newBinding->object() == object); - - newBinding->addToObject(); - newBinding->setEnabled(true, flags); - } - - return binding; -} -QQmlAbstractBinding * -QQmlPropertyPrivate::setBindingNoEnable(QObject *object, int coreIndex, int valueTypeIndex, - QQmlAbstractBinding *newBinding) +void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, WriteFlags writeFlags) { - QQmlData *data = QQmlData::get(object, 0 != newBinding); - QQmlAbstractBinding *binding = 0; - - if (data) { - QQmlPropertyData *propertyData = - data->propertyCache?data->propertyCache->property(coreIndex):0; - if (propertyData && propertyData->isAlias()) { - QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); - - QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; - if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { - if (newBinding) newBinding->destroy(); - return 0; - } + Q_ASSERT(binding); - // This will either be a value type sub-reference or an alias to a value-type sub-reference not both - Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); - aValueTypeIndex = (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex; - return setBindingNoEnable(aObject, aCoreIndex, aValueTypeIndex, newBinding); - } - } + QObject *object = binding->targetObject(); + int index = binding->targetPropertyIndex(); - if (data && data->hasBindingBit(coreIndex)) { - binding = data->bindings; - - while (binding && binding->propertyIndex() != coreIndex) - binding = binding->nextBinding(); +#ifndef QT_NO_DEBUG + int coreIndex; + QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex); + QQmlData *data = QQmlData::get(object, true); + if (data->propertyCache) { + QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); + Q_ASSERT(propertyData && !propertyData->isAlias()); } +#endif - int index = coreIndex; - if (valueTypeIndex != -1) - index = QQmlPropertyData::encodeValueTypePropertyIndex(index, valueTypeIndex); - - if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) - binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index); - - if (binding) - binding->removeFromObject(); - - if (newBinding) { - if (newBinding->propertyIndex() != index || newBinding->object() != object) - newBinding->retargetBinding(object, index); - - Q_ASSERT(newBinding->propertyIndex() == index); - Q_ASSERT(newBinding->object() == object); + removeOldBinding(object, index, flags); - newBinding->addToObject(); - } + binding->addToObject(); + if (!(flags & DontEnable)) + binding->setEnabled(true, writeFlags); - return binding; } /*! @@ -946,9 +888,9 @@ QQmlPropertyPrivate::signalExpression(const QQmlProperty &that) if (!data) return 0; - QQmlAbstractBoundSignal *signalHandler = data->signalHandlers; + QQmlBoundSignal *signalHandler = data->signalHandlers; - while (signalHandler && signalHandler->index() != QQmlPropertyPrivate::get(that)->signalIndex()) + while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(that)->signalIndex()) signalHandler = signalHandler->m_nextSignal; if (signalHandler) @@ -959,48 +901,41 @@ QQmlPropertyPrivate::signalExpression(const QQmlProperty &that) /*! Set the signal expression associated with this signal property to \a expr. - Returns the existing signal expression (if any), otherwise null. - - A reference to \a expr will be added by QML. Ownership of the return value - reference is assumed by the caller. + A reference to \a expr will be added by QML. */ -QQmlBoundSignalExpressionPointer -QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, - QQmlBoundSignalExpression *expr) +void QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *expr) { if (expr) expr->addref(); - return QQmlPropertyPrivate::takeSignalExpression(that, expr); + QQmlPropertyPrivate::takeSignalExpression(that, expr); } /*! Set the signal expression associated with this signal property to \a expr. - Returns the existing signal expression (if any), otherwise null. - - Ownership of \a expr transfers to QML. Ownership of the return value - reference is assumed by the caller. + Ownership of \a expr transfers to QML. */ -QQmlBoundSignalExpressionPointer -QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that, +void QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *expr) { if (!(that.type() & QQmlProperty::SignalProperty)) { if (expr) expr->release(); - return 0; + return; } QQmlData *data = QQmlData::get(that.d->object, 0 != expr); if (!data) - return 0; + return; - QQmlAbstractBoundSignal *signalHandler = data->signalHandlers; + QQmlBoundSignal *signalHandler = data->signalHandlers; - while (signalHandler && signalHandler->index() != QQmlPropertyPrivate::get(that)->signalIndex()) + while (signalHandler && signalHandler->signalIndex() != QQmlPropertyPrivate::get(that)->signalIndex()) signalHandler = signalHandler->m_nextSignal; - if (signalHandler) - return signalHandler->takeExpression(expr); + if (signalHandler) { + signalHandler->takeExpression(expr); + return; + } if (expr) { int signalIndex = QQmlPropertyPrivate::get(that)->signalIndex(); @@ -1008,7 +943,6 @@ QQmlPropertyPrivate::takeSignalExpression(const QQmlProperty &that, expr->context()->engine); signal->takeExpression(expr); } - return 0; } /*! @@ -1125,7 +1059,7 @@ QVariant QQmlPropertyPrivate::readValueProperty() } // helper function to allow assignment / binding to QList<QUrl> properties. -static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *context) +QVariant QQmlPropertyPrivate::resolvedUrlSequence(const QVariant &value, QQmlContextData *context) { QList<QUrl> urls; if (value.userType() == qMetaTypeId<QUrl>()) { @@ -1138,16 +1072,22 @@ static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *cont urls = value.value<QList<QUrl> >(); } else if (value.userType() == qMetaTypeId<QStringList>()) { QStringList urlStrings = value.value<QStringList>(); - for (int i = 0; i < urlStrings.size(); ++i) + const int urlStringsSize = urlStrings.size(); + urls.reserve(urlStringsSize); + for (int i = 0; i < urlStringsSize; ++i) urls.append(QUrl(urlStrings.at(i))); } else if (value.userType() == qMetaTypeId<QList<QString> >()) { QList<QString> urlStrings = value.value<QList<QString> >(); - for (int i = 0; i < urlStrings.size(); ++i) + const int urlStringsSize = urlStrings.size(); + urls.reserve(urlStringsSize); + for (int i = 0; i < urlStringsSize; ++i) urls.append(QUrl(urlStrings.at(i))); } // note: QList<QByteArray> is not currently supported. QList<QUrl> resolvedUrls; - for (int i = 0; i < urls.size(); ++i) { + const int urlsSize = urls.size(); + resolvedUrls.reserve(urlsSize); + for (int i = 0; i < urlsSize; ++i) { QUrl u = urls.at(i); if (context && u.isRelative() && !u.isEmpty()) u = context->resolvedUrl(u); @@ -1210,12 +1150,8 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, QQmlContextData *context, WriteFlags flags) { // Remove any existing bindings on this property - if (!(flags & DontRemoveBinding) && object) { - QQmlAbstractBinding *binding = setBinding(object, core.coreIndex, - core.getValueTypeCoreIndex(), - 0, flags); - if (binding) binding->destroy(); - } + if (!(flags & DontRemoveBinding) && object) + removeBinding(object, core.encodedIndex()); bool rv = false; if (core.isValueTypeVirtual()) { @@ -1255,7 +1191,7 @@ bool QQmlPropertyPrivate::write(QObject *object, // Enum values come through the script engine as doubles if (value.userType() == QVariant::Double) { double integral; - double fractional = modf(value.toDouble(), &integral); + double fractional = std::modf(value.toDouble(), &integral); if (qFuzzyIsNull(fractional)) v.convert(QVariant::Int); } @@ -1475,166 +1411,6 @@ bool QQmlPropertyPrivate::write(QObject *object, return true; } -// Returns true if successful, false if an error description was set on expression -bool QQmlPropertyPrivate::writeBinding(QObject *object, - const QQmlPropertyData &core, - QQmlContextData *context, - QQmlJavaScriptExpression *expression, - const QV4::Value &result, bool isUndefined, - WriteFlags flags) -{ - Q_ASSERT(object); - Q_ASSERT(core.coreIndex != -1); - - QQmlEngine *engine = context->engine; - QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); - -#define QUICK_STORE(cpptype, conversion) \ - { \ - cpptype o = (conversion); \ - int status = -1; \ - void *argv[] = { &o, 0, &status, &flags }; \ - QMetaObject::metacall(object, QMetaObject::WriteProperty, core.coreIndex, argv); \ - return true; \ - } \ - - - if (!isUndefined && !core.isValueTypeVirtual()) { - switch (core.propType) { - case QMetaType::Int: - if (result.isInteger()) - QUICK_STORE(int, result.integerValue()) - else if (result.isNumber()) - QUICK_STORE(int, result.doubleValue()) - break; - case QMetaType::Double: - if (result.isNumber()) - QUICK_STORE(double, result.asDouble()) - break; - case QMetaType::Float: - if (result.isNumber()) - QUICK_STORE(float, result.asDouble()) - break; - case QMetaType::QString: - if (result.isString()) - QUICK_STORE(QString, result.toQStringNoThrow()) - break; - default: - if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) { - if (vtw->d()->valueType->typeId == core.propType) { - return vtw->write(object, core.coreIndex); - } - } - break; - } - } -#undef QUICK_STORE - - int type = core.isValueTypeVirtual()?core.valueTypePropType:core.propType; - - QQmlJavaScriptExpression::DeleteWatcher watcher(expression); - - QVariant value; - bool isVarProperty = core.isVarProperty(); - - if (isUndefined) { - } else if (core.isQList()) { - value = QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QObject *> >()); - } else if (result.isNull() && core.isQObject()) { - value = QVariant::fromValue((QObject *)0); - } else if (core.propType == qMetaTypeId<QList<QUrl> >()) { - value = resolvedUrlSequence(QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QUrl> >()), context); - } else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) { - value = QV8Engine::getV4(v8engine)->toVariant(result, type); - } - - if (expression->hasError()) { - return false; - } else if (isVarProperty) { - QV4::FunctionObject *f = result.asFunctionObject(); - if (f && f->isBinding()) { - // we explicitly disallow this case to avoid confusion. Users can still store one - // in an array in a var property if they need to, but the common case is user error. - expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); - expression->delayedError()->setErrorObject(object); - return false; - } - - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); - Q_ASSERT(vmemo); - vmemo->setVMEProperty(core.coreIndex, result); - } else if (isUndefined && core.isResettable()) { - void *args[] = { 0 }; - QMetaObject::metacall(object, QMetaObject::ResetProperty, core.coreIndex, args); - } else if (isUndefined && type == qMetaTypeId<QVariant>()) { - writeValueProperty(object, core, QVariant(), context, flags); - } else if (type == qMetaTypeId<QJSValue>()) { - QV4::FunctionObject *f = result.asFunctionObject(); - if (f && f->isBinding()) { - expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); - expression->delayedError()->setErrorObject(object); - return false; - } - writeValueProperty(object, core, QVariant::fromValue( - QJSValue(QV8Engine::getV4(v8engine), result.asReturnedValue())), - context, flags); - } else if (isUndefined) { - QString errorStr = QLatin1String("Unable to assign [undefined] to "); - if (!QMetaType::typeName(type)) - errorStr += QLatin1String("[unknown property type]"); - else - errorStr += QLatin1String(QMetaType::typeName(type)); - expression->delayedError()->setErrorDescription(errorStr); - expression->delayedError()->setErrorObject(object); - return false; - } else if (QV4::FunctionObject *f = result.asFunctionObject()) { - if (f->isBinding()) - expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); - else - expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var.")); - expression->delayedError()->setErrorObject(object); - return false; - } else if (!writeValueProperty(object, core, value, context, flags)) { - - if (watcher.wasDeleted()) - return true; - - const char *valueType = 0; - const char *propertyType = 0; - - if (value.userType() == QMetaType::QObjectStar) { - if (QObject *o = *(QObject *const *)value.constData()) { - valueType = o->metaObject()->className(); - - QQmlMetaObject propertyMetaObject = rawMetaObjectForType(QQmlEnginePrivate::get(engine), type); - if (!propertyMetaObject.isNull()) - propertyType = propertyMetaObject.className(); - } - } else if (value.userType() != QVariant::Invalid) { - if (value.userType() == QMetaType::VoidStar) - valueType = "null"; - else - valueType = QMetaType::typeName(value.userType()); - } - - if (!valueType) - valueType = "undefined"; - if (!propertyType) - propertyType = QMetaType::typeName(type); - if (!propertyType) - propertyType = "[unknown property type]"; - - expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign ") + - QLatin1String(valueType) + - QLatin1String(" to ") + - QLatin1String(propertyType)); - expression->delayedError()->setErrorObject(object); - return false; - } - - return true; -} - QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engine, int userType) { QMetaType metaType(userType); diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 98e310ebce..51a1db7b90 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -58,6 +58,7 @@ QT_BEGIN_NAMESPACE class QQmlContext; class QQmlEnginePrivate; class QQmlJavaScriptExpression; + class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount { public: @@ -103,15 +104,18 @@ public: QQmlContextData *, WriteFlags flags = 0); static void findAliasTarget(QObject *, int, QObject **, int *); - static QQmlAbstractBinding *setBinding(QObject *, int coreIndex, - int valueTypeIndex /* -1 */, - QQmlAbstractBinding *, - WriteFlags flags = DontRemoveBinding); - static QQmlAbstractBinding *setBindingNoEnable(QObject *, int coreIndex, - int valueTypeIndex /* -1 */, - QQmlAbstractBinding *); - static QQmlAbstractBinding *binding(QObject *, int coreIndex, - int valueTypeIndex /* -1 */); + enum BindingFlag { + None = 0, + DontEnable = 0x1 + }; + Q_DECLARE_FLAGS(BindingFlags, BindingFlag) + + static void setBinding(QQmlAbstractBinding *binding, BindingFlags flags = None, WriteFlags writeFlags = DontRemoveBinding); + + static void removeBinding(const QQmlProperty &that); + static void removeBinding(QObject *o, int index); + static void removeBinding(QQmlAbstractBinding *b); + static QQmlAbstractBinding *binding(QObject *, int index); static QQmlPropertyData saveValueType(const QQmlPropertyData &, const QMetaObject *, int, @@ -128,20 +132,13 @@ public: // "Public" (to QML) methods static QQmlAbstractBinding *binding(const QQmlProperty &that); - static QQmlAbstractBinding *setBinding(const QQmlProperty &that, - QQmlAbstractBinding *, - WriteFlags flags = DontRemoveBinding); + static void setBinding(const QQmlProperty &that, QQmlAbstractBinding *); static QQmlBoundSignalExpression *signalExpression(const QQmlProperty &that); - static QQmlBoundSignalExpressionPointer setSignalExpression(const QQmlProperty &that, + static void setSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *); - static QQmlBoundSignalExpressionPointer takeSignalExpression(const QQmlProperty &that, + static void takeSignalExpression(const QQmlProperty &that, QQmlBoundSignalExpression *); static bool write(const QQmlProperty &that, const QVariant &, WriteFlags); - static bool writeBinding(QObject *, const QQmlPropertyData &, - QQmlContextData *context, - QQmlJavaScriptExpression *expression, - const QV4::Value &result, bool isUndefined, - WriteFlags flags); static int valueTypeCoreIndex(const QQmlProperty &that); static int bindingIndex(const QQmlProperty &that); static int bindingIndex(const QQmlPropertyData &that); @@ -150,9 +147,12 @@ public: const QObject *receiver, int method_index, int type = 0, int *types = 0); static void flushSignal(const QObject *sender, int signal_index); + + static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *context); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyPrivate::WriteFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyPrivate::BindingFlags) QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 0018275b95..cd8eb0d3e2 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -42,7 +42,7 @@ #include <private/qqmlaccessors_p.h> #include <private/qmetaobjectbuilder_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <QtCore/qdebug.h> @@ -837,7 +837,7 @@ void QQmlPropertyCache::updateRecur(const QMetaObject *metaObject) void QQmlPropertyCache::update(const QMetaObject *metaObject) { Q_ASSERT(metaObject); - Q_ASSERT(stringCache.isEmpty()); + stringCache.clear(); // Preallocate enough space in the index caches for all the properties/methods/signals that // are not cached in a parent cache so that the caches never need to be reallocated as this @@ -862,7 +862,6 @@ void QQmlPropertyCache::update(const QMetaObject *metaObject) */ void QQmlPropertyCache::invalidate(const QMetaObject *metaObject) { - stringCache.clear(); propertyIndexCache.clear(); methodIndexCache.clear(); signalHandlerIndexCache.clear(); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 6ed9ec0d36..4d8c6dd9a5 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -54,7 +54,7 @@ #include <QtCore/qvarlengtharray.h> #include <QtCore/qvector.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> QT_BEGIN_NAMESPACE @@ -174,7 +174,6 @@ public: int propType; // When !NotFullyResolved const char *propTypeName; // When NotFullyResolved }; - int coreIndex; union { // The notify index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). @@ -208,6 +207,7 @@ public: qintptr accessorData; }; }; + int coreIndex; private: friend class QQmlPropertyData; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index d713e9ee03..408f17ffde 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -81,7 +81,7 @@ #define ASSERT_MAINTHREAD() do { if (m_thread->isThisThread()) qFatal("QQmlTypeLoader: Caller not in main thread"); } while (false) #define ASSERT_LOADTHREAD() do { if (!m_thread->isThisThread()) qFatal("QQmlTypeLoader: Caller not in load thread"); } while (false) -#define ASSERT_CALLBACK() do { if(!m_manager || !m_manager->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while(false) +#define ASSERT_CALLBACK() do { if (!m_typeLoader || !m_typeLoader->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while (false) #else @@ -1612,6 +1612,20 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode) } else { QQmlTypeLoader::load(typeData, mode); } + } else if ((mode == PreferSynchronous) && QQmlFile::isSynchronous(url)) { + // this was started Asynchronous, but we need to force Synchronous + // completion now (if at all possible with this type of URL). + + if (!m_thread->isThisThread()) { + // this only works when called directly from the UI thread, but not + // when recursively called on the QML thread via resolveTypes() + + while (!typeData->isCompleteOrError()) { + unlock(); + m_thread->waitForNextMessage(); + lock(); + } + } } typeData->addref(); @@ -2114,6 +2128,7 @@ void QQmlTypeData::dataReceived(const Data &data) QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) { QList<QQmlError> errors; + errors.reserve(compiler.errors.count()); foreach (const QQmlJS::DiagnosticMessage &msg, compiler.errors) { QQmlError e; e.setUrl(finalUrl()); @@ -2519,8 +2534,8 @@ QV4::PersistentValue QQmlScriptData::scriptValueForContext(QQmlContextData *pare return QV4::PersistentValue(); } - QV4::ScopedValue qmlglobal(scope, QV4::QmlContextWrapper::qmlScope(v4, ctxt, 0)); - QV4::QmlContextWrapper::takeContextOwnership(qmlglobal); + QV4::Scoped<QV4::QmlContextWrapper> qmlglobal(scope, QV4::QmlContextWrapper::qmlScope(v4, ctxt, 0)); + qmlglobal->takeContextOwnership(); m_program->qml.set(scope.engine, qmlglobal); m_program->run(); @@ -2594,7 +2609,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) return; } if (!unit) { - unit.take(new EmptyCompilationUnit); + unit.adopt(new EmptyCompilationUnit); } irUnit.javaScriptCompilationUnit = unit; irUnit.imports = collector.imports; diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index d9ea273698..5be478fa65 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -60,7 +60,7 @@ #include <private/qflagpointer_p.h> #include <private/qqmlirbuilder_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4script_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp index d0658f2c3c..23cc12c895 100644 --- a/src/qml/qml/qqmltypenamecache.cpp +++ b/src/qml/qml/qqmltypenamecache.cpp @@ -125,7 +125,7 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, cons const Import *i = static_cast<const Import *>(importNamespace); Q_ASSERT(i->scriptIndex == -1); - QMap<const Import *, QStringHash<Import> >::const_iterator it = m_namespacedImports.find(i); + QMap<const Import *, QStringHash<Import> >::const_iterator it = m_namespacedImports.constFind(i); if (it != m_namespacedImports.constEnd()) { Result r = query(*it, name); if (r.isValid()) diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 8a2118ef27..1d72b2da0d 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -124,19 +124,19 @@ ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, Q } -ReturnedValue QmlTypeWrapper::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<QmlTypeWrapper>()); - QV4::ExecutionEngine *v4 = static_cast<QmlTypeWrapper *>(m)->engine(); + QV4::ExecutionEngine *v4 = static_cast<const QmlTypeWrapper *>(m)->engine(); QV4::Scope scope(v4); - Scoped<QmlTypeWrapper> w(scope, static_cast<QmlTypeWrapper *>(m)); + Scoped<QmlTypeWrapper> w(scope, static_cast<const QmlTypeWrapper *>(m)); if (hasProperty) *hasProperty = true; - QQmlContextData *context = v4->v8Engine->callingContext(); + QQmlContextData *context = v4->callingQmlContext(); QObject *object = w->d()->object; @@ -240,7 +240,7 @@ void QmlTypeWrapper::put(Managed *m, String *name, const Value &value) return; QV4::Scope scope(v4); - QQmlContextData *context = v4->v8Engine->callingContext(); + QQmlContextData *context = v4->callingQmlContext(); QQmlType *type = w->d()->type; if (type && !type->isSingleton() && w->d()->object) { diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 660d2836ff..14741a5bad 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -48,7 +48,7 @@ #include <QtCore/qglobal.h> #include <QtCore/qpointer.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE @@ -94,7 +94,7 @@ struct Q_QML_EXPORT QmlTypeWrapper : Object Heap::QmlTypeWrapper::TypeNameMode = Heap::QmlTypeWrapper::IncludeEnums); - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void 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/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 10eaae0c77..6a71a07e9b 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -517,7 +517,9 @@ void QQmlEasingValueType::setBezierCurve(const QVariantList &customCurveVariant) if ((variantList.count() % 6) == 0) { bool allRealsOk = true; QList<qreal> reals; - for (int i = 0; i < variantList.count(); i++) { + const int variantListCount = variantList.count(); + reals.reserve(variantListCount); + for (int i = 0; i < variantListCount; i++) { bool ok; const qreal real = variantList.at(i).toReal(&ok); reals.append(real); diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h index 2c02cc0aa1..abd73d7f35 100644 --- a/src/qml/qml/qqmlvaluetype_p.h +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -209,7 +209,6 @@ struct QQmlEasingValueType { QEasingCurve v; Q_GADGET - Q_ENUMS(Type) Q_PROPERTY(QQmlEasingValueType::Type type READ type WRITE setType FINAL) Q_PROPERTY(qreal amplitude READ amplitude WRITE setAmplitude FINAL) @@ -243,6 +242,7 @@ public: SineCurve = QEasingCurve::SineCurve, CosineCurve = QEasingCurve::CosineCurve, Bezier = QEasingCurve::BezierSpline }; + Q_ENUM(Type) Type type() const; qreal amplitude() const; diff --git a/src/qml/qml/qqmlvaluetypeproxybinding.cpp b/src/qml/qml/qqmlvaluetypeproxybinding.cpp index cfc9b196d2..3bc8493cbb 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding.cpp +++ b/src/qml/qml/qqmlvaluetypeproxybinding.cpp @@ -35,80 +35,42 @@ QT_BEGIN_NAMESPACE -// Used in qqmlabstractbinding.cpp -QQmlAbstractBinding::VTable QQmlValueTypeProxyBinding_vtable = { - QQmlAbstractBinding::default_destroy<QQmlValueTypeProxyBinding>, - QQmlAbstractBinding::default_expression, - QQmlValueTypeProxyBinding::propertyIndex, - QQmlValueTypeProxyBinding::object, - QQmlValueTypeProxyBinding::setEnabled, - QQmlValueTypeProxyBinding::update, - QQmlAbstractBinding::default_retargetBinding -}; - QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index) -: QQmlAbstractBinding(ValueTypeProxy), m_object(o), m_index(index), m_bindings(0) + : QQmlAbstractBinding(), + m_bindings(0) { + m_target = o; + m_targetIndex = index; } QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding() { - QQmlAbstractBinding *binding = m_bindings; - // This must be identical to the logic in QQmlData::destroyed() + QQmlAbstractBinding *binding = m_bindings.data(); while (binding) { - QQmlAbstractBinding *next = binding->nextBinding(); binding->setAddedToObject(false); - binding->setNextBinding(0); - binding->destroy(); - binding = next; + binding = binding->nextBinding(); } } -void QQmlValueTypeProxyBinding::setEnabled(QQmlAbstractBinding *_This, - bool e, QQmlPropertyPrivate::WriteFlags flags) +void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) { - QQmlValueTypeProxyBinding *This = static_cast<QQmlValueTypeProxyBinding *>(_This); - - if (e) { - QQmlAbstractBinding *bindings = This->m_bindings; - This->recursiveEnable(bindings, flags); - } else { - QQmlAbstractBinding *bindings = This->m_bindings; - This->recursiveDisable(bindings); + QQmlAbstractBinding *b = m_bindings.data(); + while (b) { + b->setEnabled(e, flags); + b = b->nextBinding(); } } -void QQmlValueTypeProxyBinding::recursiveEnable(QQmlAbstractBinding *b, QQmlPropertyPrivate::WriteFlags flags) -{ - if (!b) - return; - - recursiveEnable(b->nextBinding(), flags); - - if (b) - b->setEnabled(true, flags); -} - -void QQmlValueTypeProxyBinding::recursiveDisable(QQmlAbstractBinding *b) -{ - if (!b) - return; - - recursiveDisable(b->nextBinding()); - - if (b) - b->setEnabled(false, 0); -} - -void QQmlValueTypeProxyBinding::update(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags) +bool QQmlValueTypeProxyBinding::isValueTypeProxy() const { + return true; } QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex) { - QQmlAbstractBinding *binding = m_bindings; + QQmlAbstractBinding *binding = m_bindings.data(); - while (binding && binding->propertyIndex() != propertyIndex) + while (binding && binding->targetPropertyIndex() != propertyIndex) binding = binding->nextBinding(); return binding; @@ -119,23 +81,20 @@ Removes a collection of bindings, corresponding to the set bits in \a mask. */ void QQmlValueTypeProxyBinding::removeBindings(quint32 mask) { - QQmlAbstractBinding *binding = m_bindings; + QQmlAbstractBinding *binding = m_bindings.data(); QQmlAbstractBinding *lastBinding = 0; while (binding) { - int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(binding->propertyIndex()); + int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(binding->targetPropertyIndex()); if (valueTypeIndex != -1 && (mask & (1 << valueTypeIndex))) { QQmlAbstractBinding *remove = binding; + remove->setAddedToObject(false); binding = remove->nextBinding(); if (lastBinding == 0) m_bindings = remove->nextBinding(); else lastBinding->setNextBinding(remove->nextBinding()); - - remove->setAddedToObject(false); - remove->setNextBinding(0); - remove->destroy(); } else { lastBinding = binding; binding = binding->nextBinding(); @@ -143,24 +102,4 @@ void QQmlValueTypeProxyBinding::removeBindings(quint32 mask) } } -int QQmlValueTypeProxyBinding::propertyIndex(const QQmlAbstractBinding *This) -{ - return static_cast<const QQmlValueTypeProxyBinding *>(This)->m_index; -} - -QObject *QQmlValueTypeProxyBinding::object(const QQmlAbstractBinding *This) -{ - return static_cast<const QQmlValueTypeProxyBinding *>(This)->m_object; -} - -int QQmlValueTypeProxyBinding::propertyIndex() const -{ - return m_index; -} - -QObject *QQmlValueTypeProxyBinding::object() const -{ - return m_object; -} - QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h index 873fbb4af1..4afadfc17d 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h +++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h @@ -54,30 +54,18 @@ class QQmlValueTypeProxyBinding : public QQmlAbstractBinding public: QQmlValueTypeProxyBinding(QObject *o, int coreIndex); - int propertyIndex() const; - QObject *object() const; - - QQmlAbstractBinding *binding(int propertyIndex); - + QQmlAbstractBinding *binding(int targetPropertyIndex); void removeBindings(quint32 mask); - // "Inherited" from QQmlAbstractBinding - static void setEnabled(QQmlAbstractBinding *, bool, QQmlPropertyPrivate::WriteFlags); - static void update(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); - static int propertyIndex(const QQmlAbstractBinding *); - static QObject *object(const QQmlAbstractBinding *); + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags); + virtual bool isValueTypeProxy() const; protected: ~QQmlValueTypeProxyBinding(); private: - void recursiveEnable(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); - void recursiveDisable(QQmlAbstractBinding *); - friend class QQmlAbstractBinding; - QObject *m_object; - int m_index; - QQmlAbstractBinding *m_bindings; + Ptr m_bindings; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index e87d9ede77..b0ab85199d 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -43,7 +43,6 @@ #include <private/qv4engine_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4variantobject_p.h> -#include <private/qv4qmlextensions_p.h> #include <private/qv4alloca_p.h> QT_BEGIN_NAMESPACE @@ -165,13 +164,13 @@ bool QQmlValueTypeReference::readReferenceValue() const void QQmlValueTypeWrapper::initProto(ExecutionEngine *v4) { - if (v4->qmlExtensions()->valueTypeWrapperPrototype) + if (v4->valueTypeWrapperPrototype()->as<Object>()) return; Scope scope(v4); ScopedObject o(scope, v4->newObject()); - o->defineDefaultProperty(v4->id_toString, method_toString, 1); - v4->qmlExtensions()->valueTypeWrapperPrototype = o->d(); + o->defineDefaultProperty(v4->id_toString(), method_toString, 1); + v4->jsObjects[QV4::ExecutionEngine::ValueTypeProto] = o->d(); } ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *object, int property, const QMetaObject *metaObject, int typeId) @@ -180,7 +179,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj initProto(engine); Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->alloc<QQmlValueTypeReference>(engine)); - ScopedObject proto(scope, engine->qmlExtensions()->valueTypeWrapperPrototype); + ScopedObject proto(scope, engine->valueTypeWrapperPrototype()); r->setPrototype(proto); r->d()->object = object; r->d()->property = property; r->d()->propertyCache = QJSEnginePrivate::get(engine)->cache(metaObject); @@ -195,7 +194,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVaria initProto(engine); Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->alloc<QQmlValueTypeWrapper>(engine)); - ScopedObject proto(scope, engine->qmlExtensions()->valueTypeWrapperPrototype); + ScopedObject proto(scope, engine->valueTypeWrapperPrototype()); r->setPrototype(proto); r->d()->propertyCache = QJSEnginePrivate::get(engine)->cache(metaObject); r->d()->valueType = QQmlValueTypeFactory::valueType(typeId); @@ -292,7 +291,7 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const ReturnedValue QQmlValueTypeWrapper::method_toString(CallContext *ctx) { - Object *o = ctx->thisObject().asObject(); + Object *o = ctx->thisObject().as<Object>(); if (!o) return ctx->engine()->throwTypeError(); QQmlValueTypeWrapper *w = o->as<QQmlValueTypeWrapper>(); @@ -327,14 +326,14 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(CallContext *ctx) return Encode(ctx->engine()->newString(result)); } -ReturnedValue QQmlValueTypeWrapper::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<QQmlValueTypeWrapper>()); - QQmlValueTypeWrapper *r = static_cast<QQmlValueTypeWrapper *>(m); + const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); QV4::ExecutionEngine *v4 = r->engine(); // Note: readReferenceValue() can change the reference->type. - if (QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { + if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) { if (!reference->readReferenceValue()) return Primitive::undefinedValue().asReturnedValue(); } @@ -410,45 +409,41 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QMetaProperty property = metaObject->property(pd->coreIndex); Q_ASSERT(property.isValid()); - QQmlBinding *newBinding = 0; - - QV4::ScopedFunctionObject f(scope, value); - if (reference && f) { - if (!f->isBinding()) { - // assigning a JS function to a non-var-property is not allowed. - QString error = QStringLiteral("Cannot assign JavaScript function to value-type property"); - ScopedString e(scope, v4->newString(error)); - v4->throwError(e); - return; - } + if (reference) { + QV4::ScopedFunctionObject f(scope, value); + if (f) { + if (!f->isBinding()) { + // assigning a JS function to a non-var-property is not allowed. + QString error = QStringLiteral("Cannot assign JavaScript function to value-type property"); + ScopedString e(scope, v4->newString(error)); + v4->throwError(e); + return; + } - QQmlContextData *context = QmlContextWrapper::callingContext(v4); + QQmlContextData *context = v4->callingQmlContext(); - QQmlPropertyData cacheData; - cacheData.setFlags(QQmlPropertyData::IsWritable | - QQmlPropertyData::IsValueTypeVirtual); - cacheData.propType = writeBackPropertyType; - cacheData.coreIndex = reference->d()->property; - cacheData.valueTypeFlags = 0; - cacheData.valueTypeCoreIndex = pd->coreIndex; - cacheData.valueTypePropType = property.userType(); + QQmlPropertyData cacheData; + cacheData.setFlags(QQmlPropertyData::IsWritable | + QQmlPropertyData::IsValueTypeVirtual); + cacheData.propType = writeBackPropertyType; + cacheData.coreIndex = reference->d()->property; + cacheData.valueTypeFlags = 0; + cacheData.valueTypeCoreIndex = pd->coreIndex; + cacheData.valueTypePropType = property.userType(); - QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); - bindingFunction->initBindingLocation(); + QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); + bindingFunction->initBindingLocation(); - newBinding = new QQmlBinding(value, reference->d()->object, context); - newBinding->setTarget(reference->d()->object, cacheData, context); - } + QQmlBinding *newBinding = new QQmlBinding(value, reference->d()->object, context); + newBinding->setTarget(reference->d()->object, cacheData); + QQmlPropertyPrivate::setBinding(newBinding); + return; + } else { + QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyData::encodeValueTypePropertyIndex(reference->d()->property, pd->coreIndex)); - if (reference) { - QQmlAbstractBinding *oldBinding = - QQmlPropertyPrivate::setBinding(reference->d()->object, reference->d()->property, pd->coreIndex, newBinding); - if (oldBinding) - oldBinding->destroy(); + } } - if (newBinding) - return; QVariant v = v4->toVariant(value, property.userType()); diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index cad48e661c..5f0edfb71d 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -48,7 +48,7 @@ #include <QtCore/qglobal.h> #include <private/qtqmlglobal_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> QT_BEGIN_NAMESPACE @@ -89,7 +89,7 @@ public: int typeId() const; bool write(QObject *target, int propertyIndex) const; - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void put(Managed *m, String *name, const Value &value); static bool isEqualTo(Managed *m, Managed *other); static PropertyAttributes query(const Managed *, String *name); diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 97fc382c33..9f02c905fc 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -104,13 +104,11 @@ public: inline double asDouble(); inline const QString &asQString(); inline const QUrl &asQUrl(); - inline const QTime &asQTime(); inline const QDate &asQDate(); inline const QDateTime &asQDateTime(); inline const QRectF &asQRectF(); inline const QPointF &asQPointF(); inline const QSizeF &asQSizeF(); - inline const QJSValue &asQJSValue(); inline void setValue(QObject *v, QQmlVMEMetaObject *target, int index); inline void setValue(const QVariant &); @@ -125,9 +123,6 @@ public: inline void setValue(const QRectF &); inline void setValue(const QPointF &); inline void setValue(const QSizeF &); - inline void setValue(const QJSValue &); - - inline void setDataType(int t); inline void ensureValueType(int); @@ -196,9 +191,6 @@ void QQmlVMEVariant::cleanup() } else if (type == qMetaTypeId<QVariant>()) { ((QVariant *)dataPtr())->~QVariant(); type = QVariant::Invalid; - } else if (type == qMetaTypeId<QJSValue>()) { - ((QJSValue *)dataPtr())->~QJSValue(); - type = QVariant::Invalid; } else { if (QQml_valueTypeProvider()->destroyValueType(type, dataPtr(), dataSize())) { type = QVariant::Invalid; @@ -282,14 +274,6 @@ const QUrl &QQmlVMEVariant::asQUrl() return *(QUrl *)(dataPtr()); } -const QTime &QQmlVMEVariant::asQTime() -{ - if (type != QMetaType::QTime) - setValue(QTime()); - - return *(QTime *)(dataPtr()); -} - const QDate &QQmlVMEVariant::asQDate() { if (type != QMetaType::QDate) @@ -330,14 +314,6 @@ const QPointF &QQmlVMEVariant::asQPointF() return *(QPointF *)(dataPtr()); } -const QJSValue &QQmlVMEVariant::asQJSValue() -{ - if (type != qMetaTypeId<QJSValue>()) - setValue(QJSValue()); - - return *(QJSValue *)(dataPtr()); -} - void QQmlVMEVariant::setValue(QObject *v, QQmlVMEMetaObject *target, int index) { if (type != QMetaType::QObjectStar) { @@ -474,22 +450,6 @@ void QQmlVMEVariant::setValue(const QSizeF &v) } } -void QQmlVMEVariant::setValue(const QJSValue &v) -{ - if (type != qMetaTypeId<QJSValue>()) { - cleanup(); - type = qMetaTypeId<QJSValue>(); - new (dataPtr()) QJSValue(v); - } else { - *(QJSValue *)(dataPtr()) = v; - } -} - -void QQmlVMEVariant::setDataType(int t) -{ - type = t; -} - void QQmlVMEVariant::ensureValueType(int t) { if (type != t) { @@ -500,8 +460,8 @@ void QQmlVMEVariant::ensureValueType(int t) } QQmlVMEMetaObjectEndpoint::QQmlVMEMetaObjectEndpoint() + : QQmlNotifierEndpoint(QQmlNotifierEndpoint::QQmlVMEMetaObjectEndpoint) { - setCallback(QQmlNotifierEndpoint::QQmlVMEMetaObjectEndpoint); } void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *e, void **) @@ -874,10 +834,8 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) int flags = *reinterpret_cast<int*>(a[3]); if (flags & QQmlPropertyPrivate::RemoveBindingOnAliasWrite) { QQmlData *targetData = QQmlData::get(target); - if (targetData && targetData->hasBindingBit(d->propertyIndex())) { - QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(target, d->propertyIndex(), d->isValueTypeAlias()?d->valueTypeIndex():-1, 0); - if (binding) binding->destroy(); - } + if (targetData && targetData->hasBindingBit(d->propertyIndex())) + QQmlPropertyPrivate::removeBinding(target, d->propertyIdx); } } @@ -1183,7 +1141,7 @@ void QQmlVMEMetaObject::setVmeMethod(int index, const QV4::Value &function) v8methods = new QV4::PersistentValue[metaData->methodCount]; int methodIndex = index - methodOffset() - plainSignals; - v8methods[methodIndex].set(function.asObject()->engine(), function); + v8methods[methodIndex].set(function.as<QV4::Object>()->engine(), function); } QV4::ReturnedValue QQmlVMEMetaObject::vmeProperty(int index) @@ -1220,8 +1178,9 @@ bool QQmlVMEMetaObject::ensureVarPropertiesAllocated() void QQmlVMEMetaObject::ensureQObjectWrapper() { - QQmlEnginePrivate *ep = (ctxt == 0 || ctxt->engine == 0) ? 0 : QQmlEnginePrivate::get(ctxt->engine); - QV4::ExecutionEngine *v4 = (ep == 0) ? 0 : ep->v4engine(); + Q_ASSERT(ctxt && ctxt->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(ctxt->engine); + QV4::ExecutionEngine *v4 = ep->v4engine(); QV4::QObjectWrapper::wrap(v4, object); } @@ -1251,7 +1210,7 @@ void QQmlVMEMetaObject::mark(QV4::ExecutionEngine *e) void QQmlVMEMetaObject::allocateVarPropertiesArray() { QQmlEngine *qml = qmlEngine(object); - assert(qml); + Q_ASSERT(qml); QV4::ExecutionEngine *v4 = QV8Engine::getV4(qml->handle()); QV4::Scope scope(v4); varProperties.set(scope.engine, v4->newArrayObject(metaData->varPropertyCount)); diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index f3048d426a..a320163f23 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -63,7 +63,7 @@ #include <private/qv8engine_p.h> #include <private/qflagpointer_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 2a3ede6a22..0edb672517 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -60,6 +60,7 @@ #include <private/qv4objectproto_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qv4arraybuffer_p.h> +#include <private/qv4jsonobject_p.h> using namespace QV4; @@ -99,7 +100,7 @@ static ReturnedValue constructMeObject(const Value &thisObj, ExecutionEngine *v4 Scope scope(v4); ScopedObject meObj(scope, v4->newObject()); meObj->put(ScopedString(scope, v4->newString(QStringLiteral("ThisObject"))), thisObj); - ScopedValue v(scope, QmlContextWrapper::qmlScope(v4, v4->v8Engine->callingContext(), 0)); + ScopedValue v(scope, QmlContextWrapper::qmlScope(v4, v4->callingQmlContext(), 0)); meObj->put(ScopedString(scope, v4->newString(QStringLiteral("ActivationObject"))), v); return meObj.asReturnedValue(); } @@ -222,8 +223,8 @@ public: static ReturnedValue create(ExecutionEngine *, NodeImpl *, const QList<NodeImpl *> &); // JS API - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); }; Heap::NamedNodeMap::NamedNodeMap(ExecutionEngine *engine, NodeImpl *data, const QList<NodeImpl *> &list) @@ -244,8 +245,8 @@ public: V4_NEEDS_DESTROY // JS API - static ReturnedValue get(Managed *m, String *name, bool *hasProperty); - static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); // C++ API static ReturnedValue create(ExecutionEngine *, NodeImpl *); @@ -273,6 +274,7 @@ public: static ReturnedValue method_get_nodeName(CallContext *ctx); static ReturnedValue method_get_nodeValue(CallContext *ctx); static ReturnedValue method_get_nodeType(CallContext *ctx); + static ReturnedValue method_get_namespaceUri(CallContext *ctx); static ReturnedValue method_get_parentNode(CallContext *ctx); static ReturnedValue method_get_childNodes(CallContext *ctx); @@ -302,6 +304,7 @@ Heap::NodePrototype::NodePrototype(ExecutionEngine *engine) o->defineAccessorProperty(QStringLiteral("nodeName"), QV4::NodePrototype::method_get_nodeName, 0); o->defineAccessorProperty(QStringLiteral("nodeValue"), QV4::NodePrototype::method_get_nodeValue, 0); o->defineAccessorProperty(QStringLiteral("nodeType"), QV4::NodePrototype::method_get_nodeType, 0); + o->defineAccessorProperty(QStringLiteral("namespaceUri"), QV4::NodePrototype::method_get_namespaceUri, 0); o->defineAccessorProperty(QStringLiteral("parentNode"), QV4::NodePrototype::method_get_parentNode, 0); o->defineAccessorProperty(QStringLiteral("childNodes"), QV4::NodePrototype::method_get_childNodes, 0); @@ -470,6 +473,16 @@ ReturnedValue NodePrototype::method_get_nodeType(CallContext *ctx) return Encode(r->d()->d->type); } +ReturnedValue NodePrototype::method_get_namespaceUri(CallContext *ctx) +{ + Scope scope(ctx); + Scoped<Node> r(scope, ctx->thisObject().as<Node>()); + if (!r) + return ctx->engine()->throwTypeError(); + + return Encode(ctx->d()->engine->newString(r->d()->d->namespaceUri)); +} + ReturnedValue NodePrototype::method_get_parentNode(CallContext *ctx) { Scope scope(ctx); @@ -871,10 +884,10 @@ bool Node::isNull() const return d()->d == 0; } -ReturnedValue NamedNodeMap::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue NamedNodeMap::getIndexed(const Managed *m, uint index, bool *hasProperty) { Q_ASSERT(m->as<NamedNodeMap>()); - NamedNodeMap *r = static_cast<NamedNodeMap *>(m); + const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m); QV4::ExecutionEngine *v4 = r->engine(); if ((int)index < r->d()->list.count()) { @@ -887,14 +900,14 @@ ReturnedValue NamedNodeMap::getIndexed(Managed *m, uint index, bool *hasProperty return Encode::undefined(); } -ReturnedValue NamedNodeMap::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<NamedNodeMap>()); - NamedNodeMap *r = static_cast<NamedNodeMap *>(m); + const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m); QV4::ExecutionEngine *v4 = r->engine(); name->makeIdentifier(v4); - if (name->equals(v4->id_length)) + if (name->equals(v4->id_length())) return Primitive::fromInt32(r->d()->list.count()).asReturnedValue(); QString str = name->toQString(); @@ -916,10 +929,10 @@ ReturnedValue NamedNodeMap::create(ExecutionEngine *v4, NodeImpl *data, const QL return (v4->memoryManager->alloc<NamedNodeMap>(v4, data, list))->asReturnedValue(); } -ReturnedValue NodeList::getIndexed(Managed *m, uint index, bool *hasProperty) +ReturnedValue NodeList::getIndexed(const Managed *m, uint index, bool *hasProperty) { Q_ASSERT(m->as<NodeList>()); - NodeList *r = static_cast<NodeList *>(m); + const NodeList *r = static_cast<const NodeList *>(m); QV4::ExecutionEngine *v4 = r->engine(); if ((int)index < r->d()->d->children.count()) { @@ -932,15 +945,15 @@ ReturnedValue NodeList::getIndexed(Managed *m, uint index, bool *hasProperty) return Encode::undefined(); } -ReturnedValue NodeList::get(Managed *m, String *name, bool *hasProperty) +ReturnedValue NodeList::get(const Managed *m, String *name, bool *hasProperty) { Q_ASSERT(m->as<NodeList>()); - NodeList *r = static_cast<NodeList *>(m); + const NodeList *r = static_cast<const NodeList *>(m); QV4::ExecutionEngine *v4 = r->engine(); name->makeIdentifier(v4); - if (name->equals(v4->id_length)) + if (name->equals(v4->id_length())) return Primitive::fromInt32(r->d()->d->children.count()).asReturnedValue(); return Object::get(m, name, hasProperty); } @@ -1019,13 +1032,15 @@ public: QString header(const QString &name); QString headers(); - QString responseBody(); const QByteArray & rawResponseBody() const; bool receivedXml() const; const QString & responseType() const; void setResponseType(const QString &); + + QV4::ReturnedValue jsonResponseBody(QV4::ExecutionEngine*); + QV4::ReturnedValue xmlResponseBody(QV4::ExecutionEngine*); private slots: void readyRead(); void error(QNetworkReply::NetworkError); @@ -1076,6 +1091,7 @@ private: QNetworkAccessManager *networkAccessManager() { return m_nam; } QString m_responseType; + QV4::PersistentValue m_parsedDocument; }; QQmlXMLHttpRequest::QQmlXMLHttpRequest(ExecutionEngine *engine, QNetworkAccessManager *manager) @@ -1083,6 +1099,7 @@ QQmlXMLHttpRequest::QQmlXMLHttpRequest(ExecutionEngine *engine, QNetworkAccessMa , m_state(Unsent), m_errorFlag(false), m_sendFlag(false) , m_redirectCount(0), m_gotXml(false), m_textCodec(0), m_network(0), m_nam(manager) , m_responseType() + , m_parsedDocument() { } @@ -1233,11 +1250,12 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url) m_network = networkAccessManager()->put(request, m_data); } else if (m_method == QLatin1String("DELETE")) { m_network = networkAccessManager()->deleteResource(request); - } else if (m_method == QLatin1String("OPTIONS")) { + } else if ((m_method == QLatin1String("OPTIONS")) || + m_method == QLatin1String("PROPFIND")) { QBuffer *buffer = new QBuffer; buffer->setData(m_data); buffer->open(QIODevice::ReadOnly); - m_network = networkAccessManager()->sendCustomRequest(request, QByteArrayLiteral("OPTIONS"), buffer); + m_network = networkAccessManager()->sendCustomRequest(request, QByteArray(m_method.toUtf8().constData()), buffer); buffer->setParent(m_network); } @@ -1478,6 +1496,32 @@ void QQmlXMLHttpRequest::setResponseType(const QString &responseType) m_responseType = responseType; } +QV4::ReturnedValue QQmlXMLHttpRequest::jsonResponseBody(QV4::ExecutionEngine* engine) +{ + if (m_parsedDocument.isEmpty()) { + Scope scope(engine); + + QJsonParseError error; + const QString& jtext = responseBody(); + JsonParser parser(scope.engine, jtext.constData(), jtext.length()); + ScopedValue jsonObject(scope, parser.parse(&error)); + if (error.error != QJsonParseError::NoError) + return engine->throwSyntaxError(QStringLiteral("JSON.parse: Parse error")); + + m_parsedDocument.set(scope.engine, jsonObject); + } + + return m_parsedDocument.value(); +} + +QV4::ReturnedValue QQmlXMLHttpRequest::xmlResponseBody(QV4::ExecutionEngine* engine) +{ + if (m_parsedDocument.isEmpty()) { + m_parsedDocument.set(engine, Document::load(engine, rawResponseBody())); + } + + return m_parsedDocument.value(); +} #ifndef QT_NO_TEXTCODEC QTextCodec* QQmlXMLHttpRequest::findTextCodec() const @@ -1599,7 +1643,7 @@ struct QQmlXMLHttpRequestWrapper : Object { struct QQmlXMLHttpRequestCtor : FunctionObject { QQmlXMLHttpRequestCtor(ExecutionEngine *engine); - Object *proto; + Pointer<Object> proto; }; } @@ -1625,9 +1669,9 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject c->proto->mark(e); FunctionObject::markObjects(that, e); } - static ReturnedValue construct(Managed *that, QV4::CallData *) + static ReturnedValue construct(const Managed *that, QV4::CallData *) { - Scope scope(static_cast<QQmlXMLHttpRequestCtor *>(that)->engine()); + Scope scope(static_cast<const QQmlXMLHttpRequestCtor *>(that)->engine()); Scoped<QQmlXMLHttpRequestCtor> ctor(scope, that->as<QQmlXMLHttpRequestCtor>()); if (!ctor) return scope.engine->throwTypeError(); @@ -1639,7 +1683,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject return w.asReturnedValue(); } - static ReturnedValue call(Managed *, QV4::CallData *) { + static ReturnedValue call(const Managed *, QV4::CallData *) { return Primitive::undefinedValue().asReturnedValue(); } @@ -1679,7 +1723,7 @@ Heap::QQmlXMLHttpRequestCtor::QQmlXMLHttpRequestCtor(ExecutionEngine *engine) ctor->defineReadonlyProperty(QStringLiteral("DONE"), Primitive::fromInt32(4)); if (!ctor->d()->proto) ctor->setupProto(); - ScopedString s(scope, engine->id_prototype); + ScopedString s(scope, engine->id_prototype()); ctor->defineDefaultProperty(s, ScopedObject(scope, ctor->d()->proto)); } @@ -1739,14 +1783,15 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx) method != QLatin1String("HEAD") && method != QLatin1String("POST") && method != QLatin1String("DELETE") && - method != QLatin1String("OPTIONS")) + method != QLatin1String("OPTIONS") && + method != QLatin1String("PROPFIND")) V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Unsupported HTTP method type"); // Argument 1 - URL QUrl url = QUrl(ctx->args()[1].toQStringNoThrow()); if (url.isRelative()) - url = scope.engine->v8Engine->callingContext()->resolvedUrl(url); + url = scope.engine->callingQmlContext()->resolvedUrl(url); bool async = true; // Argument 2 - async (optional) @@ -1965,7 +2010,9 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseXML(CallContext *ctx) r->readyState() != QQmlXMLHttpRequest::Done)) { return Encode::null(); } else { - return Document::load(scope.engine, r->rawResponseBody()); + if (r->responseType().isEmpty()) + r->setResponseType(QLatin1String("document")); + return r->xmlResponseBody(scope.engine); } } @@ -1986,6 +2033,10 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_get_response(CallContext *ctx) return QV4::Encode(scope.engine->newString(r->responseBody())); } else if (responseType.compare(QLatin1String("arraybuffer"), Qt::CaseInsensitive) == 0) { return QV4::Encode(scope.engine->newArrayBuffer(r->rawResponseBody())); + } else if (responseType.compare(QLatin1String("json"), Qt::CaseInsensitive) == 0) { + return r->jsonResponseBody(scope.engine); + } else if (responseType.compare(QLatin1String("document"), Qt::CaseInsensitive) == 0) { + return r->xmlResponseBody(scope.engine); } else { return QV4::Encode(scope.engine->newString(QString())); } @@ -2033,7 +2084,7 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4) Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->alloc<QQmlXMLHttpRequestCtor>(v4)); ScopedString s(scope, v4->newString(QStringLiteral("XMLHttpRequest"))); - v4->globalObject()->defineReadonlyProperty(s, ctor); + v4->globalObject->defineReadonlyProperty(s, ctor); QQmlXMLHttpRequestData *data = new QQmlXMLHttpRequestData; return data; diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index f53b9a0c7d..ce86fd3923 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -41,7 +41,8 @@ #include <private/qv8engine_p.h> #include <QFileInfo> -#include <private/qqmlprofilerservice_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmlglobal_p.h> #include <private/qqmlplatform_p.h> @@ -51,6 +52,7 @@ #include <private/qv4include_p.h> #include <private/qv4context_p.h> #include <private/qv4stringobject_p.h> +#include <private/qv4dateobject_p.h> #include <private/qv4mm_p.h> #include <private/qv4jsonobject_p.h> #include <private/qv4objectproto_p.h> @@ -669,7 +671,7 @@ ReturnedValue QtObject::method_formatTime(QV4::CallContext *ctx) QVariant argVariant = ctx->engine()->toVariant(ctx->args()[0], -1); QTime time; - if (ctx->args()[0].asDateObject() || (argVariant.type() == QVariant::String)) + if (ctx->args()[0].as<DateObject>() || (argVariant.type() == QVariant::String)) time = argVariant.toDateTime().time(); else // if (argVariant.type() == QVariant::Time), or invalid. time = argVariant.toTime(); @@ -839,21 +841,21 @@ ReturnedValue QtObject::method_openUrlExternally(QV4::CallContext *ctx) */ ReturnedValue QtObject::method_resolvedUrl(QV4::CallContext *ctx) { - QV8Engine *v8engine = ctx->d()->engine->v8Engine; + ExecutionEngine *v4 = ctx->engine(); - QUrl url = ctx->engine()->toVariant(ctx->args()[0], -1).toUrl(); - QQmlEngine *e = v8engine->engine(); + QUrl url = v4->toVariant(ctx->args()[0], -1).toUrl(); + QQmlEngine *e = v4->qmlEngine(); QQmlEnginePrivate *p = 0; if (e) p = QQmlEnginePrivate::get(e); if (p) { - QQmlContextData *ctxt = v8engine->callingContext(); + QQmlContextData *ctxt = v4->callingQmlContext(); if (ctxt) - return ctx->d()->engine->newString(ctxt->resolvedUrl(url).toString())->asReturnedValue(); + return v4->newString(ctxt->resolvedUrl(url).toString())->asReturnedValue(); else - return ctx->d()->engine->newString(url.toString())->asReturnedValue(); + return v4->newString(url.toString())->asReturnedValue(); } - return ctx->d()->engine->newString(e->baseUrl().resolved(url).toString())->asReturnedValue(); + return v4->newString(e->baseUrl().resolved(url).toString())->asReturnedValue(); } /*! @@ -983,7 +985,7 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) QV8Engine *v8engine = ctx->d()->engine->v8Engine; QQmlEngine *engine = v8engine->engine(); - QQmlContextData *context = v8engine->callingContext(); + QQmlContextData *context = scope.engine->callingQmlContext(); Q_ASSERT(context); QQmlContext *effectiveContext = 0; if (context->isPragmaLibraryContext) @@ -1090,7 +1092,7 @@ ReturnedValue QtObject::method_createComponent(CallContext *ctx) QV8Engine *v8engine = ctx->d()->engine->v8Engine; QQmlEngine *engine = v8engine->engine(); - QQmlContextData *context = v8engine->callingContext(); + QQmlContextData *context = scope.engine->callingQmlContext(); Q_ASSERT(context); QQmlContextData *effectiveContext = context; if (context->isPragmaLibraryContext) @@ -1178,7 +1180,7 @@ ReturnedValue QtObject::method_locale(CallContext *ctx) return QQmlLocale::locale(ctx->engine(), code); } -Heap::QQmlBindingFunction::QQmlBindingFunction(QV4::FunctionObject *originalFunction) +Heap::QQmlBindingFunction::QQmlBindingFunction(const QV4::FunctionObject *originalFunction) : QV4::Heap::FunctionObject(originalFunction->scope(), originalFunction->name()) , originalFunction(originalFunction->d()) { @@ -1191,10 +1193,10 @@ void QQmlBindingFunction::initBindingLocation() d()->bindingLocation.line = frame.line; } -ReturnedValue QQmlBindingFunction::call(Managed *that, CallData *callData) +ReturnedValue QQmlBindingFunction::call(const Managed *that, CallData *callData) { - Scope scope(static_cast<QQmlBindingFunction*>(that)->engine()); - ScopedFunctionObject function(scope, static_cast<QQmlBindingFunction*>(that)->d()->originalFunction); + Scope scope(static_cast<const QQmlBindingFunction*>(that)->engine()); + ScopedFunctionObject function(scope, static_cast<const QQmlBindingFunction*>(that)->d()->originalFunction); return function->call(callData); } @@ -1256,7 +1258,7 @@ ReturnedValue QtObject::method_binding(CallContext *ctx) { if (ctx->argc() != 1) V4THROW_ERROR("binding() requires 1 argument"); - QV4::FunctionObject *f = ctx->args()[0].asFunctionObject(); + const QV4::FunctionObject *f = ctx->args()[0].as<FunctionObject>(); if (!f) V4THROW_TYPE("binding(): argument (binding expression) must be a function"); @@ -1267,7 +1269,7 @@ ReturnedValue QtObject::method_binding(CallContext *ctx) ReturnedValue QtObject::method_get_platform(CallContext *ctx) { // ### inefficient. Should be just a value based getter - Object *o = ctx->thisObject().asObject(); + Object *o = ctx->thisObject().as<Object>(); if (!o) return ctx->engine()->throwTypeError(); QtObject *qt = o->as<QtObject>(); @@ -1284,7 +1286,7 @@ ReturnedValue QtObject::method_get_platform(CallContext *ctx) ReturnedValue QtObject::method_get_application(CallContext *ctx) { // ### inefficient. Should be just a value based getter - Object *o = ctx->thisObject().asObject(); + Object *o = ctx->thisObject().as<Object>(); if (!o) return ctx->engine()->throwTypeError(); QtObject *qt = o->as<QtObject>(); @@ -1353,12 +1355,12 @@ static QString jsStack(QV4::ExecutionEngine *engine) { QString stackFrame; if (frame.column >= 0) - stackFrame = QString::fromLatin1("%1 (%2:%3:%4)").arg(frame.function, + stackFrame = QStringLiteral("%1 (%2:%3:%4)").arg(frame.function, frame.source, QString::number(frame.line), QString::number(frame.column)); else - stackFrame = QString::fromLatin1("%1 (%2:%3)").arg(frame.function, + stackFrame = QStringLiteral("%1 (%2:%3)").arg(frame.function, frame.source, QString::number(frame.line)); @@ -1379,7 +1381,7 @@ static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *c if (i != 0) result.append(QLatin1Char(' ')); - if (ctx->args()[i].asArrayObject()) + if (ctx->args()[i].as<ArrayObject>()) result.append(QStringLiteral("[") + ctx->args()[i].toQStringNoThrow() + QStringLiteral("]")); else result.append(ctx->args()[i].toQStringNoThrow()); @@ -1446,10 +1448,11 @@ QV4::ReturnedValue ConsoleObject::method_profile(CallContext *ctx) const QByteArray baSource = frame.source.toUtf8(); const QByteArray baFunction = frame.function.toUtf8(); QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData()); - if (!QQmlDebugService::isDebuggingEnabled()) { + QQmlProfilerService *service = QQmlDebugConnector::service<QQmlProfilerService>(); + if (!service) { logger.warning("Cannot start profiling because debug service is disabled. Start with -qmljsdebugger=port:XXXXX."); } else { - QQmlProfilerService::instance()->startProfiling(v4->qmlEngine()); + service->startProfiling(v4->qmlEngine()); logger.debug("Profiling started."); } @@ -1465,10 +1468,11 @@ QV4::ReturnedValue ConsoleObject::method_profileEnd(CallContext *ctx) const QByteArray baFunction = frame.function.toUtf8(); QMessageLogger logger(baSource.constData(), frame.line, baFunction.constData()); - if (!QQmlDebugService::isDebuggingEnabled()) { + QQmlProfilerService *service = QQmlDebugConnector::service<QQmlProfilerService>(); + if (!service) { logger.warning("Ignoring console.profileEnd(): the debug service is disabled."); } else { - QQmlProfilerService::instance()->stopProfiling(v4->qmlEngine()); + service->stopProfiling(v4->qmlEngine()); logger.debug("Profiling ended."); } @@ -1612,7 +1616,7 @@ void QV4::GlobalExtensions::init(QQmlEngine *qmlEngine, Object *globalObject) globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); // string prototype extension - v4->stringPrototype.asObject()->defineDefaultProperty(QStringLiteral("arg"), method_string_arg); + v4->stringPrototype()->defineDefaultProperty(QStringLiteral("arg"), method_string_arg); } @@ -1726,9 +1730,8 @@ ReturnedValue GlobalExtensions::method_qsTr(CallContext *ctx) V4THROW_ERROR("qsTr(): third argument (n) must be a number"); Scope scope(ctx); - QV8Engine *v8engine = ctx->d()->engine->v8Engine; QString context; - if (QQmlContextData *ctxt = v8engine->callingContext()) { + if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { QString path = ctxt->urlString(); int lastSlash = path.lastIndexOf(QLatin1Char('/')); int lastDot = path.lastIndexOf(QLatin1Char('.')); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index b78375118b..bdd53fe601 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -68,8 +68,8 @@ struct ConsoleObject : Object { }; struct QQmlBindingFunction : FunctionObject { - QQmlBindingFunction(QV4::FunctionObject *originalFunction); - FunctionObject *originalFunction; + QQmlBindingFunction(const QV4::FunctionObject *originalFunction); + Pointer<FunctionObject> originalFunction; // Set when the binding is created later QQmlSourceLocation bindingLocation; }; @@ -166,7 +166,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject void initBindingLocation(); // from caller stack trace - static ReturnedValue call(Managed *that, CallData *callData); + static ReturnedValue call(const Managed *that, CallData *callData); static void markObjects(Heap::Base *that, ExecutionEngine *e); }; diff --git a/src/qml/qml/v8/qv4domerrors.cpp b/src/qml/qml/v8/qv4domerrors.cpp index c318e2e550..1baaa113aa 100644 --- a/src/qml/qml/v8/qv4domerrors.cpp +++ b/src/qml/qml/v8/qv4domerrors.cpp @@ -59,7 +59,7 @@ void qt_add_domexceptions(ExecutionEngine *e) domexception->defineReadonlyProperty(QStringLiteral("INVALID_ACCESS_ERR"), Primitive::fromInt32(DOMEXCEPTION_INVALID_ACCESS_ERR)); domexception->defineReadonlyProperty(QStringLiteral("VALIDATION_ERR"), Primitive::fromInt32(DOMEXCEPTION_VALIDATION_ERR)); domexception->defineReadonlyProperty(QStringLiteral("TYPE_MISMATCH_ERR"), Primitive::fromInt32(DOMEXCEPTION_TYPE_MISMATCH_ERR)); - e->globalObject()->defineDefaultProperty(QStringLiteral("DOMException"), domexception); + e->globalObject->defineDefaultProperty(QStringLiteral("DOMException"), domexception); } QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv4sqlerrors.cpp b/src/qml/qml/v8/qv4sqlerrors.cpp index b7a5b71540..c61e57560d 100644 --- a/src/qml/qml/v8/qv4sqlerrors.cpp +++ b/src/qml/qml/v8/qv4sqlerrors.cpp @@ -51,7 +51,7 @@ void qt_add_sqlexceptions(QV4::ExecutionEngine *engine) sqlexception->defineReadonlyProperty(QStringLiteral("SYNTAX_ERR"), Primitive::fromInt32(SQLEXCEPTION_SYNTAX_ERR)); sqlexception->defineReadonlyProperty(QStringLiteral("CONSTRAINT_ERR"), Primitive::fromInt32(SQLEXCEPTION_CONSTRAINT_ERR)); sqlexception->defineReadonlyProperty(QStringLiteral("TIMEOUT_ERR"), Primitive::fromInt32(SQLEXCEPTION_TIMEOUT_ERR)); - engine->globalObject()->defineDefaultProperty(QStringLiteral("SQLException"), sqlexception); + engine->globalObject->defineDefaultProperty(QStringLiteral("SQLException"), sqlexception); } QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index a7c63c9df1..6cb316ce9f 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -61,7 +61,7 @@ #include <QtCore/qdatastream.h> #include <private/qsimd_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4objectiterator_p.h> #include <private/qv4mm_p.h> @@ -171,15 +171,10 @@ const QSet<QString> &QV8Engine::illegalNames() const return m_illegalNames; } -QQmlContextData *QV8Engine::callingContext() -{ - return QV4::QmlContextWrapper::callingContext(m_v4Engine); -} - void QV8Engine::initializeGlobal() { QV4::Scope scope(m_v4Engine); - QV4::GlobalExtensions::init(m_engine, m_v4Engine->globalObject()); + QV4::GlobalExtensions::init(m_engine, m_v4Engine->globalObject); QQmlLocale::registerStringLocaleCompare(m_v4Engine); QQmlDateExtension::registerExtension(m_v4Engine); @@ -191,9 +186,9 @@ void QV8Engine::initializeGlobal() qt_add_sqlexceptions(m_v4Engine); { - for (uint i = 0; i < m_v4Engine->globalObject()->internalClass()->size; ++i) { - if (m_v4Engine->globalObject()->internalClass()->nameMap.at(i)) - m_illegalNames.insert(m_v4Engine->globalObject()->internalClass()->nameMap.at(i)->string); + for (uint i = 0; i < m_v4Engine->globalObject->internalClass()->size; ++i) { + if (m_v4Engine->globalObject->internalClass()->nameMap.at(i)) + m_illegalNames.insert(m_v4Engine->globalObject->internalClass()->nameMap.at(i)->string); } } @@ -229,7 +224,7 @@ void QV8Engine::freezeObject(const QV4::Value &value) QV4::ScopedFunctionObject f(scope, m_freezeObject.value()); QV4::ScopedCallData callData(scope, 1); callData->args[0] = value; - callData->thisObject = m_v4Engine->globalObject(); + callData->thisObject = m_v4Engine->globalObject; f->call(callData); } @@ -266,9 +261,7 @@ void QV8Engine::setExtensionData(int index, Deletable *data) void QV8Engine::initQmlGlobalObject() { initializeGlobal(); - QV4::Scope scope(m_v4Engine); - QV4::ScopedValue v(scope, m_v4Engine->globalObject()); - freezeObject(v); + freezeObject(*m_v4Engine->globalObject); } void QV8Engine::setEngine(QQmlEngine *engine) @@ -279,7 +272,7 @@ void QV8Engine::setEngine(QQmlEngine *engine) QV4::ReturnedValue QV8Engine::global() { - return m_v4Engine->globalObject()->asReturnedValue(); + return m_v4Engine->globalObject->asReturnedValue(); } void QV8Engine::startTimer(const QString &timerName) diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index fb538772d1..5d12244dcb 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -60,7 +60,7 @@ #include <private/qqmlpropertycache_p.h> #include <private/qv4qobjectwrapper_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4object_p.h> #include <private/qv4identifier_p.h> @@ -101,19 +101,19 @@ namespace QV4 { return rv; \ } \ -// Used to allow a QObject method take and return raw V8 handles without having to expose -// v8 in the public API. +// Used to allow a QObject method take and return raw V4 handles without having to expose +// 48 in the public API. // Use like this: // class MyClass : public QObject { // Q_OBJECT // ... -// Q_INVOKABLE void myMethod(QQmlV8Function*); +// Q_INVOKABLE void myMethod(QQmlV4Function*); // }; // The QQmlV8Function - and consequently the arguments and return value - only remains // valid during the call. If the return value isn't set within myMethod(), the will return // undefined. class QV8Engine; -// ### GC + class QQmlV4Function { public: @@ -133,7 +133,7 @@ private: const QV4::Value &global, QQmlContextData *c, QV4::ExecutionEngine *e) : callData(callData), retVal(retVal), ctx(c), e(e) { - callData->thisObject.val = global.asReturnedValue(); + callData->thisObject = QV4::Value::fromReturnedValue(global.asReturnedValue()); } QV4::CallData *callData; @@ -163,8 +163,6 @@ class QQmlContextData; class Q_QML_PRIVATE_EXPORT QV8Engine { friend class QJSEngine; - // ### GC - typedef QSet<QV4::Heap::Object *> V8ObjectSet; public: static QV8Engine* get(QJSEngine* q) { Q_ASSERT(q); return q->handle(); } // static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } @@ -192,8 +190,6 @@ public: Deletable *listModelData() { return m_listModelData; } void setListModelData(Deletable *d) { if (m_listModelData) delete m_listModelData; m_listModelData = d; } - QQmlContextData *callingContext(); - void freezeObject(const QV4::Value &value); // Return the network access manager for this engine. By default this returns the network diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index 7814fa6d56..a154da8323 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -52,8 +52,8 @@ QT_BEGIN_NAMESPACE class QQmlBindPrivate : public QObjectPrivate { public: - QQmlBindPrivate() : componentComplete(true), obj(0), prevBind(0) {} - ~QQmlBindPrivate() { if (prevBind) prevBind->destroy(); } + QQmlBindPrivate() : componentComplete(true), obj(0) {} + ~QQmlBindPrivate() { } QQmlNullableValue<bool> when; bool componentComplete; @@ -61,7 +61,7 @@ public: QString propName; QQmlNullableValue<QVariant> value; QQmlProperty prop; - QQmlAbstractBinding *prevBind; + QQmlAbstractBinding::Ptr prevBind; }; @@ -70,65 +70,56 @@ public: \instantiates QQmlBind \inqmlmodule QtQml \ingroup qtquick-interceptors - \brief Enables the arbitrary creation of property bindings + \brief Enables the arbitrary creation of property bindings. - \section1 Binding to an Inaccessible Property + In QML, property bindings result in a dependency between the properties of + different objects. - Sometimes it is necessary to bind to a property of an object that wasn't - directly instantiated by QML - generally a property of a class exported - to QML by C++. In these cases, regular property binding doesn't work. Binding - allows you to bind any value to any property. + \section1 Binding to an inaccessible property + + Sometimes it is necessary to bind an object's property to + that of another object that isn't directly instantiated by QML, such as a + property of a class exported to QML by C++. You can use the Binding type + to establish this dependency; binding any value to any object's property. + + For example, in a C++ application that maps an "app.enteredText" property + into QML, you can use Binding to update the enteredText property. - For example, imagine a C++ application that maps an "app.enteredText" - property into QML. You could use Binding to update the enteredText property - like this. \code TextEdit { id: myTextField; text: "Please type here..." } Binding { target: app; property: "enteredText"; value: myTextField.text } \endcode - Whenever the text in the TextEdit is updated, the C++ property will be - updated also. - \section1 "Single-branch" conditional binding + When \c{text} changes, the C++ property \c{enteredText} will update + automatically. - In some circumstances you may want to control the value of a property - only when a certain condition is true (and relinquish control in all - other circumstances). This often isn't possible to accomplish with a direct - binding, as you need to supply values for all possible branches. + \section1 Conditional bindings - \code + In some cases you may want to modify the value of a property when a certain + condition is met but leave it unmodified otherwise. Often, it's not possible + to do this with direct bindings, as you have to supply values for all + possible branches. + + For example, the code snippet below results in a warning whenever you + release the mouse. This is because the value of the binding is undefined + when the mouse isn't pressed. + + \qml // produces warning: "Unable to assign [undefined] to double value" value: if (mouse.pressed) mouse.mouseX - \endcode + \endqml - The above example will produce a warning whenever we release the mouse, as the value - of the binding is undefined when the mouse isn't pressed. We can use the Binding - type to rewrite the above code and avoid the warning. + The Binding type can prevent this warning. - \code + \qml Binding on value { when: mouse.pressed value: mouse.mouseX } - \endcode - - The Binding type will also restore any previously set direct bindings on - the property. In that sense, it functions much like a simplified State. - - \qml - // this is equivalent to the above Binding - State { - name: "pressed" - when: mouse.pressed - PropertyChanges { - target: obj - value: mouse.mouseX - } - } \endqml - If the binding target or binding property is changed, the bound value is - immediately pushed onto the new target. + The Binding type restores any previously set direct bindings on the + property. \sa {Qt QML} */ @@ -277,22 +268,17 @@ void QQmlBind::eval() if (!d->when) { //restore any previous binding if (d->prevBind) { - QQmlAbstractBinding *tmp = d->prevBind; + QQmlAbstractBinding::Ptr p = d->prevBind; d->prevBind = 0; - tmp = QQmlPropertyPrivate::setBinding(d->prop, tmp); - if (tmp) //should this ever be true? - tmp->destroy(); + QQmlPropertyPrivate::setBinding(p.data()); } return; } //save any set binding for restoration - QQmlAbstractBinding *tmp; - tmp = QQmlPropertyPrivate::setBinding(d->prop, 0); - if (tmp && d->prevBind) - tmp->destroy(); - else if (!d->prevBind) - d->prevBind = tmp; + if (!d->prevBind) + d->prevBind = QQmlPropertyPrivate::binding(d->prop); + QQmlPropertyPrivate::removeBinding(d->prop); } d->prop.write(d->value.value); diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 0c81855e49..6a93410ecb 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -165,7 +165,7 @@ void QQmlConnections::setTarget(QObject *obj) foreach (QQmlBoundSignal *s, d->boundsignals) { // It is possible that target is being changed due to one of our signal // handlers -> use deleteLater(). - if (s->isEvaluating()) + if (s->isNotifying()) (new QQmlBoundSignalDeleter(s))->deleteLater(); else delete s; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 201fd4572c..bc70e68904 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -44,7 +44,7 @@ #include <private/qqmlincubator_p.h> #include <private/qqmlcompiler_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> #include <qv4objectiterator_p.h> @@ -86,16 +86,11 @@ struct DelegateModelGroupFunction : QV4::FunctionObject return scope->engine()->memoryManager->alloc<DelegateModelGroupFunction>(scope, flag, code); } - static QV4::ReturnedValue construct(QV4::Managed *m, QV4::CallData *) + static QV4::ReturnedValue call(const QV4::Managed *that, QV4::CallData *callData) { - return static_cast<DelegateModelGroupFunction *>(m)->engine()->throwTypeError(); - } - - static QV4::ReturnedValue call(QV4::Managed *that, QV4::CallData *callData) - { - QV4::ExecutionEngine *v4 = static_cast<DelegateModelGroupFunction *>(that)->engine(); + QV4::ExecutionEngine *v4 = static_cast<const DelegateModelGroupFunction *>(that)->engine(); QV4::Scope scope(v4); - QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<DelegateModelGroupFunction *>(that)); + QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that)); QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject); if (!o) return v4->throwTypeError(QStringLiteral("Not a valid VisualData object")); @@ -1062,7 +1057,7 @@ int QQmlDelegateModel::indexOf(QObject *item, QObject *) const return -1; } -void QQmlDelegateModel::setWatchedRoles(QList<QByteArray> roles) +void QQmlDelegateModel::setWatchedRoles(const QList<QByteArray> &roles) { Q_D(QQmlDelegateModel); d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); @@ -1637,7 +1632,7 @@ bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const if (!object.isObject()) return false; - QV4::ExecutionEngine *v4 = object.asObject()->engine(); + QV4::ExecutionEngine *v4 = object.as<QV4::Object>()->engine(); QV4::Scope scope(v4); QV4::ScopedObject o(scope, object); if (!o) @@ -2511,7 +2506,7 @@ bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *ind if (!value.isObject()) return false; - QV4::ExecutionEngine *v4 = value.asObject()->engine(); + QV4::ExecutionEngine *v4 = value.as<QV4::Object>()->engine(); QV4::Scope scope(v4); QV4::Scoped<QQmlDelegateModelItemObject> object(scope, value); @@ -2579,9 +2574,9 @@ void QQmlDelegateModelGroup::insert(QQmlV4Function *args) groups |= model->m_cacheMetaType->parseGroups(val); } - if (v->asArrayObject()) { + if (v->as<QV4::ArrayObject>()) { return; - } else if (v->asObject()) { + } else if (v->as<QV4::Object>()) { model->insert(before, v, groups); model->emitChanges(); } @@ -2626,7 +2621,7 @@ void QQmlDelegateModelGroup::create(QQmlV4Function *args) if (i < args->length() && index >= 0 && index <= model->m_compositor.count(group)) { v = (*args)[i]; - if (v->asObject()) { + if (v->as<QV4::Object>()) { int groups = 1 << d->group; if (++i < args->length()) { QV4::ScopedValue val(scope, (*args)[i]); @@ -3190,7 +3185,7 @@ QString QQmlPartsModel::stringValue(int index, const QString &role) return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); } -void QQmlPartsModel::setWatchedRoles(QList<QByteArray> roles) +void QQmlPartsModel::setWatchedRoles(const QList<QByteArray> &roles) { QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); @@ -3286,12 +3281,12 @@ public: quint32 count() const { return d()->changes.count(); } const QQmlChangeSet::Change &at(int index) const { return d()->changes.at(index); } - static QV4::ReturnedValue getIndexed(QV4::Managed *m, uint index, bool *hasProperty) + static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty) { Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - QV4::ExecutionEngine *v4 = static_cast<QQmlDelegateModelGroupChangeArray *>(m)->engine(); + QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine(); QV4::Scope scope(v4); - QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<QQmlDelegateModelGroupChangeArray *>(m)); + QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m)); if (index >= array->count()) { if (hasProperty) @@ -3311,12 +3306,12 @@ public: return object.asReturnedValue(); } - static QV4::ReturnedValue get(QV4::Managed *m, QV4::String *name, bool *hasProperty) + static QV4::ReturnedValue get(const QV4::Managed *m, QV4::String *name, bool *hasProperty) { Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - QQmlDelegateModelGroupChangeArray *array = static_cast<QQmlDelegateModelGroupChangeArray *>(m); + const QQmlDelegateModelGroupChangeArray *array = static_cast<const QQmlDelegateModelGroupChangeArray *>(m); - if (name->equals(array->engine()->id_length)) { + if (name->equals(array->engine()->id_length())) { if (hasProperty) *hasProperty = true; return QV4::Encode(array->count()); diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h index 5ebffd5b9d..e222fadec2 100644 --- a/src/qml/types/qqmldelegatemodel_p.h +++ b/src/qml/types/qqmldelegatemodel_p.h @@ -96,7 +96,7 @@ public: ReleaseFlags release(QObject *object); void cancel(int index); virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList<QByteArray> roles); + virtual void setWatchedRoles(const QList<QByteArray> &roles); int indexOf(QObject *object, QObject *objectContext) const; diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index dc289eb35e..5c28021c0e 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -356,7 +356,7 @@ public: ReleaseFlags release(QObject *item); QString stringValue(int index, const QString &role); QList<QByteArray> watchedRoles() const { return m_watchedRoles; } - void setWatchedRoles(QList<QByteArray> roles); + void setWatchedRoles(const QList<QByteArray> &roles); int indexOf(QObject *item, QObject *objectContext) const; diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index ed97690adb..de46020ad8 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -88,7 +88,7 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::Da if (node) { const Role &r = *node->value; if (type != r.type) - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + qmlInfo(0) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); return r; } @@ -101,7 +101,7 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data if (node) { const Role &r = *node->value; if (type != r.type) - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + qmlInfo(0) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); return r; } @@ -150,7 +150,9 @@ const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::R ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0) { - for (int i=0 ; i < other->roles.count() ; ++i) { + const int otherRolesCount = other->roles.count(); + roles.reserve(otherRolesCount); + for (int i=0 ; i < otherRolesCount; ++i) { Role *role = new Role(other->roles[i]); roles.append(role); roleHash.insert(role->name, role); @@ -421,13 +423,13 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) int roleIndex = -1; // Add the value now - if (QV4::String *s = propertyValue->asString()) { + if (const QV4::String *s = propertyValue->as<QV4::String>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); roleIndex = e->setStringProperty(r, s->toQString()); } else if (propertyValue->isNumber()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); roleIndex = e->setDoubleProperty(r, propertyValue->asDouble()); - } else if (QV4::ArrayObject *a = propertyValue->asArrayObject()) { + } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); ListModel *subModel = new ListModel(r.subLayout, 0, -1); @@ -441,11 +443,11 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) } else if (propertyValue->isBoolean()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); roleIndex = e->setBoolProperty(r, propertyValue->booleanValue()); - } else if (QV4::DateObject *dd = propertyValue->asDateObject()) { + } else if (QV4::DateObject *dd = propertyValue->as<QV4::DateObject>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); QDateTime dt = dd->toQDateTime(); roleIndex = e->setDateTimeProperty(r, dt); - } else if (QV4::Object *o = propertyValue->asObject()) { + } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { QObject *o = wrapper->object(); const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); @@ -502,7 +504,7 @@ void ListModel::set(int elementIndex, QV4::Object *object) if (r.type == ListLayout::Role::Number) { e->setDoublePropertyFast(r, propertyValue->asDouble()); } - } else if (QV4::ArrayObject *a = propertyValue->asArrayObject()) { + } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); if (r.type == ListLayout::Role::List) { ListModel *subModel = new ListModel(r.subLayout, 0, -1); @@ -520,13 +522,13 @@ void ListModel::set(int elementIndex, QV4::Object *object) if (r.type == ListLayout::Role::Bool) { e->setBoolPropertyFast(r, propertyValue->booleanValue()); } - } else if (QV4::DateObject *date = propertyValue->asDateObject()) { + } else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); if (r.type == ListLayout::Role::DateTime) { QDateTime dt = date->toQDateTime();; e->setDateTimePropertyFast(r, dt); } - } else if (QV4::Object *o = propertyValue->asObject()) { + } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { QObject *o = wrapper->object(); const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); @@ -1169,7 +1171,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d roleIndex = setStringProperty(role, qstr); } else if (d.isNumber()) { roleIndex = setDoubleProperty(role, d.asDouble()); - } else if (d.asArrayObject()) { + } else if (d.as<QV4::ArrayObject>()) { QV4::ScopedArrayObject a(scope, d); if (role.type == ListLayout::Role::List) { QV4::Scope scope(a->engine()); @@ -1183,11 +1185,11 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d } roleIndex = setListProperty(role, subModel); } else { - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); + qmlInfo(0) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); } } else if (d.isBoolean()) { roleIndex = setBoolProperty(role, d.booleanValue()); - } else if (d.asDateObject()) { + } else if (d.as<QV4::DateObject>()) { QV4::Scoped<QV4::DateObject> dd(scope, d); QDateTime dt = dd->toQDateTime(); roleIndex = setDateTimeProperty(role, dt); @@ -1330,8 +1332,8 @@ void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector<int> QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner); QVariantList subArray = value.toList(); - QVariantList::const_iterator subIt = subArray.begin(); - QVariantList::const_iterator subEnd = subArray.end(); + QVariantList::const_iterator subIt = subArray.cbegin(); + QVariantList::const_iterator subEnd = subArray.cend(); while (subIt != subEnd) { const QVariantMap &subObject = subIt->toMap(); subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); @@ -1398,8 +1400,8 @@ void DynamicRoleModelNodeMetaObject::propertyWritten(int index) QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel); QVariantList subArray = v.toList(); - QVariantList::const_iterator subIt = subArray.begin(); - QVariantList::const_iterator subEnd = subArray.end(); + QVariantList::const_iterator subIt = subArray.cbegin(); + QVariantList::const_iterator subEnd = subArray.cend(); while (subIt != subEnd) { const QVariantMap &subObject = subIt->toMap(); subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp index 3e53efd8b9..062d30c252 100644 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ b/src/qml/types/qqmlmodelsmodule.cpp @@ -48,6 +48,7 @@ void QQmlModelsModule::defineModule() qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel"); qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup"); qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel"); + qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel"); qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel"); } diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp index 1d892beabf..0076c26950 100644 --- a/src/qml/types/qqmlobjectmodel.cpp +++ b/src/qml/types/qqmlobjectmodel.cpp @@ -36,10 +36,12 @@ #include <QtCore/qcoreapplication.h> #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlinfo.h> #include <private/qqmlchangeset_p.h> #include <private/qqmlglobal_p.h> #include <private/qobject_p.h> +#include <private/qpodvector_p.h> #include <QtCore/qhash.h> #include <QtCore/qlist.h> @@ -67,9 +69,8 @@ public: QQmlObjectModelPrivate() : QObjectPrivate() {} static void children_append(QQmlListProperty<QObject> *prop, QObject *item) { - static_cast<QQmlObjectModelPrivate *>(prop->data)->children.append(Item(item)); - static_cast<QQmlObjectModelPrivate *>(prop->data)->itemAppended(); - static_cast<QQmlObjectModelPrivate *>(prop->data)->emitChildrenChanged(); + int index = static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count(); + static_cast<QQmlObjectModelPrivate *>(prop->data)->insert(index, item); } static int children_count(QQmlListProperty<QObject> *prop) { @@ -81,33 +82,77 @@ public: } static void children_clear(QQmlListProperty<QObject> *prop) { - static_cast<QQmlObjectModelPrivate *>(prop->data)->itemCleared(static_cast<QQmlObjectModelPrivate *>(prop->data)->children); - static_cast<QQmlObjectModelPrivate *>(prop->data)->children.clear(); - static_cast<QQmlObjectModelPrivate *>(prop->data)->emitChildrenChanged(); + static_cast<QQmlObjectModelPrivate *>(prop->data)->clear(); } - void itemAppended() { + void insert(int index, QObject *item) { Q_Q(QQmlObjectModel); - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.last().item); - attached->setIndex(children.count()-1); + children.insert(index, Item(item)); + for (int i = index; i < children.count(); ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(i); + } QQmlChangeSet changeSet; - changeSet.insert(children.count() - 1, 1); + changeSet.insert(index, 1); emit q->modelUpdated(changeSet, false); emit q->countChanged(); + emit q->childrenChanged(); } - void itemCleared(const QList<Item> &children) { + void move(int from, int to, int n) { Q_Q(QQmlObjectModel); - foreach (const Item &child, children) - emit q->destroyingItem(child.item); - emit q->countChanged(); + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + + QPODVector<QQmlObjectModelPrivate::Item, 4> store; + for (int i = 0; i < to - from; ++i) + store.append(children[from + n + i]); + for (int i = 0; i < n; ++i) + store.append(children[from + i]); + + for (int i = 0; i < store.count(); ++i) { + children[from + i] = store[i]; + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(from + i).item); + attached->setIndex(from + i); + } + + QQmlChangeSet changeSet; + changeSet.move(from, to, n, -1); + emit q->modelUpdated(changeSet, false); + emit q->childrenChanged(); } - void emitChildrenChanged() { + void remove(int index, int n) { Q_Q(QQmlObjectModel); + for (int i = index; i < index + n; ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(-1); + } + children.erase(children.begin() + index, children.begin() + index + n); + for (int i = index; i < children.count(); ++i) { + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); + attached->setIndex(i); + } + QQmlChangeSet changeSet; + changeSet.remove(index, n); + emit q->modelUpdated(changeSet, false); + emit q->countChanged(); emit q->childrenChanged(); } + void clear() { + Q_Q(QQmlObjectModel); + foreach (const Item &child, children) + emit q->destroyingItem(child.item); + remove(0, children.count()); + } + int indexOf(QObject *item) const { for (int i = 0; i < children.count(); ++i) if (children.at(i).item == item) @@ -258,4 +303,133 @@ QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) return QQmlObjectModelAttached::properties(obj); } +/*! + \qmlmethod object QtQml.Models::ObjectModel::get(int index) + \since 5.6 + + Returns the item at \a index in the model. This allows the item + to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + objectModel.append(objectComponent.createObject()) + console.log(objectModel.get(0).objectName); + objectModel.get(0).objectName = "first"; + } + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +QObject *QQmlObjectModel::get(int index) const +{ + Q_D(const QQmlObjectModel); + if (index < 0 || index >= d->children.count()) + return 0; + return d->children.at(index).item; +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::append(object item) + \since 5.6 + + Appends a new item to the end of the model. + + \code + objectModel.append(objectComponent.createObject()) + \endcode + + \sa insert(), remove() +*/ +void QQmlObjectModel::append(QObject *object) +{ + Q_D(QQmlObjectModel); + d->insert(count(), object); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::insert(int index, object item) + \since 5.6 + + Inserts a new item to the model at position \a index. + + \code + objectModel.insert(2, objectComponent.createObject()) + \endcode + + The \a index must be to an existing item in the list, or one past + the end of the list (equivalent to append). + + \sa append(), remove() +*/ +void QQmlObjectModel::insert(int index, QObject *object) +{ + Q_D(QQmlObjectModel); + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + d->insert(index, object); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::move(int from, int to, int n = 1) + \since 5.6 + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the model: + + \code + objectModel.move(0, objectModel.count - 3, 3) + \endcode + + \sa append() +*/ +void QQmlObjectModel::move(int from, int to, int n) +{ + Q_D(QQmlObjectModel); + if (n <= 0 || from == to) + return; + if (from < 0 || to < 0 || from + n > count() || to + n > count()) { + qmlInfo(this) << tr("move: out of range"); + return; + } + d->move(from, to, n); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::remove(int index, int n = 1) + \since 5.6 + + Removes the items at \a index from the model. + + \sa clear() +*/ +void QQmlObjectModel::remove(int index, int n) +{ + Q_D(QQmlObjectModel); + if (index < 0 || n <= 0 || index + n > count()) { + qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+n).arg(count()); + return; + } + d->remove(index, n); +} + +/*! + \qmlmethod QtQml.Models::ObjectModel::clear() + \since 5.6 + + Clears all items from the model. + + \sa append(), remove() +*/ +void QQmlObjectModel::clear() +{ + Q_D(QQmlObjectModel); + d->clear(); +} + QT_END_NAMESPACE diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h index 4c37a5ac30..f6ab2a19e2 100644 --- a/src/qml/types/qqmlobjectmodel_p.h +++ b/src/qml/types/qqmlobjectmodel_p.h @@ -61,7 +61,7 @@ public: virtual ReleaseFlags release(QObject *object) = 0; virtual void cancel(int) {} virtual QString stringValue(int, const QString &) = 0; - virtual void setWatchedRoles(QList<QByteArray> roles) = 0; + virtual void setWatchedRoles(const QList<QByteArray> &roles) = 0; virtual int indexOf(QObject *object, QObject *objectContext) const = 0; @@ -99,7 +99,7 @@ public: virtual QObject *object(int index, bool asynchronous=false); virtual ReleaseFlags release(QObject *object); virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList<QByteArray>) {} + virtual void setWatchedRoles(const QList<QByteArray> &) {} virtual int indexOf(QObject *object, QObject *objectContext) const; @@ -107,6 +107,15 @@ public: static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); + Q_REVISION(3) Q_INVOKABLE QObject *get(int index) const; + Q_REVISION(3) Q_INVOKABLE void append(QObject *object); + Q_REVISION(3) Q_INVOKABLE void insert(int index, QObject *object); + Q_REVISION(3) Q_INVOKABLE void move(int from, int to, int n = 1); + Q_REVISION(3) Q_INVOKABLE void remove(int index, int n = 1); + +public Q_SLOTS: + Q_REVISION(3) void clear(); + Q_SIGNALS: void childrenChanged(); @@ -120,7 +129,7 @@ class QQmlObjectModelAttached : public QObject public: QQmlObjectModelAttached(QObject *parent) - : QObject(parent), m_index(0) {} + : QObject(parent), m_index(-1) {} ~QQmlObjectModelAttached() { attachedProperties.remove(parent()); } diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index c2c6e5ef5c..0aa5dc4ef6 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -54,7 +54,7 @@ #include <private/qv8engine_p.h> #include <private/qv4serialize_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4script_p.h> #include <private/qv4scopedvalue_p.h> diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index 356970eef0..17f4ffd62b 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -38,7 +38,7 @@ #include <private/qqmlproperty_p.h> #include <private/qv8engine_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> QT_BEGIN_NAMESPACE @@ -159,7 +159,9 @@ public: signalIndexes.append(propertyId + signalOffset); } if (roles.isEmpty()) { - for (int propertyId = 0; propertyId < propertyRoles.count(); ++propertyId) + const int propertyRolesCount = propertyRoles.count(); + signalIndexes.reserve(propertyRolesCount); + for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId) signalIndexes.append(propertyId + signalOffset); } diff --git a/src/qmldevtools/qmldevtools.pro b/src/qmldevtools/qmldevtools.pro index df1b750282..85f21ce6f6 100644 --- a/src/qmldevtools/qmldevtools.pro +++ b/src/qmldevtools/qmldevtools.pro @@ -19,3 +19,4 @@ include(../3rdparty/masm/masm-defs.pri) include(../qml/parser/parser.pri) include(../qml/jsruntime/jsruntime.pri) include(../qml/compiler/compiler.pri) +include(../qml/memory/memory.pri) diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro index 24b87588b8..289a0584e0 100644 --- a/src/qmltest/qmltest.pro +++ b/src/qmltest/qmltest.pro @@ -1,8 +1,8 @@ TARGET = QtQuickTest DEFINES += QT_NO_URL_CAST_FROM_STRING -QT = core -QT_PRIVATE = testlib-private quick qml-private gui core-private +QT = core testlib-private +QT_PRIVATE = quick qml-private gui core-private # Testlib is only a private dependency, which results in our users not # inheriting testlibs's MODULE_CONFIG transitively. Make it explicit. diff --git a/src/qmltest/quicktest.h b/src/qmltest/quicktest.h index 49bd7edfaa..b317a07db4 100644 --- a/src/qmltest/quicktest.h +++ b/src/qmltest/quicktest.h @@ -35,9 +35,12 @@ #define QUICKTEST_H #include <QtQuickTest/quicktestglobal.h> +#include <QtTest/qtest.h> QT_BEGIN_NAMESPACE +QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS + Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, const char *sourceDir); #ifdef QUICK_TEST_SOURCE_DIR @@ -45,12 +48,16 @@ Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, #define QUICK_TEST_MAIN(name) \ int main(int argc, char **argv) \ { \ + QTEST_ADD_GPU_BLACKLIST_SUPPORT \ + QTEST_SET_MAIN_SOURCE_PATH \ return quick_test_main(argc, argv, #name, QUICK_TEST_SOURCE_DIR); \ } #define QUICK_TEST_OPENGL_MAIN(name) \ int main(int argc, char **argv) \ { \ + QTEST_ADD_GPU_BLACKLIST_SUPPORT \ + QTEST_SET_MAIN_SOURCE_PATH \ return quick_test_main(argc, argv, #name, QUICK_TEST_SOURCE_DIR); \ } @@ -59,12 +66,16 @@ Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, #define QUICK_TEST_MAIN(name) \ int main(int argc, char **argv) \ { \ + QTEST_ADD_GPU_BLACKLIST_SUPPORT \ + QTEST_SET_MAIN_SOURCE_PATH \ return quick_test_main(argc, argv, #name, 0); \ } #define QUICK_TEST_OPENGL_MAIN(name) \ int main(int argc, char **argv) \ { \ + QTEST_ADD_GPU_BLACKLIST_SUPPORT \ + QTEST_SET_MAIN_SOURCE_PATH \ return quick_test_main(argc, argv, #name, 0); \ } diff --git a/src/qmltest/quicktestevent.cpp b/src/qmltest/quicktestevent.cpp index df8de14c14..cfa80c4f19 100644 --- a/src/qmltest/quicktestevent.cpp +++ b/src/qmltest/quicktestevent.cpp @@ -50,7 +50,7 @@ QuickTestEvent::~QuickTestEvent() bool QuickTestEvent::keyPress(int key, int modifiers, int delay) { - QWindow *window = eventWindow(); + QWindow *window = activeWindow(); if (!window) return false; QTest::keyPress(window, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay); @@ -59,7 +59,7 @@ bool QuickTestEvent::keyPress(int key, int modifiers, int delay) bool QuickTestEvent::keyRelease(int key, int modifiers, int delay) { - QWindow *window = eventWindow(); + QWindow *window = activeWindow(); if (!window) return false; QTest::keyRelease(window, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay); @@ -68,7 +68,7 @@ bool QuickTestEvent::keyRelease(int key, int modifiers, int delay) bool QuickTestEvent::keyClick(int key, int modifiers, int delay) { - QWindow *window = eventWindow(); + QWindow *window = activeWindow(); if (!window) return false; QTest::keyClick(window, Qt::Key(key), Qt::KeyboardModifiers(modifiers), delay); @@ -78,7 +78,7 @@ bool QuickTestEvent::keyClick(int key, int modifiers, int delay) bool QuickTestEvent::keyPressChar(const QString &character, int modifiers, int delay) { QTEST_ASSERT(character.length() == 1); - QWindow *window = eventWindow(); + QWindow *window = activeWindow(); if (!window) return false; QTest::keyPress(window, character[0].toLatin1(), Qt::KeyboardModifiers(modifiers), delay); @@ -88,7 +88,7 @@ bool QuickTestEvent::keyPressChar(const QString &character, int modifiers, int d bool QuickTestEvent::keyReleaseChar(const QString &character, int modifiers, int delay) { QTEST_ASSERT(character.length() == 1); - QWindow *window = eventWindow(); + QWindow *window = activeWindow(); if (!window) return false; QTest::keyRelease(window, character[0].toLatin1(), Qt::KeyboardModifiers(modifiers), delay); @@ -98,7 +98,7 @@ bool QuickTestEvent::keyReleaseChar(const QString &character, int modifiers, int bool QuickTestEvent::keyClickChar(const QString &character, int modifiers, int delay) { QTEST_ASSERT(character.length() == 1); - QWindow *window = eventWindow(); + QWindow *window = activeWindow(); if (!window) return false; QTest::keyClick(window, character[0].toLatin1(), Qt::KeyboardModifiers(modifiers), delay); @@ -211,7 +211,7 @@ bool QuickTestEvent::mousePress (QObject *item, qreal x, qreal y, int button, int modifiers, int delay) { - QWindow *view = eventWindow(); + QWindow *view = eventWindow(item); if (!view) return false; QtQuickTest::mouseEvent(QtQuickTest::MousePress, view, item, @@ -226,7 +226,7 @@ bool QuickTestEvent::mouseWheel( QObject *item, qreal x, qreal y, int buttons, int modifiers, int xDelta, int yDelta, int delay) { - QWindow *view = eventWindow(); + QWindow *view = eventWindow(item); if (!view) return false; QtQuickTest::mouseWheel(view, item, Qt::MouseButtons(buttons), @@ -240,7 +240,7 @@ bool QuickTestEvent::mouseRelease (QObject *item, qreal x, qreal y, int button, int modifiers, int delay) { - QWindow *view = eventWindow(); + QWindow *view = eventWindow(item); if (!view) return false; QtQuickTest::mouseEvent(QtQuickTest::MouseRelease, view, item, @@ -254,7 +254,7 @@ bool QuickTestEvent::mouseClick (QObject *item, qreal x, qreal y, int button, int modifiers, int delay) { - QWindow *view = eventWindow(); + QWindow *view = eventWindow(item); if (!view) return false; QtQuickTest::mouseEvent(QtQuickTest::MouseClick, view, item, @@ -268,7 +268,7 @@ bool QuickTestEvent::mouseDoubleClick (QObject *item, qreal x, qreal y, int button, int modifiers, int delay) { - QWindow *view = eventWindow(); + QWindow *view = eventWindow(item); if (!view) return false; QtQuickTest::mouseEvent(QtQuickTest::MouseDoubleClick, view, item, @@ -282,7 +282,7 @@ bool QuickTestEvent::mouseDoubleClickSequence (QObject *item, qreal x, qreal y, int button, int modifiers, int delay) { - QWindow *view = eventWindow(); + QWindow *view = eventWindow(item); if (!view) return false; QtQuickTest::mouseEvent(QtQuickTest::MouseDoubleClickSequence, view, item, @@ -295,7 +295,7 @@ bool QuickTestEvent::mouseDoubleClickSequence bool QuickTestEvent::mouseMove (QObject *item, qreal x, qreal y, int delay, int buttons) { - QWindow *view = eventWindow(); + QWindow *view = eventWindow(item); if (!view) return false; QtQuickTest::mouseEvent(QtQuickTest::MouseMove, view, item, @@ -304,12 +304,23 @@ bool QuickTestEvent::mouseMove return true; } -QWindow *QuickTestEvent::eventWindow() +QWindow *QuickTestEvent::eventWindow(QObject *item) { - QQuickItem *sgitem = qobject_cast<QQuickItem *>(parent()); - if (sgitem) - return sgitem->window(); + QQuickItem *quickItem = qobject_cast<QQuickItem *>(item); + if (quickItem) + return quickItem->window(); + + QQuickItem *testParentitem = qobject_cast<QQuickItem *>(parent()); + if (testParentitem) + return testParentitem->window(); return 0; } +QWindow *QuickTestEvent::activeWindow() +{ + if (QWindow *window = QGuiApplication::focusWindow()) + return window; + return eventWindow(); +} + QT_END_NAMESPACE diff --git a/src/qmltest/quicktestevent_p.h b/src/qmltest/quicktestevent_p.h index b7f8f3eda2..338464c6b3 100644 --- a/src/qmltest/quicktestevent_p.h +++ b/src/qmltest/quicktestevent_p.h @@ -73,7 +73,8 @@ public Q_SLOTS: #endif private: - QWindow *eventWindow(); + QWindow *eventWindow(QObject *item = 0); + QWindow *activeWindow(); }; QT_END_NAMESPACE diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp index 3f91ce85a8..57ba65e588 100644 --- a/src/qmltest/quicktestresult.cpp +++ b/src/qmltest/quicktestresult.cpp @@ -34,6 +34,7 @@ #include "quicktestresult_p.h" #include <QtTest/qtestcase.h> #include <QtTest/qtestsystem.h> +#include <QtTest/private/qtestblacklist_p.h> #include <QtTest/private/qtestresult_p.h> #include <QtTest/private/qtesttable_p.h> #include <QtTest/private/qtestlog_p.h> @@ -108,6 +109,14 @@ public Q_SLOTS: pixel += x; return QColor::fromRgba(*pixel); } + + bool equals(QuickTestImageObject *other) const + { + if (!other) + return m_image.isNull(); + + return m_image == other->m_image; + } private: QImage m_image; }; @@ -205,6 +214,7 @@ void QuickTestResult::setFunctionName(const QString &name) QString fullName = d->testCaseName + QLatin1String("::") + name; QTestResult::setCurrentTestFunction (d->intern(fullName).constData()); + QTestPrivate::checkBlackLists(fullName.toUtf8().constData(), 0); } } else { QTestResult::setCurrentTestFunction(0); @@ -233,6 +243,7 @@ void QuickTestResult::setDataTag(const QString &tag) if (!tag.isEmpty()) { QTestData *data = &(QTest::newRow(tag.toUtf8().constData())); QTestResult::setCurrentTestData(data); + QTestPrivate::checkBlackLists((testCaseName() + QStringLiteral("::") + functionName()).toUtf8().constData(), tag.toUtf8().constData()); emit dataTagChanged(); } else { QTestResult::setCurrentTestData(0); @@ -270,6 +281,8 @@ bool QuickTestResult::isSkipped() const void QuickTestResult::setSkipped(bool skip) { QTestResult::setSkipCurrentTest(skip); + if (!skip) + QTestResult::setBlacklistCurrentTest(false); emit skippedChanged(); } @@ -478,8 +491,8 @@ void QuickTestResult::stringify(QQmlV4Function *args) //Check for Object Type if (value->isObject() - && !value->asFunctionObject() - && !value->asArrayObject()) { + && !value->as<QV4::FunctionObject>() + && !value->as<QV4::ArrayObject>()) { QVariant v = scope.engine->toVariant(value, QMetaType::UnknownType); if (v.isValid()) { switch (v.type()) { @@ -500,7 +513,7 @@ void QuickTestResult::stringify(QQmlV4Function *args) if (result.isEmpty()) { QString tmp = value->toQStringNoThrow(); - if (value->asArrayObject()) + if (value->as<QV4::ArrayObject>()) result.append(QString::fromLatin1("[%1]").arg(tmp)); else result.append(tmp); @@ -707,6 +720,8 @@ void QuickTestResult::parseArgs(int argc, char *argv[]) void QuickTestResult::setProgramName(const char *name) { if (name) { + QTestPrivate::parseBlackList(); + QTestPrivate::parseGpuBlackList(); QTestResult::reset(); } else if (!name && loggingStarted) { QTestResult::setCurrentTestObject(globalProgramName); diff --git a/src/qmltest/quicktestresult_p.h b/src/qmltest/quicktestresult_p.h index 80f84bfa55..45df83621f 100644 --- a/src/qmltest/quicktestresult_p.h +++ b/src/qmltest/quicktestresult_p.h @@ -50,7 +50,6 @@ class QuickTestResultPrivate; class Q_QUICK_TEST_EXPORT QuickTestResult : public QObject { Q_OBJECT - Q_ENUMS(RunMode) Q_PROPERTY(QString testCaseName READ testCaseName WRITE setTestCaseName NOTIFY testCaseNameChanged) Q_PROPERTY(QString functionName READ functionName WRITE setFunctionName NOTIFY functionNameChanged) Q_PROPERTY(QString dataTag READ dataTag WRITE setDataTag NOTIFY dataTagChanged) @@ -70,6 +69,7 @@ public: RepeatUntilValidMeasurement, RunOnce }; + Q_ENUM(RunMode) QString testCaseName() const; void setTestCaseName(const QString &name); diff --git a/src/quick/designer/designer.pri b/src/quick/designer/designer.pri index 9f3f7e8be6..eb2141134d 100644 --- a/src/quick/designer/designer.pri +++ b/src/quick/designer/designer.pri @@ -1,4 +1,21 @@ -HEADERS += designer/designersupport.h \ - designer/designerwindowmanager_p.h -SOURCES += designer/designersupport.cpp \ - designer/designerwindowmanager.cpp +HEADERS += \ + designer/qquickdesignercustomobjectdata_p.h \ + designer/qquickdesignersupportitems_p.h \ + designer/qquickdesignerwindowmanager_p.h \ + designer/qquickdesignersupportstates_p.h \ + designer/qquickdesignersupportpropertychanges_p.h \ + designer/qquickdesignersupportproperties_p.h \ + designer/qquickdesignersupportmetainfo_p.h \ + designer/qqmldesignermetaobject_p.h \ + designer/qquickdesignersupport_p.h + +SOURCES += \ + designer/qquickdesignercustomobjectdata.cpp \ + designer/qquickdesignersupport.cpp \ + designer/qquickdesignersupportitems.cpp \ + designer/qquickdesignersupportmetainfo.cpp \ + designer/qquickdesignersupportproperties.cpp \ + designer/qquickdesignersupportpropertychanges.cpp \ + designer/qquickdesignersupportstates.cpp \ + designer/qquickdesignerwindowmanager.cpp \ + designer/qqmldesignermetaobject.cpp diff --git a/src/quick/designer/qqmldesignermetaobject.cpp b/src/quick/designer/qqmldesignermetaobject.cpp new file mode 100644 index 0000000000..4273c0fbc2 --- /dev/null +++ b/src/quick/designer/qqmldesignermetaobject.cpp @@ -0,0 +1,340 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldesignermetaobject_p.h" + +#include <QSharedPointer> +#include <QMetaProperty> +#include <qnumeric.h> +#include <QDebug> + +#include <private/qqmlengine_p.h> +#include <private/qqmlpropertycache_p.h> + +QT_BEGIN_NAMESPACE + +static QHash<QDynamicMetaObjectData *, bool> nodeInstanceMetaObjectList; +static void (*notifyPropertyChangeCallBack)(QObject*, const QQuickDesignerSupport::PropertyName &propertyName) = 0; + +struct MetaPropertyData { + inline QPair<QVariant, bool> &getDataRef(int idx) { + while (m_data.count() <= idx) + m_data << QPair<QVariant, bool>(QVariant(), false); + return m_data[idx]; + } + + inline QVariant &getData(int idx) { + QPair<QVariant, bool> &prop = getDataRef(idx); + if (!prop.second) { + prop.first = QVariant(); + prop.second = true; + } + return prop.first; + } + + inline bool hasData(int idx) const { + if (idx >= m_data.count()) + return false; + return m_data[idx].second; + } + + inline int count() { return m_data.count(); } + + QVector<QPair<QVariant, bool> > m_data; +}; + +static bool constructedMetaData(const QQmlVMEMetaData* data) +{ + return data->varPropertyCount == 0 + && data->propertyCount == 0 + && data->aliasCount == 0 + && data->signalCount == 0 + && data->methodCount == 0; +} + +static QQmlVMEMetaData* fakeMetaData() +{ + QQmlVMEMetaData* data = new QQmlVMEMetaData; + data->varPropertyCount = 0; + data->propertyCount = 0; + data->aliasCount = 0; + data->signalCount = 0; + data->methodCount = 0; + + return data; +} + +static const QQmlVMEMetaData* vMEMetaDataForObject(QObject *object) +{ + QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object); + if (metaObject) + return metaObject->metaData; + + return fakeMetaData(); +} + +static QQmlPropertyCache *cacheForObject(QObject *object, QQmlEngine *engine) +{ + QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object); + if (metaObject) + return metaObject->cache; + + return QQmlEnginePrivate::get(engine)->cache(object); +} + +QQmlDesignerMetaObject* QQmlDesignerMetaObject::getNodeInstanceMetaObject(QObject *object, QQmlEngine *engine) +{ + //Avoid setting up multiple MetaObjects on the same QObject + QObjectPrivate *op = QObjectPrivate::get(object); + QDynamicMetaObjectData *parent = op->metaObject; + if (nodeInstanceMetaObjectList.contains(parent)) + return static_cast<QQmlDesignerMetaObject *>(parent); + + // we just create one and the ownership goes automatically to the object in nodeinstance see init method + return new QQmlDesignerMetaObject(object, engine); +} + +void QQmlDesignerMetaObject::init(QObject *object, QQmlEngine *engine) +{ + //Creating QQmlOpenMetaObjectType + m_type = new QQmlOpenMetaObjectType(metaObjectParent(), engine); + m_type->addref(); + //Assigning type to this + copyTypeMetaObject(); + + //Assign this to object + QObjectPrivate *op = QObjectPrivate::get(object); + op->metaObject = this; + + //create cache + cache = m_cache = QQmlEnginePrivate::get(engine)->cache(this); + cache->addref(); + + //If our parent is not a VMEMetaObject we just se the flag to false again + if (constructedMetaData(metaData)) + QQmlData::get(object)->hasVMEMetaObject = false; + + nodeInstanceMetaObjectList.insert(this, true); + hasAssignedMetaObjectData = true; +} + +QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engine) + : QQmlVMEMetaObject(object, cacheForObject(object, engine), vMEMetaDataForObject(object)), + m_context(engine->contextForObject(object)), + m_data(new MetaPropertyData), + m_cache(0) +{ + init(object, engine); + + QQmlData *ddata = QQmlData::get(object, false); + + //Assign cache to object + if (ddata && ddata->propertyCache) { + cache->setParent(ddata->propertyCache); + cache->invalidate(engine, this); + ddata->propertyCache = m_cache; + } + +} + +QQmlDesignerMetaObject::~QQmlDesignerMetaObject() +{ + if (cache->count() > 1) // qml is crashing because the property cache is not removed from the engine + cache->release(); + else + m_type->release(); + + nodeInstanceMetaObjectList.remove(this); +} + +void QQmlDesignerMetaObject::createNewDynamicProperty(const QString &name) +{ + int id = m_type->createProperty(name.toUtf8()); + copyTypeMetaObject(); + setValue(id, QVariant()); + Q_ASSERT(id >= 0); + Q_UNUSED(id); + + //Updating cache + QQmlPropertyCache *oldParent = m_cache->parent(); + QQmlEnginePrivate::get(m_context->engine())->cache(this)->invalidate(m_context->engine(), this); + m_cache->setParent(oldParent); + + QQmlProperty property(myObject(), name, m_context); + Q_ASSERT(property.isValid()); +} + +void QQmlDesignerMetaObject::setValue(int id, const QVariant &value) +{ + QPair<QVariant, bool> &prop = m_data->getDataRef(id); + prop.first = propertyWriteValue(id, value); + prop.second = true; + QMetaObject::activate(myObject(), id + m_type->signalOffset(), 0); +} + +QVariant QQmlDesignerMetaObject::propertyWriteValue(int, const QVariant &value) +{ + return value; +} + +const QAbstractDynamicMetaObject *QQmlDesignerMetaObject::dynamicMetaObjectParent() const +{ + if (QQmlVMEMetaObject::parent.isT1()) + return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object); + else + return 0; +} + +const QMetaObject *QQmlDesignerMetaObject::metaObjectParent() const +{ + if (QQmlVMEMetaObject::parent.isT1()) + return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object); + + return QQmlVMEMetaObject::parent.asT2(); +} + +int QQmlDesignerMetaObject::propertyOffset() const +{ + return cache->propertyOffset(); +} + +int QQmlDesignerMetaObject::openMetaCall(QMetaObject::Call call, int id, void **a) +{ + if ((call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty) + && id >= m_type->propertyOffset()) { + int propId = id - m_type->propertyOffset(); + if (call == QMetaObject::ReadProperty) { + //propertyRead(propId); + *reinterpret_cast<QVariant *>(a[0]) = m_data->getData(propId); + } else if (call == QMetaObject::WriteProperty) { + if (propId <= m_data->count() || m_data->m_data[propId].first != *reinterpret_cast<QVariant *>(a[0])) { + //propertyWrite(propId); + QPair<QVariant, bool> &prop = m_data->getDataRef(propId); + prop.first = propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0])); + prop.second = true; + //propertyWritten(propId); + activate(myObject(), m_type->signalOffset() + propId, 0); + } + } + return -1; + } else { + QAbstractDynamicMetaObject *directParent = parent(); + if (directParent) + return directParent->metaCall(call, id, a); + else + return myObject()->qt_metacall(call, id, a); + } +} + +int QQmlDesignerMetaObject::metaCall(QMetaObject::Call call, int id, void **a) +{ + int metaCallReturnValue = -1; + + const QMetaProperty propertyById = QQmlVMEMetaObject::property(id); + + if (call == QMetaObject::WriteProperty + && propertyById.userType() == QMetaType::QVariant + && reinterpret_cast<QVariant *>(a[0])->type() == QVariant::Double + && qIsNaN(reinterpret_cast<QVariant *>(a[0])->toDouble())) { + return -1; + } + + if (call == QMetaObject::WriteProperty + && propertyById.userType() == QMetaType::Double + && qIsNaN(*reinterpret_cast<double*>(a[0]))) { + return -1; + } + + if (call == QMetaObject::WriteProperty + && propertyById.userType() == QMetaType::Float + && qIsNaN(*reinterpret_cast<float*>(a[0]))) { + return -1; + } + + QVariant oldValue; + + if (call == QMetaObject::WriteProperty && !propertyById.hasNotifySignal()) + { + oldValue = propertyById.read(myObject()); + } + + QAbstractDynamicMetaObject *directParent = parent(); + if (directParent && id < directParent->propertyOffset()) { + metaCallReturnValue = directParent->metaCall(call, id, a); + } else { + openMetaCall(call, id, a); + } + + + if (call == QMetaObject::WriteProperty + && !propertyById.hasNotifySignal() + && oldValue != propertyById.read(myObject())) + notifyPropertyChange(id); + + return metaCallReturnValue; +} + +void QQmlDesignerMetaObject::notifyPropertyChange(int id) +{ + const QMetaProperty propertyById = property(id); + + if (id < propertyOffset()) { + if (notifyPropertyChangeCallBack) + notifyPropertyChangeCallBack(myObject(), propertyById.name()); + } else { + if (notifyPropertyChangeCallBack) + notifyPropertyChangeCallBack(myObject(), name(id - propertyOffset())); + } +} + +int QQmlDesignerMetaObject::count() const +{ + return m_type->propertyCount(); +} + +QByteArray QQmlDesignerMetaObject::name(int idx) const +{ + return m_type->propertyName(idx); +} + +void QQmlDesignerMetaObject::copyTypeMetaObject() +{ + *static_cast<QMetaObject *>(this) = *m_type->metaObject(); +} + +void QQmlDesignerMetaObject::registerNotifyPropertyChangeCallBack(void (*callback)(QObject *, const QQuickDesignerSupport::PropertyName &)) +{ + notifyPropertyChangeCallBack = callback; +} + +QT_END_NAMESPACE diff --git a/src/quick/designer/qqmldesignermetaobject_p.h b/src/quick/designer/qqmldesignermetaobject_p.h new file mode 100644 index 0000000000..eca4fb6a99 --- /dev/null +++ b/src/quick/designer/qqmldesignermetaobject_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NODEINSTANCEMETAOBJECT_H +#define NODEINSTANCEMETAOBJECT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickdesignersupport_p.h" + +#include <QQmlContext> +#include <QScopedPointer> +#include <private/qqmlopenmetaobject_p.h> +#include <private/qqmlvmemetaobject_p.h> + +QT_BEGIN_NAMESPACE + +struct MetaPropertyData; + +class QQmlDesignerMetaObject : public QQmlVMEMetaObject +{ +public: + ~QQmlDesignerMetaObject(); + + static void registerNotifyPropertyChangeCallBack(void (*callback)(QObject*, const QQuickDesignerSupport::PropertyName &propertyName)); + +protected: + QQmlDesignerMetaObject(QObject *object, QQmlEngine *engine); + static QQmlDesignerMetaObject* getNodeInstanceMetaObject(QObject *object, QQmlEngine *engine); + + void createNewDynamicProperty(const QString &name); + int openMetaCall(QMetaObject::Call _c, int _id, void **_a); + int metaCall(QMetaObject::Call _c, int _id, void **_a); + void notifyPropertyChange(int id); + void setValue(int id, const QVariant &value); + QVariant propertyWriteValue(int, const QVariant &); + + QObject *myObject() const { return QQmlVMEMetaObject::object; } + QAbstractDynamicMetaObject *parent() const { return const_cast<QAbstractDynamicMetaObject *>(dynamicMetaObjectParent()); } + + const QAbstractDynamicMetaObject *dynamicMetaObjectParent() const; + + const QMetaObject *metaObjectParent() const; + + int propertyOffset() const; + + int count() const; + QByteArray name(int) const; + + void copyTypeMetaObject(); + +private: + void init(QObject *, QQmlEngine *engine); + + QPointer<QQmlContext> m_context; + QQmlOpenMetaObjectType *m_type; + QScopedPointer<MetaPropertyData> m_data; + //QAbstractDynamicMetaObject *m_parent; + QQmlPropertyCache *m_cache; + + friend class QQuickDesignerSupportProperties; +}; + +QT_END_NAMESPACE + +#endif // NODEINSTANCEMETAOBJECT_H diff --git a/src/quick/designer/qquickdesignercustomobjectdata.cpp b/src/quick/designer/qquickdesignercustomobjectdata.cpp new file mode 100644 index 0000000000..1666ffb0a5 --- /dev/null +++ b/src/quick/designer/qquickdesignercustomobjectdata.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdesignersupportmetainfo_p.h" +#include "qquickdesignersupportproperties_p.h" + +#include "qquickdesignercustomobjectdata_p.h" + +#include <QGlobalStatic> +#include <QQmlContext> +#include <QQmlEngine> + +#include <private/qqmlbinding_p.h> + +QT_BEGIN_NAMESPACE + +typedef QHash<QObject*, QQuickDesignerCustomObjectData*> CustomObjectDataHash; +Q_GLOBAL_STATIC(CustomObjectDataHash, s_designerObjectToDataHash) + +struct HandleDestroyedFunctor { + QQuickDesignerCustomObjectData *data; + void operator()() { data->handleDestroyed(); } +}; + +QQuickDesignerCustomObjectData::QQuickDesignerCustomObjectData(QObject *object) + : m_object(object) +{ + if (object) { + populateResetHashes(); + s_designerObjectToDataHash()->insert(object, this); + + HandleDestroyedFunctor functor; + functor.data = this; + QObject::connect(object, &QObject::destroyed, functor); + } +} + +void QQuickDesignerCustomObjectData::registerData(QObject *object) +{ + new QQuickDesignerCustomObjectData(object); +} + +QQuickDesignerCustomObjectData *QQuickDesignerCustomObjectData::get(QObject *object) +{ + return s_designerObjectToDataHash()->value(object); +} + +QVariant QQuickDesignerCustomObjectData::getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName) +{ + QQuickDesignerCustomObjectData* data = get(object); + + if (data) + return data->getResetValue(propertyName); + + return QVariant(); +} + +void QQuickDesignerCustomObjectData::doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName) +{ + QQuickDesignerCustomObjectData* data = get(object); + + if (data) + data->doResetProperty(context, propertyName); +} + +bool QQuickDesignerCustomObjectData::hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName) +{ + QQuickDesignerCustomObjectData* data = get(object); + + if (data) + return data->hasValidResetBinding(propertyName); + + return false; +} + +bool QQuickDesignerCustomObjectData::hasBindingForProperty(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + bool *hasChanged) +{ + QQuickDesignerCustomObjectData* data = get(object); + + if (data) + return data->hasBindingForProperty(context, propertyName, hasChanged); + + return false; +} + +void QQuickDesignerCustomObjectData::setPropertyBinding(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression) +{ + QQuickDesignerCustomObjectData* data = get(object); + + if (data) + data->setPropertyBinding(context, propertyName, expression); +} + +void QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName) +{ + QQuickDesignerCustomObjectData* data = get(object); + + if (data) + data->keepBindingFromGettingDeleted(context, propertyName); +} + +void QQuickDesignerCustomObjectData::populateResetHashes() +{ + QQuickDesignerSupport::PropertyNameList propertyNameList = + QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object()); + + Q_FOREACH (const QQuickDesignerSupport::PropertyName &propertyName, propertyNameList) { + QQmlProperty property(object(), QString::fromUtf8(propertyName), QQmlEngine::contextForObject(object())); + + QQmlAbstractBinding::Ptr binding = QQmlAbstractBinding::Ptr(QQmlPropertyPrivate::binding(property)); + + if (binding) { + m_resetBindingHash.insert(propertyName, binding); + } else if (property.isWritable()) { + m_resetValueHash.insert(propertyName, property.read()); + } + } +} + +QObject *QQuickDesignerCustomObjectData::object() const +{ + return m_object; +} + +QVariant QQuickDesignerCustomObjectData::getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const +{ + return m_resetValueHash.value(propertyName); +} + +void QQuickDesignerCustomObjectData::doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName) +{ + QQmlProperty property(object(), QString::fromUtf8(propertyName), context); + + if (!property.isValid()) + return; + + QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(property); + if (binding && !(hasValidResetBinding(propertyName) && getResetBinding(propertyName) == binding)) { + binding->setEnabled(false, 0); + } + + + if (hasValidResetBinding(propertyName)) { + QQmlAbstractBinding *binding = getResetBinding(propertyName); + + QQmlBinding *qmlBinding = dynamic_cast<QQmlBinding*>(binding); + if (qmlBinding) + qmlBinding->setTarget(property); + QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding); + if (qmlBinding) + qmlBinding->update(); + + } else if (property.isResettable()) { + property.reset(); + } else if (property.propertyTypeCategory() == QQmlProperty::List) { + QQmlListReference list = qvariant_cast<QQmlListReference>(property.read()); + + if (!QQuickDesignerSupportProperties::hasFullImplementedListInterface(list)) { + qWarning() << "Property list interface not fully implemented for Class " << property.property().typeName() << " in property " << property.name() << "!"; + return; + } + + list.clear(); + } else if (property.isWritable()) { + if (property.read() == getResetValue(propertyName)) + return; + + property.write(getResetValue(propertyName)); + } +} + +bool QQuickDesignerCustomObjectData::hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const +{ + return m_resetBindingHash.contains(propertyName) && m_resetBindingHash.value(propertyName).data(); +} + +QQmlAbstractBinding *QQuickDesignerCustomObjectData::getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const +{ + return m_resetBindingHash.value(propertyName).data(); +} + +bool QQuickDesignerCustomObjectData::hasBindingForProperty(QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + bool *hasChanged) const +{ + if (QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName)) + return false; + + QQmlProperty property(object(), QString::fromUtf8(propertyName), context); + + bool hasBinding = QQmlPropertyPrivate::binding(property); + + if (hasChanged) { + *hasChanged = hasBinding != m_hasBindingHash.value(propertyName, false); + if (*hasChanged) + m_hasBindingHash.insert(propertyName, hasBinding); + } + + return QQmlPropertyPrivate::binding(property); +} + +void QQuickDesignerCustomObjectData::setPropertyBinding(QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression) +{ + QQmlProperty property(object(), QString::fromUtf8(propertyName), context); + + if (!property.isValid()) + return; + + if (property.isProperty()) { + QQmlBinding *binding = new QQmlBinding(expression, object(), context); + binding->setTarget(property); + binding->setNotifyOnValueChanged(true); + + QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding); + //Refcounting is taking take care of deletion + binding->update(); + if (binding->hasError()) { + if (property.property().userType() == QVariant::String) + property.write(QVariant(QLatin1Char('#') + expression + QLatin1Char('#'))); + } + + } else { + qWarning() << Q_FUNC_INFO << ": Cannot set binding for property" << propertyName << ": property is unknown for type"; + } +} + +void QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName) +{ + //Refcounting is taking care + Q_UNUSED(context) + Q_UNUSED(propertyName) +} + +void QQuickDesignerCustomObjectData::handleDestroyed() +{ + s_designerObjectToDataHash()->remove(m_object); + delete this; +} + +QT_END_NAMESPACE + diff --git a/src/quick/designer/qquickdesignercustomobjectdata_p.h b/src/quick/designer/qquickdesignercustomobjectdata_p.h new file mode 100644 index 0000000000..6734540a54 --- /dev/null +++ b/src/quick/designer/qquickdesignercustomobjectdata_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DESIGNERCUSTOMOBJECTDATA_H +#define DESIGNERCUSTOMOBJECTDATA_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickdesignersupport_p.h" + +#include <QHash> +#include <QObject> +#include <QVariant> + +#include <private/qqmlbinding_p.h> + +QT_BEGIN_NAMESPACE +class QQmlContext; + +class QQuickDesignerCustomObjectData +{ +public: + static void registerData(QObject *object); + static QQuickDesignerCustomObjectData *get(QObject *object); + static QVariant getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName); + static void doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName); + static bool hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName); + static bool hasBindingForProperty(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + bool *hasChanged); + static void setPropertyBinding(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression); + static void keepBindingFromGettingDeleted(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName); + void handleDestroyed(); + +private: + QQuickDesignerCustomObjectData(QObject *object); + void populateResetHashes(); + QObject *object() const; + QVariant getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const; + void doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName); + bool hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const; + QQmlAbstractBinding *getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const; + bool hasBindingForProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName, bool *hasChanged) const; + void setPropertyBinding(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName, const QString &expression); + void keepBindingFromGettingDeleted(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName); + + QObject *m_object; + QHash<QQuickDesignerSupport::PropertyName, QVariant> m_resetValueHash; + QHash<QQuickDesignerSupport::PropertyName, QQmlAbstractBinding::Ptr> m_resetBindingHash; + mutable QHash<QQuickDesignerSupport::PropertyName, bool> m_hasBindingHash; +}; + +QT_END_NAMESPACE + +#endif // DESIGNERCUSTOMOBJECTDATA_H diff --git a/src/quick/designer/designersupport.cpp b/src/quick/designer/qquickdesignersupport.cpp index 56d2badb62..bc6352a677 100644 --- a/src/quick/designer/designersupport.cpp +++ b/src/quick/designer/qquickdesignersupport.cpp @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include "designersupport.h" +#include "qquickdesignersupport_p.h" #include <private/qquickitem_p.h> #include <QtQuick/private/qquickshadereffectsource_p.h> @@ -46,16 +46,16 @@ #include <private/qqmlcomponentattached_p.h> #include <private/qqmldata_p.h> -#include "designerwindowmanager_p.h" +#include "qquickdesignerwindowmanager_p.h" QT_BEGIN_NAMESPACE -DesignerSupport::DesignerSupport() +QQuickDesignerSupport::QQuickDesignerSupport() { } -DesignerSupport::~DesignerSupport() +QQuickDesignerSupport::~QQuickDesignerSupport() { typedef QHash<QQuickItem*, QSGLayer*>::iterator ItemTextureHashIt; @@ -67,7 +67,7 @@ DesignerSupport::~DesignerSupport() } } -void DesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool hide) +void QQuickDesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool hide) { if (referencedItem == 0) return; @@ -100,7 +100,7 @@ void DesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool hide) } } -void DesignerSupport::derefFromEffectItem(QQuickItem *referencedItem, bool unhide) +void QQuickDesignerSupport::derefFromEffectItem(QQuickItem *referencedItem, bool unhide) { if (referencedItem == 0) return; @@ -109,7 +109,7 @@ void DesignerSupport::derefFromEffectItem(QQuickItem *referencedItem, bool unhid QQuickItemPrivate::get(referencedItem)->derefFromEffectItem(unhide); } -QImage DesignerSupport::renderImageForItem(QQuickItem *referencedItem, const QRectF &boundingRect, const QSize &imageSize) +QImage QQuickDesignerSupport::renderImageForItem(QQuickItem *referencedItem, const QRectF &boundingRect, const QSize &imageSize) { if (referencedItem == 0 || referencedItem->parentItem() == 0) { qDebug() << __FILE__ << __LINE__ << "Warning: Item can be rendered."; @@ -136,7 +136,7 @@ QImage DesignerSupport::renderImageForItem(QQuickItem *referencedItem, const QRe return renderImage; } -bool DesignerSupport::isDirty(QQuickItem *referencedItem, DirtyType dirtyType) +bool QQuickDesignerSupport::isDirty(QQuickItem *referencedItem, DirtyType dirtyType) { if (referencedItem == 0) return false; @@ -144,7 +144,7 @@ bool DesignerSupport::isDirty(QQuickItem *referencedItem, DirtyType dirtyType) return QQuickItemPrivate::get(referencedItem)->dirtyAttributes & dirtyType; } -void DesignerSupport::addDirty(QQuickItem *referencedItem, DesignerSupport::DirtyType dirtyType) +void QQuickDesignerSupport::addDirty(QQuickItem *referencedItem, QQuickDesignerSupport::DirtyType dirtyType) { if (referencedItem == 0) return; @@ -152,7 +152,7 @@ void DesignerSupport::addDirty(QQuickItem *referencedItem, DesignerSupport::Dirt QQuickItemPrivate::get(referencedItem)->dirtyAttributes |= dirtyType; } -void DesignerSupport::resetDirty(QQuickItem *referencedItem) +void QQuickDesignerSupport::resetDirty(QQuickItem *referencedItem) { if (referencedItem == 0) return; @@ -161,7 +161,7 @@ void DesignerSupport::resetDirty(QQuickItem *referencedItem) QQuickItemPrivate::get(referencedItem)->removeFromDirtyList(); } -QTransform DesignerSupport::windowTransform(QQuickItem *referencedItem) +QTransform QQuickDesignerSupport::windowTransform(QQuickItem *referencedItem) { if (referencedItem == 0) return QTransform(); @@ -169,7 +169,7 @@ QTransform DesignerSupport::windowTransform(QQuickItem *referencedItem) return QQuickItemPrivate::get(referencedItem)->itemToWindowTransform(); } -QTransform DesignerSupport::parentTransform(QQuickItem *referencedItem) +QTransform QQuickDesignerSupport::parentTransform(QQuickItem *referencedItem) { if (referencedItem == 0) return QTransform(); @@ -211,12 +211,9 @@ bool isValidAnchorName(const QString &name) return anchorNameList.contains(name); } -bool DesignerSupport::isAnchoredTo(QQuickItem *fromItem, QQuickItem *toItem) +bool QQuickDesignerSupport::isAnchoredTo(QQuickItem *fromItem, QQuickItem *toItem) { -#ifndef QT_NO_DYNAMIC_CAST - Q_ASSERT(dynamic_cast<QQuickItemPrivate*>(QQuickItemPrivate::get(fromItem))); -#endif - QQuickItemPrivate *fromItemPrivate = static_cast<QQuickItemPrivate*>(QQuickItemPrivate::get(fromItem)); + QQuickItemPrivate *fromItemPrivate = QQuickItemPrivate::get(fromItem); QQuickAnchors *anchors = fromItemPrivate->anchors(); return anchors->fill() == toItem || anchors->centerIn() == toItem @@ -229,9 +226,9 @@ bool DesignerSupport::isAnchoredTo(QQuickItem *fromItem, QQuickItem *toItem) || anchors->baseline().item == toItem; } -bool DesignerSupport::areChildrenAnchoredTo(QQuickItem *fromItem, QQuickItem *toItem) +bool QQuickDesignerSupport::areChildrenAnchoredTo(QQuickItem *fromItem, QQuickItem *toItem) { - foreach (QQuickItem *childItem, fromItem->childItems()) { + Q_FOREACH (QQuickItem *childItem, fromItem->childItems()) { if (childItem) { if (isAnchoredTo(childItem, toItem)) return true; @@ -246,7 +243,7 @@ bool DesignerSupport::areChildrenAnchoredTo(QQuickItem *fromItem, QQuickItem *to QQuickAnchors *anchors(QQuickItem *item) { - QQuickItemPrivate *itemPrivate = static_cast<QQuickItemPrivate*>(QQuickItemPrivate::get(item)); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); return itemPrivate->anchors(); } @@ -278,7 +275,7 @@ QQuickAnchors::Anchor anchorLineFlagForName(const QString &name) return QQuickAnchors::LeftAnchor; } -bool DesignerSupport::hasAnchor(QQuickItem *item, const QString &name) +bool QQuickDesignerSupport::hasAnchor(QQuickItem *item, const QString &name) { if (!isValidAnchorName(name)) return false; @@ -313,19 +310,19 @@ bool DesignerSupport::hasAnchor(QQuickItem *item, const QString &name) return anchors(item)->usedAnchors().testFlag(anchorLineFlagForName(name)); } -QQuickItem *DesignerSupport::anchorFillTargetItem(QQuickItem *item) +QQuickItem *QQuickDesignerSupport::anchorFillTargetItem(QQuickItem *item) { return anchors(item)->fill(); } -QQuickItem *DesignerSupport::anchorCenterInTargetItem(QQuickItem *item) +QQuickItem *QQuickDesignerSupport::anchorCenterInTargetItem(QQuickItem *item) { return anchors(item)->centerIn(); } -QPair<QString, QObject*> DesignerSupport::anchorLineTarget(QQuickItem *item, const QString &name, QQmlContext *context) +QPair<QString, QObject*> QQuickDesignerSupport::anchorLineTarget(QQuickItem *item, const QString &name, QQmlContext *context) { QObject *targetObject = 0; QString targetName; @@ -350,7 +347,7 @@ QPair<QString, QObject*> DesignerSupport::anchorLineTarget(QQuickItem *item, con return QPair<QString, QObject*>(targetName, targetObject); } -void DesignerSupport::resetAnchor(QQuickItem *item, const QString &name) +void QQuickDesignerSupport::resetAnchor(QQuickItem *item, const QString &name) { if (name == QLatin1String("anchors.fill")) { anchors(item)->resetFill(); @@ -373,7 +370,7 @@ void DesignerSupport::resetAnchor(QQuickItem *item, const QString &name) } } -void DesignerSupport::emitComponentCompleteSignalForAttachedProperty(QQuickItem *item) +void QQuickDesignerSupport::emitComponentCompleteSignalForAttachedProperty(QQuickItem *item) { QQmlData *data = QQmlData::get(item); if (data && data->context) { @@ -384,24 +381,24 @@ void DesignerSupport::emitComponentCompleteSignalForAttachedProperty(QQuickItem } } -QList<QObject*> DesignerSupport::statesForItem(QQuickItem *item) +QList<QObject*> QQuickDesignerSupport::statesForItem(QQuickItem *item) { QList<QObject*> objectList; QList<QQuickState *> stateList = QQuickItemPrivate::get(item)->_states()->states(); objectList.reserve(stateList.size()); - foreach (QQuickState* state, stateList) + Q_FOREACH (QQuickState* state, stateList) objectList.append(state); return objectList; } -bool DesignerSupport::isComponentComplete(QQuickItem *item) +bool QQuickDesignerSupport::isComponentComplete(QQuickItem *item) { - return static_cast<QQuickItemPrivate*>(QQuickItemPrivate::get(item))->componentComplete; + return QQuickItemPrivate::get(item)->componentComplete; } -int DesignerSupport::borderWidth(QQuickItem *item) +int QQuickDesignerSupport::borderWidth(QQuickItem *item) { QQuickRectangle *rectangle = qobject_cast<QQuickRectangle*>(item); if (rectangle) @@ -410,60 +407,71 @@ int DesignerSupport::borderWidth(QQuickItem *item) return 0; } -void DesignerSupport::refreshExpressions(QQmlContext *context) +void QQuickDesignerSupport::refreshExpressions(QQmlContext *context) { QQmlContextPrivate::get(context)->data->refreshExpressions(); } -void DesignerSupport::setRootItem(QQuickView *view, QQuickItem *item) +void QQuickDesignerSupport::setRootItem(QQuickView *view, QQuickItem *item) { QQuickViewPrivate::get(view)->setRootObject(item); } -bool DesignerSupport::isValidWidth(QQuickItem *item) +bool QQuickDesignerSupport::isValidWidth(QQuickItem *item) { return QQuickItemPrivate::get(item)->heightValid; } -bool DesignerSupport::isValidHeight(QQuickItem *item) +bool QQuickDesignerSupport::isValidHeight(QQuickItem *item) { return QQuickItemPrivate::get(item)->widthValid; } -void DesignerSupport::updateDirtyNode(QQuickItem *item) +void QQuickDesignerSupport::updateDirtyNode(QQuickItem *item) { if (item->window()) QQuickWindowPrivate::get(item->window())->updateDirtyNode(item); } -void DesignerSupport::activateDesignerWindowManager() +void QQuickDesignerSupport::activateDesignerWindowManager() { - QSGRenderLoop::setInstance(new DesignerWindowManager); + QSGRenderLoop::setInstance(new QQuickDesignerWindowManager); } -void DesignerSupport::activateDesignerMode() +void QQuickDesignerSupport::activateDesignerMode() { QQmlEnginePrivate::activateDesignerMode(); } -void DesignerSupport::disableComponentComplete() +void QQuickDesignerSupport::disableComponentComplete() { QQmlVME::disableComponentComplete(); } -void DesignerSupport::enableComponentComplete() +void QQuickDesignerSupport::enableComponentComplete() { QQmlVME::enableComponentComplete(); } -void DesignerSupport::createOpenGLContext(QQuickWindow *window) +void QQuickDesignerSupport::createOpenGLContext(QQuickWindow *window) { - DesignerWindowManager::createOpenGLContext(window); + QQuickDesignerWindowManager::createOpenGLContext(window); } -void DesignerSupport::polishItems(QQuickWindow *window) +void QQuickDesignerSupport::polishItems(QQuickWindow *window) { QQuickWindowPrivate::get(window)->polishItems(); } +ComponentCompleteDisabler::ComponentCompleteDisabler() +{ + QQuickDesignerSupport::disableComponentComplete(); +} + +ComponentCompleteDisabler::~ComponentCompleteDisabler() +{ + QQuickDesignerSupport::enableComponentComplete(); +} + + QT_END_NAMESPACE diff --git a/src/quick/designer/designersupport.h b/src/quick/designer/qquickdesignersupport_p.h index 51fcef4512..e15c109f7b 100644 --- a/src/quick/designer/designersupport.h +++ b/src/quick/designer/qquickdesignersupport_p.h @@ -45,7 +45,6 @@ // We mean it. // - #include <QtQuick/qtquickglobal.h> #include <QtCore/QtGlobal> #include <QtCore/QHash> @@ -62,9 +61,13 @@ class QQuickView; class QObject; class QQuickWindow; -class Q_QUICK_EXPORT DesignerSupport +class Q_QUICK_EXPORT QQuickDesignerSupport { public: + typedef QByteArray PropertyName; + typedef QList<PropertyName> PropertyNameList; + typedef QByteArray TypeName; + enum DirtyType { TransformOrigin = 0x00000001, Transform = 0x00000002, @@ -95,8 +98,8 @@ public: }; - DesignerSupport(); - ~DesignerSupport(); + QQuickDesignerSupport(); + ~QQuickDesignerSupport(); void refFromEffectItem(QQuickItem *referencedItem, bool hide = true); void derefFromEffectItem(QQuickItem *referencedItem, bool unhide = true); @@ -119,7 +122,6 @@ public: static void resetAnchor(QQuickItem *item, const QString &name); static void emitComponentCompleteSignalForAttachedProperty(QQuickItem *item); - static QList<QObject*> statesForItem(QQuickItem *item); static bool isComponentComplete(QQuickItem *item); @@ -149,6 +151,16 @@ private: QHash<QQuickItem*, QSGLayer*> m_itemTextureHash; }; +class Q_QUICK_EXPORT ComponentCompleteDisabler +{ +public: + ComponentCompleteDisabler(); + + ~ComponentCompleteDisabler(); +}; + +typedef QQuickDesignerSupport DesignerSupport; + QT_END_NAMESPACE #endif // DESIGNERSUPPORT_H diff --git a/src/quick/designer/qquickdesignersupportitems.cpp b/src/quick/designer/qquickdesignersupportitems.cpp new file mode 100644 index 0000000000..4f23fe6630 --- /dev/null +++ b/src/quick/designer/qquickdesignersupportitems.cpp @@ -0,0 +1,320 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdesignersupportitems_p.h" +#include "qquickdesignersupportproperties_p.h" + +#include <private/qabstractanimation_p.h> +#include <private/qobject_p.h> +#include <private/qquickbehavior_p.h> +#include <private/qquicktext_p.h> +#include <private/qquicktextinput_p.h> +#include <private/qquicktextedit_p.h> +#include <private/qquicktransition_p.h> + +#include <private/qquickanimation_p.h> +#include <private/qqmlmetatype_p.h> +#include <private/qqmltimer_p.h> + +QT_BEGIN_NAMESPACE + +static void (*fixResourcePathsForObjectCallBack)(QObject*) = 0; + +static void stopAnimation(QObject *object) +{ + if (object == 0) + return; + + QQuickTransition *transition = qobject_cast<QQuickTransition*>(object); + QQuickAbstractAnimation *animation = qobject_cast<QQuickAbstractAnimation*>(object); + QQmlTimer *timer = qobject_cast<QQmlTimer*>(object); + if (transition) { + transition->setFromState(QString()); + transition->setToState(QString()); + } else if (animation) { +// QQuickScriptAction *scriptAimation = qobject_cast<QQuickScriptAction*>(animation); +// if (scriptAimation) FIXME +// scriptAimation->setScript(QQmlScriptString()); + animation->setLoops(1); + animation->complete(); + animation->setDisableUserControl(); + } else if (timer) { + timer->blockSignals(true); + } +} + +static void allSubObjects(QObject *object, QObjectList &objectList) +{ + // don't add null pointer and stop if the object is already in the list + if (!object || objectList.contains(object)) + return; + + objectList.append(object); + + for (int index = QObject::staticMetaObject.propertyOffset(); + index < object->metaObject()->propertyCount(); + index++) { + QMetaProperty metaProperty = object->metaObject()->property(index); + + // search recursive in property objects + if (metaProperty.isReadable() + && metaProperty.isWritable() + && QQmlMetaType::isQObject(metaProperty.userType())) { + if (qstrcmp(metaProperty.name(), "parent")) { + QObject *propertyObject = QQmlMetaType::toQObject(metaProperty.read(object)); + allSubObjects(propertyObject, objectList); + } + + } + + // search recursive in property object lists + if (metaProperty.isReadable() + && QQmlMetaType::isList(metaProperty.userType())) { + QQmlListReference list(object, metaProperty.name()); + if (list.canCount() && list.canAt()) { + for (int i = 0; i < list.count(); i++) { + QObject *propertyObject = list.at(i); + allSubObjects(propertyObject, objectList); + + } + } + } + } + + // search recursive in object children list + Q_FOREACH (QObject *childObject, object->children()) { + allSubObjects(childObject, objectList); + } + + // search recursive in quick item childItems list + QQuickItem *quickItem = qobject_cast<QQuickItem*>(object); + if (quickItem) { + Q_FOREACH (QQuickItem *childItem, quickItem->childItems()) { + allSubObjects(childItem, objectList); + } + } +} + +void QQuickDesignerSupportItems::tweakObjects(QObject *object) +{ + QObjectList objectList; + allSubObjects(object, objectList); + Q_FOREACH (QObject* childObject, objectList) { + stopAnimation(childObject); + if (fixResourcePathsForObjectCallBack) + fixResourcePathsForObjectCallBack(childObject); + } +} + +static QObject *createDummyWindow(QQmlEngine *engine) +{ + QQmlComponent component(engine, QUrl(QStringLiteral("qrc:/qtquickplugin/mockfiles/Window.qml"))); + return component.create(); +} + +static bool isWindowMetaObject(const QMetaObject *metaObject) +{ + if (metaObject) { + if (metaObject->className() == QByteArrayLiteral("QWindow")) + return true; + + return isWindowMetaObject(metaObject->superClass()); + } + + return false; +} + +static bool isWindow(QObject *object) { + if (object) + return isWindowMetaObject(object->metaObject()); + + return false; +} + +static QQmlType *getQmlType(const QString &typeName, int majorNumber, int minorNumber) +{ + return QQmlMetaType::qmlType(typeName, majorNumber, minorNumber); +} + +static bool isCrashingType(QQmlType *type) +{ + if (type) { + if (type->qmlTypeName() == QStringLiteral("QtMultimedia/MediaPlayer")) + return true; + + if (type->qmlTypeName() == QStringLiteral("QtMultimedia/Audio")) + return true; + + if (type->qmlTypeName() == QStringLiteral("QtQuick.Controls/MenuItem")) + return true; + + if (type->qmlTypeName() == QStringLiteral("QtQuick.Controls/Menu")) + return true; + + if (type->qmlTypeName() == QStringLiteral("QtQuick/Timer")) + return true; + } + + return false; +} + +QObject *QQuickDesignerSupportItems::createPrimitive(const QString &typeName, int majorNumber, int minorNumber, QQmlContext *context) +{ + ComponentCompleteDisabler disableComponentComplete; + + Q_UNUSED(disableComponentComplete) + + QObject *object = 0; + QQmlType *type = getQmlType(typeName, majorNumber, minorNumber); + + if (isCrashingType(type)) { + object = new QObject; + } else if (type) { + if ( type->isComposite()) { + object = createComponent(type->sourceUrl(), context); + } else + { + if (type->typeName() == "QQmlComponent") { + object = new QQmlComponent(context->engine(), 0); + } else { + object = type->create(); + } + } + + if (isWindow(object)) { + delete object; + object = createDummyWindow(context->engine()); + } + + } + + if (!object) { + qWarning() << "QuickDesigner: Cannot create an object of type" + << QString::fromLatin1("%1 %2,%3").arg(typeName).arg(majorNumber).arg(minorNumber) + << "- type isn't known to declarative meta type system"; + } + + tweakObjects(object); + + if (object && QQmlEngine::contextForObject(object) == 0) + QQmlEngine::setContextForObject(object, context); + + QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); + + return object; +} + +QObject *QQuickDesignerSupportItems::createComponent(const QUrl &componentUrl, QQmlContext *context) +{ + ComponentCompleteDisabler disableComponentComplete; + Q_UNUSED(disableComponentComplete) + + QQmlComponent component(context->engine(), componentUrl); + + QObject *object = component.beginCreate(context); + tweakObjects(object); + component.completeCreate(); + QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); + + if (component.isError()) { + qWarning() << "Error in:" << Q_FUNC_INFO << componentUrl; + Q_FOREACH (const QQmlError &error, component.errors()) + qWarning() << error; + } + return object; +} + +bool QQuickDesignerSupportItems::objectWasDeleted(QObject *object) +{ + return QObjectPrivate::get(object)->wasDeleted; +} + +void QQuickDesignerSupportItems::disableNativeTextRendering(QQuickItem *item) +{ + QQuickText *text = qobject_cast<QQuickText*>(item); + if (text) + text->setRenderType(QQuickText::QtRendering); + + QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(item); + if (textInput) + textInput->setRenderType(QQuickTextInput::QtRendering); + + QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(item); + if (textEdit) + textEdit->setRenderType(QQuickTextEdit::QtRendering); +} + +void QQuickDesignerSupportItems::disableTextCursor(QQuickItem *item) +{ + Q_FOREACH (QQuickItem *childItem, item->childItems()) + disableTextCursor(childItem); + + QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(item); + if (textInput) + textInput->setCursorVisible(false); + + QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(item); + if (textEdit) + textEdit->setCursorVisible(false); +} + +void QQuickDesignerSupportItems::disableTransition(QObject *object) +{ + QQuickTransition *transition = qobject_cast<QQuickTransition*>(object); + Q_ASSERT(transition); + const QString invalidState = QLatin1String("invalidState"); + transition->setToState(invalidState); + transition->setFromState(invalidState); +} + +void QQuickDesignerSupportItems::disableBehaivour(QObject *object) +{ + QQuickBehavior* behavior = qobject_cast<QQuickBehavior*>(object); + Q_ASSERT(behavior); + behavior->setEnabled(false); +} + +void QQuickDesignerSupportItems::stopUnifiedTimer() +{ + QUnifiedTimer::instance()->setSlowdownFactor(0.00001); + QUnifiedTimer::instance()->setSlowModeEnabled(true); +} + +void QQuickDesignerSupportItems::registerFixResourcePathsForObjectCallBack(void (*callback)(QObject *)) +{ + fixResourcePathsForObjectCallBack = callback; +} + +QT_END_NAMESPACE + + diff --git a/src/qml/debugger/qqmlconfigurabledebugservice_p.h b/src/quick/designer/qquickdesignersupportitems_p.h index cf69c3a1f6..c15d651b33 100644 --- a/src/qml/debugger/qqmlconfigurabledebugservice_p.h +++ b/src/quick/designer/qquickdesignersupportitems_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage @@ -31,9 +31,8 @@ ** ****************************************************************************/ - -#ifndef QQMLCONFIGURABLEDEBUGSEVICE_H -#define QQMLCONFIGURABLEDEBUGSEVICE_H +#ifndef DESIGNERSUPPORTITEM_H +#define DESIGNERSUPPORTITEM_H // // W A R N I N G @@ -46,34 +45,33 @@ // We mean it. // -#include "qqmldebugservice_p.h" +#include "qquickdesignersupport_p.h" + +#include <QObject> +#include <QString> +#include <QVariant> +#include <QList> +#include <QByteArray> +#include <QQmlContext> +#include <QQmlListReference> QT_BEGIN_NAMESPACE -class QMutex; -class QQmlConfigurableDebugServicePrivate; -class QQmlConfigurableDebugService : public QQmlDebugService +class Q_QUICK_EXPORT QQuickDesignerSupportItems { - Q_OBJECT public: - QQmlConfigurableDebugService(const QString &name, float version, QObject *parent = 0); - -protected: - QQmlConfigurableDebugService(QQmlDebugServicePrivate &dd, const QString &name, float version, QObject *parent = 0); - - QMutex *configMutex(); - void stopWaiting(); - void init(); - - void stateChanged(State); - void engineAboutToBeAdded(QQmlEngine *); - - virtual ~QQmlConfigurableDebugService() {} -private: - Q_DISABLE_COPY(QQmlConfigurableDebugService) - Q_DECLARE_PRIVATE(QQmlConfigurableDebugService) + static QObject *createPrimitive(const QString &typeName, int majorNumber, int minorNumber, QQmlContext *context); + static QObject *createComponent(const QUrl &componentUrl, QQmlContext *context); + static void tweakObjects(QObject *object); + static bool objectWasDeleted(QObject *object); + static void disableNativeTextRendering(QQuickItem *item); + static void disableTextCursor(QQuickItem *item); + static void disableTransition(QObject *object); + static void disableBehaivour(QObject *object); + static void stopUnifiedTimer(); + static void registerFixResourcePathsForObjectCallBack(void (*callback)(QObject *)); }; QT_END_NAMESPACE -#endif // QQMLCONFIGURABLEDEBUGSEVICE_H +#endif // DESIGNERSUPPORTITEM_H diff --git a/src/qml/qml/ftw/qqmlpool.cpp b/src/quick/designer/qquickdesignersupportmetainfo.cpp index b86dcba107..c6c7ed38b9 100644 --- a/src/qml/qml/ftw/qqmlpool.cpp +++ b/src/quick/designer/qquickdesignersupportmetainfo.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage @@ -31,59 +31,38 @@ ** ****************************************************************************/ -#include "qqmlpool_p.h" -#include <stdlib.h> +#include "qquickdesignersupportmetainfo_p.h" +#include "qqmldesignermetaobject_p.h" -#ifdef Q_OS_QNX -#include <malloc.h> -#endif - -// #define POOL_DEBUG +#include <private/qqmlmetatype_p.h> QT_BEGIN_NAMESPACE -void QQmlPool::newpage() +bool QQuickDesignerSupportMetaInfo::isSubclassOf(QObject *object, const QByteArray &superTypeName) { -#ifdef POOL_DEBUG - qWarning("QQmlPool: Allocating page"); -#endif - - Page *page = (Page *)malloc(sizeof(Page)); - page->header.next = _page; - page->header.free = page->memory; - _page = page; -} + if (object == 0) + return false; -void QQmlPool::clear() -{ -#ifdef POOL_DEBUG - int count = 0; -#endif + const QMetaObject *metaObject = object->metaObject(); - Class *c = _classList; - while (c) { - Class *n = c->_next; - c->_destroy(c); -#ifdef POOL_DEBUG - ++count; -#endif - c = n; - } + while (metaObject) { + QQmlType *qmlType = QQmlMetaType::qmlType(metaObject); + if (qmlType && qmlType->qmlTypeName() == QLatin1String(superTypeName)) // ignore version numbers + return true; -#ifdef POOL_DEBUG - qWarning("QQmlPool: Destroyed %d objects", count); -#endif + if (metaObject->className() == superTypeName) + return true; - Page *p = _page; - while (p) { - Page *n = p->header.next; - free(p); - p = n; + metaObject = metaObject->superClass(); } - _classList = 0; - _page = 0; + return false; } +void QQuickDesignerSupportMetaInfo::registerNotifyPropertyChangeCallBack(void (*callback)(QObject *, const QQuickDesignerSupport::PropertyName &)) +{ + QQmlDesignerMetaObject::registerNotifyPropertyChangeCallBack(callback); +} QT_END_NAMESPACE + diff --git a/src/quick/designer/qquickdesignersupportmetainfo_p.h b/src/quick/designer/qquickdesignersupportmetainfo_p.h new file mode 100644 index 0000000000..52bf8e3ac0 --- /dev/null +++ b/src/quick/designer/qquickdesignersupportmetainfo_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DESIGNERSUPPORTMETAINFO_H +#define DESIGNERSUPPORTMETAINFO_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickdesignersupport_p.h" + +#include <QObject> +#include <QByteArray> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QQuickDesignerSupportMetaInfo +{ +public: + static bool isSubclassOf(QObject *object, const QByteArray &superTypeName); + static void registerNotifyPropertyChangeCallBack(void (*callback)(QObject *, const QQuickDesignerSupport::PropertyName &)); +}; + +QT_END_NAMESPACE + +#endif // DESIGNERSUPPORTMETAINFO_H diff --git a/src/quick/designer/qquickdesignersupportproperties.cpp b/src/quick/designer/qquickdesignersupportproperties.cpp new file mode 100644 index 0000000000..96f09ada48 --- /dev/null +++ b/src/quick/designer/qquickdesignersupportproperties.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdesignersupportproperties_p.h" + +#include "qqmldesignermetaobject_p.h" +#include "qquickdesignercustomobjectdata_p.h" + +QT_BEGIN_NAMESPACE + +static void addToPropertyNameListIfNotBlackListed(QQuickDesignerSupport::PropertyNameList *propertyNameList, + const QQuickDesignerSupport::PropertyName &propertyName) +{ + if (!QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName)) + propertyNameList->append(propertyName); +} + +void QQuickDesignerSupportProperties::createNewDynamicProperty(QObject *object, QQmlEngine *engine, const QString &name) +{ + QQmlDesignerMetaObject::getNodeInstanceMetaObject(object, engine)->createNewDynamicProperty(name); +} + +void QQuickDesignerSupportProperties::registerNodeInstanceMetaObject(QObject *object, QQmlEngine *engine) +{ + // we just create one and the ownership goes automatically to the object in nodeinstance see init method + QQmlDesignerMetaObject::getNodeInstanceMetaObject(object, engine); +} + +bool QQuickDesignerSupportProperties::hasFullImplementedListInterface(const QQmlListReference &list) +{ + return list.isValid() && list.canCount() && list.canAt() && list.canAppend() && list.canClear(); +} + +void QQuickDesignerSupportProperties::registerCustomData(QObject *object) +{ + QQuickDesignerCustomObjectData::registerData(object); +} + +QVariant QQuickDesignerSupportProperties::getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName) +{ + return QQuickDesignerCustomObjectData::getResetValue(object, propertyName); +} + +void QQuickDesignerSupportProperties::doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName) +{ + QQuickDesignerCustomObjectData::doResetProperty(object, context, propertyName); +} + +bool QQuickDesignerSupportProperties::hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName) +{ + return QQuickDesignerCustomObjectData::hasValidResetBinding(object, propertyName); +} + +bool QQuickDesignerSupportProperties::hasBindingForProperty(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + bool *hasChanged) +{ + return QQuickDesignerCustomObjectData::hasBindingForProperty(object, context, propertyName, hasChanged); +} + +void QQuickDesignerSupportProperties::setPropertyBinding(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression) +{ + QQuickDesignerCustomObjectData::setPropertyBinding(object, context, propertyName, expression); +} + +void QQuickDesignerSupportProperties::keepBindingFromGettingDeleted(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName) +{ + QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(object, context, propertyName); +} + +bool QQuickDesignerSupportProperties::isPropertyQObject(const QMetaProperty &metaProperty) +{ + return QQmlMetaType::isQObject(metaProperty.userType()); +} + + +QObject *QQuickDesignerSupportProperties::readQObjectProperty(const QMetaProperty &metaProperty, QObject *object) +{ + return QQmlMetaType::toQObject(metaProperty.read(object)); +} + +void QQuickDesignerSupportProperties::getPropertyCache(QObject *object, QQmlEngine *engine) +{ + QQmlEnginePrivate::get(engine)->cache(object->metaObject()); +} + +QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::propertyNameListForWritableProperties(QObject *object, + const QQuickDesignerSupport::PropertyName &baseName, + QObjectList *inspectedObjects) +{ + QQuickDesignerSupport::PropertyNameList propertyNameList; + + QObjectList localObjectList; + + if (inspectedObjects == 0) + inspectedObjects = &localObjectList; + + + if (inspectedObjects->contains(object)) + return propertyNameList; + + inspectedObjects->append(object); + + const QMetaObject *metaObject = object->metaObject(); + for (int index = 0; index < metaObject->propertyCount(); ++index) { + QMetaProperty metaProperty = metaObject->property(index); + QQmlProperty declarativeProperty(object, QString::fromUtf8(metaProperty.name())); + if (declarativeProperty.isValid() && !declarativeProperty.isWritable() && declarativeProperty.propertyTypeCategory() == QQmlProperty::Object) { + if (declarativeProperty.name() != QLatin1String("parent")) { + QObject *childObject = QQmlMetaType::toQObject(declarativeProperty.read()); + if (childObject) + propertyNameList.append(propertyNameListForWritableProperties(childObject, + baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()) + + '.', inspectedObjects)); + } + } else if (QQmlValueTypeFactory::valueType(metaProperty.userType())) { + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(metaProperty.userType()); + valueType->setValue(metaProperty.read(object)); + propertyNameList.append(propertyNameListForWritableProperties(valueType, + baseName + QQuickDesignerSupport::PropertyName(metaProperty.name()) + + '.', inspectedObjects)); + } + + if (metaProperty.isReadable() && metaProperty.isWritable()) { + addToPropertyNameListIfNotBlackListed(&propertyNameList, + baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())); + } + } + + return propertyNameList; +} + +bool QQuickDesignerSupportProperties::isPropertyBlackListed(const QQuickDesignerSupport::PropertyName &propertyName) +{ + if (propertyName.contains(".") && propertyName.contains("__")) + return true; + + if (propertyName.count(".") > 1) + return true; + + return false; +} + +QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::allPropertyNames(QObject *object, + const QQuickDesignerSupport::PropertyName &baseName, + QObjectList *inspectedObjects) +{ + QQuickDesignerSupport::PropertyNameList propertyNameList; + + QObjectList localObjectList; + + if (inspectedObjects == 0) + inspectedObjects = &localObjectList; + + + if (inspectedObjects->contains(object)) + return propertyNameList; + + inspectedObjects->append(object); + + + const QMetaObject *metaObject = object->metaObject(); + for (int index = 0; index < metaObject->propertyCount(); ++index) { + QMetaProperty metaProperty = metaObject->property(index); + QQmlProperty declarativeProperty(object, QString::fromUtf8(metaProperty.name())); + if (declarativeProperty.isValid() && declarativeProperty.propertyTypeCategory() == QQmlProperty::Object) { + if (declarativeProperty.name() != QLatin1String("parent")) { + QObject *childObject = QQmlMetaType::toQObject(declarativeProperty.read()); + if (childObject) + propertyNameList.append(allPropertyNames(childObject, + baseName + + QQuickDesignerSupport::PropertyName(metaProperty.name()) + + '.', inspectedObjects)); + } + } else if (QQmlValueTypeFactory::valueType(metaProperty.userType())) { + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(metaProperty.userType()); + valueType->setValue(metaProperty.read(object)); + propertyNameList.append(baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())); + propertyNameList.append(allPropertyNames(valueType, + baseName + + QQuickDesignerSupport::PropertyName(metaProperty.name()) + + '.', inspectedObjects)); + } else { + addToPropertyNameListIfNotBlackListed(&propertyNameList, + baseName + QQuickDesignerSupport::PropertyName(metaProperty.name())); + } + } + + return propertyNameList; +} + + +QT_END_NAMESPACE + + + + diff --git a/src/quick/designer/qquickdesignersupportproperties_p.h b/src/quick/designer/qquickdesignersupportproperties_p.h new file mode 100644 index 0000000000..187bc6e2a6 --- /dev/null +++ b/src/quick/designer/qquickdesignersupportproperties_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DESIGNERSUPPORTPROPERTIES_H +#define DESIGNERSUPPORTPROPERTIES_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickdesignersupport_p.h" + +#include <QObject> +#include <QString> +#include <QVariant> +#include <QList> +#include <QByteArray> +#include <QQmlContext> +#include <QQmlListReference> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QQuickDesignerSupportProperties +{ +public: + static void createNewDynamicProperty(QObject *object, QQmlEngine *engine, const QString &name); + static void registerNodeInstanceMetaObject(QObject *object, QQmlEngine *engine); + static void registerCustomData(QObject *object); + static QVariant getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName); + static void doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName); + static bool hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName); + + static bool hasBindingForProperty(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + bool *hasChanged); + static void setPropertyBinding(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression); + static void keepBindingFromGettingDeleted(QObject *object, + QQmlContext *context, + const QQuickDesignerSupport::PropertyName &propertyName); + + static bool isPropertyQObject(const QMetaProperty &metaProperty); + static QObject *readQObjectProperty(const QMetaProperty &metaProperty, QObject *object); + + static void getPropertyCache(QObject *object, QQmlEngine *engine); + static bool isPropertyBlackListed(const QQuickDesignerSupport::PropertyName &propertyName); + static QQuickDesignerSupport::PropertyNameList propertyNameListForWritableProperties(QObject *object, + const QQuickDesignerSupport::PropertyName &baseName = QQuickDesignerSupport::PropertyName(), + QObjectList *inspectedObjects = 0); + static QQuickDesignerSupport::PropertyNameList allPropertyNames(QObject *object, + const QQuickDesignerSupport::PropertyName &baseName = QQuickDesignerSupport::PropertyName(), + QObjectList *inspectedObjects = 0); + static bool hasFullImplementedListInterface(const QQmlListReference &list); +}; + +QT_END_NAMESPACE + +#endif // DESIGNERSUPPORTPROPERTIES_H diff --git a/src/quick/designer/qquickdesignersupportpropertychanges.cpp b/src/quick/designer/qquickdesignersupportpropertychanges.cpp new file mode 100644 index 0000000000..92fa637b54 --- /dev/null +++ b/src/quick/designer/qquickdesignersupportpropertychanges.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdesignersupportpropertychanges_p.h" + +#include <private/qquickpropertychanges_p.h> +#include <private/qquickstateoperations_p.h> + +QT_BEGIN_NAMESPACE + +void QQuickDesignerSupportPropertyChanges::attachToState(QObject *propertyChanges) +{ + QQuickPropertyChanges *propertyChange = qobject_cast<QQuickPropertyChanges*>(propertyChanges); + + if (!propertyChange) + return; + + propertyChange->attachToState(); +} + +QObject *QQuickDesignerSupportPropertyChanges::targetObject(QObject *propertyChanges) +{ + QQuickPropertyChanges *propertyChange = qobject_cast<QQuickPropertyChanges*>(propertyChanges); + + if (!propertyChange) + return 0; + + return propertyChange->object(); +} + +void QQuickDesignerSupportPropertyChanges::removeProperty(QObject *propertyChanges, const QQuickDesignerSupport::PropertyName &propertyName) +{ + QQuickPropertyChanges *propertyChange = qobject_cast<QQuickPropertyChanges*>(propertyChanges); + + if (!propertyChange) + return; + + propertyChange->removeProperty(QString::fromUtf8(propertyName)); +} + +QVariant QQuickDesignerSupportPropertyChanges::getProperty(QObject *propertyChanges, + const QQuickDesignerSupport::PropertyName &propertyName) +{ + QQuickPropertyChanges *propertyChange = qobject_cast<QQuickPropertyChanges*>(propertyChanges); + + if (!propertyChange) + return QVariant(); + + return propertyChange->property(QString::fromUtf8(propertyName)); +} + +void QQuickDesignerSupportPropertyChanges::changeValue(QObject *propertyChanges, + const QQuickDesignerSupport::PropertyName &propertyName, + const QVariant &value) +{ + QQuickPropertyChanges *propertyChange = qobject_cast<QQuickPropertyChanges*>(propertyChanges); + + if (!propertyChange) + return; + + propertyChange->changeValue(QString::fromUtf8(propertyName), value); +} + +void QQuickDesignerSupportPropertyChanges::changeExpression(QObject *propertyChanges, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression) +{ + QQuickPropertyChanges *propertyChange = qobject_cast<QQuickPropertyChanges*>(propertyChanges); + + if (!propertyChange) + return; + + propertyChange->changeExpression(QString::fromUtf8(propertyName), expression); +} + +QObject *QQuickDesignerSupportPropertyChanges::stateObject(QObject *propertyChanges) +{ + QQuickPropertyChanges *propertyChange = qobject_cast<QQuickPropertyChanges*>(propertyChanges); + + if (!propertyChange) + return 0; + + return propertyChange->state(); +} + +bool QQuickDesignerSupportPropertyChanges::isNormalProperty(const QQuickDesignerSupport::PropertyName &propertyName) +{ + QMetaObject metaObject = QQuickPropertyChanges::staticMetaObject; + + return (metaObject.indexOfProperty(propertyName) > 0); // 'restoreEntryValues', 'explicit' +} + +void QQuickDesignerSupportPropertyChanges::detachFromState(QObject *propertyChanges) +{ + QQuickPropertyChanges *propertyChange = qobject_cast<QQuickPropertyChanges*>(propertyChanges); + + if (!propertyChange) + return; + + propertyChange->detachFromState(); +} + +QT_END_NAMESPACE + + diff --git a/src/quick/designer/qquickdesignersupportpropertychanges_p.h b/src/quick/designer/qquickdesignersupportpropertychanges_p.h new file mode 100644 index 0000000000..432383d5b4 --- /dev/null +++ b/src/quick/designer/qquickdesignersupportpropertychanges_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DESIGNERSUPPORTPROPERTYCHANGES_H +#define DESIGNERSUPPORTPROPERTYCHANGES_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickdesignersupport_p.h" + +#include <QVariant> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QQuickDesignerSupportPropertyChanges +{ +public: + static void detachFromState(QObject *propertyChanges); + static void attachToState(QObject *propertyChanges); + static QObject *targetObject(QObject *propertyChanges); + static void removeProperty(QObject *propertyChanges, const QQuickDesignerSupport::PropertyName &propertyName); + static QVariant getProperty(QObject *propertyChanges, const QQuickDesignerSupport::PropertyName &propertyName); + static void changeValue(QObject *propertyChanges, const QQuickDesignerSupport::PropertyName &propertyName, const QVariant &value); + static void changeExpression(QObject *propertyChanges, const QQuickDesignerSupport::PropertyName &propertyName, const QString &expression); + static QObject *stateObject(QObject *propertyChanges); + static bool isNormalProperty(const QQuickDesignerSupport::PropertyName &propertyName); +}; + +QT_END_NAMESPACE + +#endif // DESIGNERSUPPORTPROPERTYCHANGES_H diff --git a/src/quick/designer/qquickdesignersupportstates.cpp b/src/quick/designer/qquickdesignersupportstates.cpp new file mode 100644 index 0000000000..b75ec7115a --- /dev/null +++ b/src/quick/designer/qquickdesignersupportstates.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdesignersupportstates_p.h" + +#include <private/qquickstategroup_p.h> +#include <private/qquickpropertychanges_p.h> + +QT_BEGIN_NAMESPACE + +bool QQuickDesignerSupportStates::isStateActive(QObject *object, QQmlContext *context) +{ + QQuickState *stateObject = qobject_cast<QQuickState*>(object); + + if (!stateObject) + return false; + + QQuickStateGroup *stateGroup = stateObject->stateGroup(); + + QQmlProperty property(object, QLatin1String("name"), context); + + return stateObject && stateGroup && stateGroup->state() == property.read(); +} + +void QQuickDesignerSupportStates::activateState(QObject *object, QQmlContext *context) +{ + QQuickState *stateObject = qobject_cast<QQuickState*>(object); + + if (!stateObject) + return; + + QQuickStateGroup *stateGroup = stateObject->stateGroup(); + + QQmlProperty property(object, QLatin1String("name"), context); + + stateGroup->setState(property.read().toString()); +} + +void QQuickDesignerSupportStates::deactivateState(QObject *object) +{ + QQuickState *stateObject = qobject_cast<QQuickState*>(object); + + if (!stateObject) + return; + + QQuickStateGroup *stateGroup = stateObject->stateGroup(); + + if (stateGroup) + stateGroup->setState(QString()); +} + +bool QQuickDesignerSupportStates::changeValueInRevertList(QObject *state, QObject *target, + const QQuickDesignerSupport::PropertyName &propertyName, + const QVariant &value) +{ + QQuickState *stateObject = qobject_cast<QQuickState*>(state); + + if (!stateObject) + return false; + + return stateObject->changeValueInRevertList(target, QString::fromUtf8(propertyName), value); +} + +bool QQuickDesignerSupportStates::updateStateBinding(QObject *state, QObject *target, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression) +{ + QQuickState *stateObject = qobject_cast<QQuickState*>(state); + + if (!stateObject) + return false; + + return stateObject->changeValueInRevertList(target, QString::fromUtf8(propertyName), expression); +} + +bool QQuickDesignerSupportStates::resetStateProperty(QObject *state, QObject *target, + const QQuickDesignerSupport::PropertyName &propertyName, + const QVariant & /* resetValue */) +{ + QQuickState *stateObject = qobject_cast<QQuickState*>(state); + + if (!stateObject) + return false; + + return stateObject->removeEntryFromRevertList(target, QString::fromUtf8(propertyName)); +} + +QT_END_NAMESPACE + diff --git a/src/quick/designer/qquickdesignersupportstates_p.h b/src/quick/designer/qquickdesignersupportstates_p.h new file mode 100644 index 0000000000..daa5a2ff01 --- /dev/null +++ b/src/quick/designer/qquickdesignersupportstates_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DESIGNERSUPPORTSTATES_H +#define DESIGNERSUPPORTSTATES_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickdesignersupport_p.h" + +#include <QVariant> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_EXPORT QQuickDesignerSupportStates +{ +public: + static bool isStateActive(QObject *object, QQmlContext *context); + static void activateState(QObject *object, QQmlContext *context); + static void deactivateState(QObject *object); + static bool changeValueInRevertList(QObject *state, + QObject *target, + const QQuickDesignerSupport::PropertyName &propertyName, + const QVariant &value); + + static bool updateStateBinding(QObject *state, QObject *target, + const QQuickDesignerSupport::PropertyName &propertyName, + const QString &expression); + + static bool resetStateProperty(QObject *state, QObject *target, + const QQuickDesignerSupport::PropertyName &propertyName, + const QVariant &); +}; + +QT_END_NAMESPACE + +#endif // DESIGNERSUPPORTSTATES_H diff --git a/src/quick/designer/designerwindowmanager.cpp b/src/quick/designer/qquickdesignerwindowmanager.cpp index f37d6a2180..b1ad42ddb4 100644 --- a/src/quick/designer/designerwindowmanager.cpp +++ b/src/quick/designer/qquickdesignerwindowmanager.cpp @@ -31,35 +31,34 @@ ** ****************************************************************************/ -#include "designerwindowmanager_p.h" +#include "qquickdesignerwindowmanager_p.h" #include "private/qquickwindow_p.h" #include <QtGui/QOpenGLContext> #include <QtQuick/QQuickWindow> - QT_BEGIN_NAMESPACE -DesignerWindowManager::DesignerWindowManager() +QQuickDesignerWindowManager::QQuickDesignerWindowManager() : m_sgContext(QSGContext::createDefaultContext()) { m_renderContext.reset(new QSGRenderContext(m_sgContext.data())); } -void DesignerWindowManager::show(QQuickWindow *window) +void QQuickDesignerWindowManager::show(QQuickWindow *window) { makeOpenGLContext(window); } -void DesignerWindowManager::hide(QQuickWindow *) +void QQuickDesignerWindowManager::hide(QQuickWindow *) { } -void DesignerWindowManager::windowDestroyed(QQuickWindow *) +void QQuickDesignerWindowManager::windowDestroyed(QQuickWindow *) { } -void DesignerWindowManager::makeOpenGLContext(QQuickWindow *window) +void QQuickDesignerWindowManager::makeOpenGLContext(QQuickWindow *window) { if (!m_openGlContext) { m_openGlContext.reset(new QOpenGLContext()); @@ -73,31 +72,31 @@ void DesignerWindowManager::makeOpenGLContext(QQuickWindow *window) } } -void DesignerWindowManager::exposureChanged(QQuickWindow *) +void QQuickDesignerWindowManager::exposureChanged(QQuickWindow *) { } -QImage DesignerWindowManager::grab(QQuickWindow *) +QImage QQuickDesignerWindowManager::grab(QQuickWindow *) { return QImage(); } -void DesignerWindowManager::maybeUpdate(QQuickWindow *) +void QQuickDesignerWindowManager::maybeUpdate(QQuickWindow *) { } -QSGContext *DesignerWindowManager::sceneGraphContext() const +QSGContext *QQuickDesignerWindowManager::sceneGraphContext() const { return m_sgContext.data(); } -void DesignerWindowManager::createOpenGLContext(QQuickWindow *window) +void QQuickDesignerWindowManager::createOpenGLContext(QQuickWindow *window) { window->create(); window->update(); } -void DesignerWindowManager::update(QQuickWindow *window) +void QQuickDesignerWindowManager::update(QQuickWindow *window) { makeOpenGLContext(window); } diff --git a/src/quick/designer/designerwindowmanager_p.h b/src/quick/designer/qquickdesignerwindowmanager_p.h index e7a7314c20..6d98e25347 100644 --- a/src/quick/designer/designerwindowmanager_p.h +++ b/src/quick/designer/qquickdesignerwindowmanager_p.h @@ -60,11 +60,11 @@ class QSGRenderContext; class QAnimationDriver; class QOpenGLContext; -class DesignerWindowManager : public QSGRenderLoop +class QQuickDesignerWindowManager : public QSGRenderLoop { Q_OBJECT public: - DesignerWindowManager(); + QQuickDesignerWindowManager(); void show(QQuickWindow *window); void hide(QQuickWindow *window); diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index 75507c68d2..9932747dd3 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -46,7 +46,7 @@ #include <QtCore/QBuffer> #include <QtCore/qdatetime.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4scopedvalue_p.h> @@ -994,7 +994,7 @@ void QQuickCanvasItem::loadImage(const QUrl& url) if (!d->pixmaps.contains(fullPathUrl)) { QQuickPixmap* pix = new QQuickPixmap(); QQmlRefPointer<QQuickCanvasPixmap> canvasPix; - canvasPix.take(new QQuickCanvasPixmap(pix)); + canvasPix.adopt(new QQuickCanvasPixmap(pix)); d->pixmaps.insert(fullPathUrl, canvasPix); pix->load(qmlEngine(this) diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h index bcd7072903..4a1a59d61e 100644 --- a/src/quick/items/context2d/qquickcanvasitem_p.h +++ b/src/quick/items/context2d/qquickcanvasitem_p.h @@ -68,8 +68,6 @@ private: class QQuickCanvasItem : public QQuickItem { Q_OBJECT - Q_ENUMS(RenderTarget) - Q_ENUMS(RenderStrategy) Q_PROPERTY(bool available READ isAvailable NOTIFY availableChanged) Q_PROPERTY(QString contextType READ contextType WRITE setContextType NOTIFY contextTypeChanged) @@ -85,12 +83,14 @@ public: Image, FramebufferObject }; + Q_ENUM(RenderTarget) enum RenderStrategy { Immediate, Threaded, Cooperative }; + Q_ENUM(RenderStrategy) QQuickCanvasItem(QQuickItem *parent = 0); ~QQuickCanvasItem(); diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 4aa3b1c8d0..0a09ee42de 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -53,7 +53,7 @@ #include <private/qv4object_p.h> #include <private/qquickwindow_p.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4objectproto_p.h> #include <private/qv4scopedvalue_p.h> @@ -300,7 +300,7 @@ static QStringList qExtractFontFamiliesFromString(const QString &fontFamiliesStr */ static bool qSetFontFamilyFromTokens(QFont &font, const QStringList &fontFamilyTokens) { - foreach (QString fontFamilyToken, fontFamilyTokens) { + foreach (const QString &fontFamilyToken, fontFamilyTokens) { QFontDatabase fontDatabase; if (fontDatabase.hasFamily(fontFamilyToken)) { font.setFamily(fontFamilyToken); @@ -411,7 +411,7 @@ static QFont qt_font_from_string(const QString& fontString, const QFont ¤t int usedTokens = NoTokens; // Optional properties can be in any order, but font-size and font-family must be last. - foreach (const QString token, tokens) { + foreach (const QString &token, tokens) { if (token.compare(QLatin1String("normal")) == 0) { if (!(usedTokens & FontStyle) || !(usedTokens & FontVariant) || !(usedTokens & FontWeight)) { // Could be font-style, font-variant or font-weight. @@ -884,7 +884,7 @@ struct QQuickJSContext2DPixelData : public QV4::Object V4_OBJECT2(QQuickJSContext2DPixelData, QV4::Object) V4_NEEDS_DESTROY - static QV4::ReturnedValue getIndexed(QV4::Managed *m, uint index, bool *hasProperty); + 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 QV4::ReturnedValue proto_get_length(QV4::CallContext *ctx); @@ -1379,7 +1379,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_fillStyle(QV4::CallContext *ctx QV4::ScopedValue value(scope, ctx->argument(0)); - if (value->asObject()) { + if (value->as<Object>()) { QColor color = scope.engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>(); if (color.isValid()) { r->d()->context->state.fillStyle = color; @@ -1488,7 +1488,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_strokeStyle(QV4::CallContext *c QV4::ScopedValue value(scope, ctx->argument(0)); - if (value->asObject()) { + if (value->as<Object>()) { QColor color = scope.engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>(); if (color.isValid()) { r->d()->context->state.fillStyle = color; @@ -1719,7 +1719,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(QV4::CallCon } else { QImage patternTexture; - if (QV4::Object *o = ctx->args()[0].asObject()) { + if (const QV4::Object *o = ctx->args()[0].as<Object>()) { QV4::ScopedString s(scope, scope.engine->newString(QStringLiteral("data"))); QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, o->get(s)); if (!!pixelData) { @@ -2912,7 +2912,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(QV4::CallContext } else if (QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(qobjectWrapper->object())) { QImage img = canvas->toImage(); if (!img.isNull()) - pixmap.take(new QQuickCanvasPixmap(img)); + pixmap.adopt(new QQuickCanvasPixmap(img)); } else { V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); } @@ -2921,7 +2921,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(QV4::CallContext if (!!imageData) { QV4::Scoped<QQuickJSContext2DPixelData> pix(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>()); if (pix && !pix->d()->image.isNull()) { - pixmap.take(new QQuickCanvasPixmap(pix->d()->image)); + pixmap.adopt(new QQuickCanvasPixmap(pix->d()->image)); } else { V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); } @@ -3089,12 +3089,12 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::proto_get_length(QV4::CallContext return QV4::Encode(r->d()->image.width() * r->d()->image.height() * 4); } -QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(QV4::Managed *m, uint index, bool *hasProperty) +QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m, uint index, bool *hasProperty) { Q_ASSERT(m->as<QQuickJSContext2DPixelData>()); - QV4::ExecutionEngine *v4 = static_cast<QQuickJSContext2DPixelData *>(m)->engine(); + QV4::ExecutionEngine *v4 = static_cast<const QQuickJSContext2DPixelData *>(m)->engine(); QV4::Scope scope(v4); - QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m)); + QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<const QQuickJSContext2DPixelData *>(m)); if (index < static_cast<quint32>(r->d()->image.width() * r->d()->image.height() * 4)) { if (hasProperty) @@ -3357,7 +3357,7 @@ QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(QV4::CallCo qreal pos = ctx->args()[0].toNumber(); QColor color; - if (ctx->args()[1].asObject()) { + if (ctx->args()[1].as<Object>()) { color = scope.engine->toVariant(ctx->args()[1], qMetaTypeId<QColor>()).value<QColor>(); } else { color = qt_color_from_string(ctx->args()[1]); @@ -4235,7 +4235,7 @@ QQuickContext2DEngineData::QQuickContext2DEngineData(QV4::ExecutionEngine *v4) gradientProto = proto; proto = scope.engine->newObject(); - proto->defineAccessorProperty(scope.engine->id_length, QQuickJSContext2DPixelData::proto_get_length, 0); + proto->defineAccessorProperty(scope.engine->id_length(), QQuickJSContext2DPixelData::proto_get_length, 0); pixelArrayProto = proto; } diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index 67d3a2e4fb..78fa26d791 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -48,7 +48,7 @@ #include <private/qv8engine_p.h> #include <QtCore/QWaitCondition> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> //#define QQUICKCONTEXT2D_DEBUG //enable this for just DEBUG purpose! diff --git a/src/quick/items/qquickanchors.cpp b/src/quick/items/qquickanchors.cpp index f559f166bf..606d4c45da 100644 --- a/src/quick/items/qquickanchors.cpp +++ b/src/quick/items/qquickanchors.cpp @@ -414,13 +414,27 @@ void QQuickAnchorsPrivate::updateMe() void QQuickAnchorsPrivate::updateOnComplete() { //optimization to only set initial dependencies once, at completion time - QSet<QQuickItem *> dependencies; - dependencies << fill << centerIn - << left.item << right.item << hCenter.item - << top.item << bottom.item << vCenter.item << baseline.item; - - foreach (QQuickItem *dependency, dependencies) - addDepend(dependency); + QQuickItem *dependencies[9]; + dependencies[0] = fill; + dependencies[1] = centerIn; + dependencies[2] = left.item; + dependencies[3] = right.item; + dependencies[4] = hCenter.item; + dependencies[5] = top.item; + dependencies[6] = bottom.item; + dependencies[7] = vCenter.item; + dependencies[8] = baseline.item; + + std::sort(dependencies, dependencies + 9); + + QQuickItem *lastDependency = 0; + for (int i = 0; i < 9; ++i) { + QQuickItem *dependency = dependencies[i]; + if (lastDependency != dependency) { + addDepend(dependency); + lastDependency = dependency; + } + } update(); } diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h index 4778afc88b..a10ebb4b73 100644 --- a/src/quick/items/qquickanimatedsprite_p.h +++ b/src/quick/items/qquickanimatedsprite_p.h @@ -70,12 +70,12 @@ class Q_AUTOTEST_EXPORT QQuickAnimatedSprite : public QQuickItem Q_PROPERTY(bool paused READ paused WRITE setPaused NOTIFY pausedChanged) Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY currentFrameChanged) - Q_ENUMS(LoopParameters) public: explicit QQuickAnimatedSprite(QQuickItem *parent = 0); enum LoopParameters { Infinite = -1 }; + Q_ENUM(LoopParameters) bool running() const { diff --git a/src/quick/items/qquickborderimage_p.h b/src/quick/items/qquickborderimage_p.h index f2a172fad3..8a88e3d0d3 100644 --- a/src/quick/items/qquickborderimage_p.h +++ b/src/quick/items/qquickborderimage_p.h @@ -44,7 +44,6 @@ class QQuickBorderImagePrivate; class Q_AUTOTEST_EXPORT QQuickBorderImage : public QQuickImageBase { Q_OBJECT - Q_ENUMS(TileMode) Q_PROPERTY(QQuickScaleGrid *border READ border CONSTANT) Q_PROPERTY(TileMode horizontalTileMode READ horizontalTileMode WRITE setHorizontalTileMode NOTIFY horizontalTileModeChanged) @@ -59,6 +58,7 @@ public: QQuickScaleGrid *border(); enum TileMode { Stretch = Qt::StretchTile, Repeat = Qt::RepeatTile, Round = Qt::RoundTile }; + Q_ENUM(TileMode) TileMode horizontalTileMode() const; void setHorizontalTileMode(TileMode); diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h index 4bd4cfc6fd..2b4b2a51d4 100644 --- a/src/quick/items/qquickdrag_p.h +++ b/src/quick/items/qquickdrag_p.h @@ -140,7 +140,6 @@ class Q_AUTOTEST_EXPORT QQuickDrag : public QObject { Q_OBJECT - Q_ENUMS(Axis DragType) Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget) Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged) Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged) @@ -160,12 +159,14 @@ public: ~QQuickDrag(); enum DragType { None, Automatic, Internal }; + Q_ENUM(DragType) QQuickItem *target() const; void setTarget(QQuickItem *target); void resetTarget(); enum Axis { XAxis=0x01, YAxis=0x02, XAndYAxis=0x03, XandYAxis=XAndYAxis }; + Q_ENUM(Axis) Axis axis() const; void setAxis(Axis); diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 19fb66c19c..ea4398bc71 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -619,7 +619,7 @@ is finished. \qmlsignal QtQuick::Flickable::movementStarted() This signal is emitted when the view begins moving due to user - interaction. + interaction or a generated flick(). The corresponding handler is \c onMovementStarted. */ @@ -628,9 +628,9 @@ is finished. \qmlsignal QtQuick::Flickable::movementEnded() This signal is emitted when the view stops moving due to user - interaction. If a flick was generated, this signal will + interaction or a generated flick(). If a flick was active, this signal will be emitted once the flick stops. If a flick was not - generated, this signal will be emitted when the + active, this signal will be emitted when the user stops dragging - i.e. a mouse or touch release. The corresponding handler is \c onMovementEnded. @@ -1347,7 +1347,7 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event) if (window() && window()->mouseGrabberItem()) { QPointF localPos = window()->mouseGrabberItem()->mapFromScene(event->windowPos()); QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos)); - window()->sendEvent(window()->mouseGrabberItem(), mouseEvent.data()); + QCoreApplication::sendEvent(window(), mouseEvent.data()); } // And the event has been consumed @@ -1451,6 +1451,7 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) d->lastPosTime = currentTimestamp; d->accumulatedWheelPixelDelta += QVector2D(event->pixelDelta()); d->drag(currentTimestamp, event->type(), event->posF(), d->accumulatedWheelPixelDelta, true, !d->scrollingPhase, true, velocity); + event->accept(); } if (!event->isAccepted()) @@ -1670,6 +1671,9 @@ void QQuickFlickable::geometryChanged(const QRectF &newGeometry, \qmlmethod QtQuick::Flickable::flick(qreal xVelocity, qreal yVelocity) Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec. + + Calling this method will update the corresponding moving and flicking properties and signals, + just like a real flick. */ void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity) @@ -1679,8 +1683,15 @@ void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity) d->vData.reset(); d->hData.velocity = xVelocity; d->vData.velocity = yVelocity; + bool flickedX = d->flickX(xVelocity); bool flickedY = d->flickY(yVelocity); + + if (flickedX) + d->hMoved = true; + if (flickedY) + d->vMoved = true; + movementStarting(); d->flickingStarted(flickedX, flickedY); } diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h index 3c3cd362dd..c974da66d6 100644 --- a/src/quick/items/qquickflickable_p.h +++ b/src/quick/items/qquickflickable_p.h @@ -93,7 +93,6 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFlickable : public QQuickItem Q_PROPERTY(QQmlListProperty<QQuickItem> flickableChildren READ flickableChildren) Q_CLASSINFO("DefaultProperty", "flickableData") - Q_ENUMS(FlickableDirection) Q_FLAGS(BoundsBehavior) public: @@ -177,6 +176,7 @@ public: QQuickItem *contentItem(); enum FlickableDirection { AutoFlickDirection=0x00, HorizontalFlick=0x01, VerticalFlick=0x02, HorizontalAndVerticalFlick=0x03 }; + Q_ENUM(FlickableDirection) FlickableDirection flickableDirection() const; void setFlickableDirection(FlickableDirection); diff --git a/src/quick/items/qquickflipable_p.h b/src/quick/items/qquickflipable_p.h index bd2efe0676..31bfe97923 100644 --- a/src/quick/items/qquickflipable_p.h +++ b/src/quick/items/qquickflipable_p.h @@ -47,7 +47,6 @@ class Q_AUTOTEST_EXPORT QQuickFlipable : public QQuickItem { Q_OBJECT - Q_ENUMS(Side) Q_PROPERTY(QQuickItem *front READ front WRITE setFront NOTIFY frontChanged) Q_PROPERTY(QQuickItem *back READ back WRITE setBack NOTIFY backChanged) Q_PROPERTY(Side side READ side NOTIFY sideChanged) @@ -64,6 +63,7 @@ public: void setBack(QQuickItem *); enum Side { Front, Back }; + Q_ENUM(Side) Side side() const; Q_SIGNALS: diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp index 6031315b90..74abd8cf9f 100644 --- a/src/quick/items/qquickframebufferobject.cpp +++ b/src/quick/items/qquickframebufferobject.cpp @@ -47,11 +47,13 @@ class QQuickFramebufferObjectPrivate : public QQuickItemPrivate public: QQuickFramebufferObjectPrivate() : followsItemSize(true) + , mirrorVertically(false) , node(0) { } bool followsItemSize; + bool mirrorVertically; mutable QSGFramebufferObjectNode *node; }; @@ -138,6 +140,34 @@ bool QQuickFramebufferObject::textureFollowsItemSize() const } /*! + * \property QQuickFramebufferObject::mirrorVertically + * + * This property controls if the size of the FBO's contents should be mirrored + * vertically when drawing. This allows easy integration of third-party + * rendering code that does not follow the standard expectations. + * + * The default value is \c {false}. + * + * \since 5.6 + */ + +void QQuickFramebufferObject::setMirrorVertically(bool enable) +{ + Q_D(QQuickFramebufferObject); + if (d->mirrorVertically == enable) + return; + d->mirrorVertically = enable; + emit mirrorVerticallyChanged(d->mirrorVertically); + update(); +} + +bool QQuickFramebufferObject::mirrorVertically() const +{ + Q_D(const QQuickFramebufferObject); + return d->mirrorVertically; +} + +/*! * \internal */ void QQuickFramebufferObject::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) @@ -290,6 +320,7 @@ QSGNode *QQuickFramebufferObject::updatePaintNode(QSGNode *node, UpdatePaintNode QQuickWindow::TextureHasAlphaChannel)); } + n->setTextureCoordinatesTransform(d->mirrorVertically ? QSGSimpleTextureNode::MirrorVertically : QSGSimpleTextureNode::NoTransform); n->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest); n->setRect(0, 0, width(), height()); diff --git a/src/quick/items/qquickframebufferobject.h b/src/quick/items/qquickframebufferobject.h index 4a0248c082..7fb7262222 100644 --- a/src/quick/items/qquickframebufferobject.h +++ b/src/quick/items/qquickframebufferobject.h @@ -49,6 +49,7 @@ class Q_QUICK_EXPORT QQuickFramebufferObject : public QQuickItem Q_DECLARE_PRIVATE(QQuickFramebufferObject) Q_PROPERTY(bool textureFollowsItemSize READ textureFollowsItemSize WRITE setTextureFollowsItemSize NOTIFY textureFollowsItemSizeChanged) + Q_PROPERTY(bool mirrorVertically READ mirrorVertically WRITE setMirrorVertically NOTIFY mirrorVerticallyChanged) public: @@ -73,6 +74,9 @@ public: bool textureFollowsItemSize() const; void setTextureFollowsItemSize(bool follows); + bool mirrorVertically() const; + void setMirrorVertically(bool enable); + virtual Renderer *createRenderer() const = 0; bool isTextureProvider() const Q_DECL_OVERRIDE; @@ -87,6 +91,7 @@ protected: Q_SIGNALS: void textureFollowsItemSizeChanged(bool); + void mirrorVerticallyChanged(bool); private Q_SLOTS: void invalidateSceneGraph(); diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 99c77488f4..3ac9c6eb1c 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -267,9 +267,13 @@ qreal QQuickGridViewPrivate::originPosition() const qreal QQuickGridViewPrivate::lastPosition() const { qreal pos = 0; - if (model && model->count()) { - // get end position of last item - pos = (rowPosAt(model->count() - 1) + rowSize()); + if (model && (model->count() || !visibleItems.isEmpty())) { + qreal lastRowPos = model->count() ? rowPosAt(model->count() - 1) : 0; + if (!visibleItems.isEmpty()) { + // If there are items in delayRemove state, they may be after any items linked to the model + lastRowPos = qMax(lastRowPos, static_cast<FxGridItemSG*>(visibleItems.last())->rowPos()); + } + pos = lastRowPos + rowSize(); } return pos; } @@ -918,13 +922,13 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte tempPosition -= bias; } FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart); - if (!topItem && strictHighlightRange && currentItem) { + if (strictHighlightRange && currentItem) { // StrictlyEnforceRange always keeps an item in range updateHighlight(); topItem = currentItem; } FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd); - if (!bottomItem && strictHighlightRange && currentItem) { + if (strictHighlightRange && currentItem) { // StrictlyEnforceRange always keeps an item in range updateHighlight(); bottomItem = currentItem; diff --git a/src/quick/items/qquickgridview_p.h b/src/quick/items/qquickgridview_p.h index 7e1ace01dd..389ef27585 100644 --- a/src/quick/items/qquickgridview_p.h +++ b/src/quick/items/qquickgridview_p.h @@ -52,8 +52,6 @@ class Q_AUTOTEST_EXPORT QQuickGridView : public QQuickItemView Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged) - Q_ENUMS(SnapMode) - Q_ENUMS(Flow) Q_CLASSINFO("DefaultProperty", "data") public: @@ -61,6 +59,7 @@ public: FlowLeftToRight = LeftToRight, FlowTopToBottom = TopToBottom }; + Q_ENUM(Flow) QQuickGridView(QQuickItem *parent=0); ~QQuickGridView(); @@ -78,6 +77,7 @@ public: void setCellHeight(qreal); enum SnapMode { NoSnap, SnapToRow, SnapOneRow }; + Q_ENUM(SnapMode) SnapMode snapMode() const; void setSnapMode(SnapMode mode); diff --git a/src/quick/items/qquickimage_p.h b/src/quick/items/qquickimage_p.h index be514ae2f5..421360bd35 100644 --- a/src/quick/items/qquickimage_p.h +++ b/src/quick/items/qquickimage_p.h @@ -40,12 +40,9 @@ QT_BEGIN_NAMESPACE class QQuickImagePrivate; -class Q_AUTOTEST_EXPORT QQuickImage : public QQuickImageBase +class Q_QUICK_PRIVATE_EXPORT QQuickImage : public QQuickImageBase { Q_OBJECT - Q_ENUMS(FillMode) - Q_ENUMS(HAlignment) - Q_ENUMS(VAlignment) Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedGeometryChanged) @@ -62,11 +59,14 @@ public: enum HAlignment { AlignLeft = Qt::AlignLeft, AlignRight = Qt::AlignRight, AlignHCenter = Qt::AlignHCenter }; + Q_ENUM(HAlignment) enum VAlignment { AlignTop = Qt::AlignTop, AlignBottom = Qt::AlignBottom, AlignVCenter = Qt::AlignVCenter }; + Q_ENUM(VAlignment) enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally, Pad }; + Q_ENUM(FillMode) FillMode fillMode() const; void setFillMode(FillMode); diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp index 223cb8f46f..e54f5bb9c9 100644 --- a/src/quick/items/qquickimagebase.cpp +++ b/src/quick/items/qquickimagebase.cpp @@ -213,15 +213,27 @@ void QQuickImageBase::load() d->devicePixelRatio = 1.0; QUrl loadUrl = d->url; - if (d->url.scheme() == QStringLiteral("image") - || d->url.toString().endsWith(QLatin1String(".svg")) - || d->url.toString().endsWith(QLatin1String(".svgz"))) { - // 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. - if (!d->sourcesize.isEmpty()) + + // 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() == QStringLiteral("image")) { + setDevicePixelRatio = true; + } else { + QString stringUrl = loadUrl.toString(); + if (stringUrl.endsWith(QLatin1String("svg")) || + stringUrl.endsWith(QLatin1String("svgz"))) { + setDevicePixelRatio = true; + } + } + + if (setDevicePixelRatio) d->devicePixelRatio = targetDevicePixelRatio; - } else { + } + + if (!setDevicePixelRatio) { // (possible) local file: loadUrl and d->devicePixelRatio will be modified if // an "@2x" file is found. resolve2xLocalFile(d->url, targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio); @@ -368,7 +380,7 @@ void QQuickImageBase::resolve2xLocalFile(const QUrl &url, qreal targetDevicePixe // Look for an @2x version QString localFile2x = image2xPath(localFile); - if (!QFile(localFile2x).exists()) + if (!QFile::exists(localFile2x)) return; // @2x file found found: Change url and devicePixelRatio diff --git a/src/quick/items/qquickimagebase_p.h b/src/quick/items/qquickimagebase_p.h index 4fcfaecd7d..98943a235c 100644 --- a/src/quick/items/qquickimagebase_p.h +++ b/src/quick/items/qquickimagebase_p.h @@ -43,7 +43,6 @@ class QQuickImageBasePrivate; class Q_QUICK_PRIVATE_EXPORT QQuickImageBase : public QQuickImplicitSizeItem { Q_OBJECT - Q_ENUMS(Status) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) @@ -57,6 +56,7 @@ public: QQuickImageBase(QQuickItem *parent=0); ~QQuickImageBase(); enum Status { Null, Ready, Loading, Error }; + Q_ENUM(Status) Status status() const; qreal progress() const; diff --git a/src/quick/items/qquickimplicitsizeitem_p_p.h b/src/quick/items/qquickimplicitsizeitem_p_p.h index f2e502af15..d606474e9d 100644 --- a/src/quick/items/qquickimplicitsizeitem_p_p.h +++ b/src/quick/items/qquickimplicitsizeitem_p_p.h @@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE -class QQuickImplicitSizeItemPrivate : public QQuickItemPrivate +class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItemPrivate : public QQuickItemPrivate { Q_DECLARE_PUBLIC(QQuickImplicitSizeItem) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 5fd1882216..32c3e651dd 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1304,8 +1304,8 @@ void QQuickKeysAttached::setPriority(Priority order) void QQuickKeysAttached::componentComplete() { - Q_D(QQuickKeysAttached); #ifndef QT_NO_IM + Q_D(QQuickKeysAttached); if (d->item) { for (int ii = 0; ii < d->targets.count(); ++ii) { QQuickItem *targetItem = d->targets.at(ii); @@ -1608,6 +1608,85 @@ void QQuickItemPrivate::setLayoutMirror(bool mirror) } } +/*! + \qmltype EnterKey + \instantiates QQuickEnterKeyAttached + \inqmlmodule QtQuick + \ingroup qtquick-input + \since 5.6 + \brief Provides a property to manipulate the appearance of Enter key on + an on-screen keyboard. + + The EnterKey attached property is used to manipulate the appearance and + behavior of the Enter key on an on-screen keyboard. +*/ + +/*! + \qmlproperty enumeration QtQuick::EnterKey::type + + Holds the type of the Enter key. + + \note Not all of these values are supported on all platforms. For + unsupported values the default key is used instead. + + \value Qt.EnterKeyDefault The default Enter key. This can be either a + button to accept the input and close the + keyboard, or a \e Return button to enter a + newline in case of a multi-line input field. + + \value Qt.EnterKeyReturn Show a \e Return button that inserts a + newline. + + \value Qt.EnterKeyDone Show a \e {"Done"} button. Typically, the + keyboard is expected to close when the button + is pressed. + + \value Qt.EnterKeyGo Show a \e {"Go"} button. Typically used in an + address bar when entering a URL. + + \value Qt.EnterKeySend Show a \e {"Send"} button. + + \value Qt.EnterKeySearch Show a \e {"Search"} button. + + \value Qt.EnterKeyNext Show a \e {"Next"} button. Typically used in a + form to allow navigating to the next input + field without the keyboard closing. + + \value Qt.EnterKeyPrevious Show a \e {"Previous"} button. +*/ + +QQuickEnterKeyAttached::QQuickEnterKeyAttached(QObject *parent) + : QObject(parent), itemPrivate(0), keyType(Qt::EnterKeyDefault) +{ + if (QQuickItem *item = qobject_cast<QQuickItem*>(parent)) { + itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->extra.value().enterKeyAttached = this; + } else + qmlInfo(parent) << tr("EnterKey attached property only works with Items"); +} + +QQuickEnterKeyAttached *QQuickEnterKeyAttached::qmlAttachedProperties(QObject *object) +{ + return new QQuickEnterKeyAttached(object); +} + +Qt::EnterKeyType QQuickEnterKeyAttached::type() const +{ + return keyType; +} + +void QQuickEnterKeyAttached::setType(Qt::EnterKeyType type) +{ + if (keyType != type) { + keyType = type; +#ifndef QT_NO_IM + if (itemPrivate && itemPrivate->activeFocus) + QGuiApplication::inputMethod()->update(Qt::ImEnterKeyType); +#endif + typeChanged(); + } +} + void QQuickItemPrivate::setAccessible() { isAccessible = true; @@ -2776,7 +2855,7 @@ void QQuickItemPrivate::refWindow(QQuickWindow *c) window = c; if (polishScheduled) - QQuickWindowPrivate::get(window)->itemsToPolish.insert(q); + QQuickWindowPrivate::get(window)->itemsToPolish.append(q); if (!parentItem) QQuickWindowPrivate::get(window)->parentlessItems.insert(q); @@ -2808,7 +2887,7 @@ void QQuickItemPrivate::derefWindow() removeFromDirtyList(); QQuickWindowPrivate *c = QQuickWindowPrivate::get(window); if (polishScheduled) - c->itemsToPolish.remove(q); + c->itemsToPolish.removeOne(q); QMutableHashIterator<int, QQuickItem *> itemTouchMapIt(c->itemForTouchPointId); while (itemTouchMapIt.hasNext()) { if (itemTouchMapIt.next().value() == q) @@ -3960,6 +4039,11 @@ QVariant QQuickItem::inputMethodQuery(Qt::InputMethodQuery query) const case Qt::ImPreferredLanguage: if (d->extra.isAllocated() && d->extra->keyHandler) v = d->extra->keyHandler->inputMethodQuery(query); + break; + case Qt::ImEnterKeyType: + if (d->extra.isAllocated() && d->extra->enterKeyAttached) + v = d->extra->enterKeyAttached->type(); + break; default: break; } @@ -4102,7 +4186,7 @@ void QQuickItem::polish() if (d->window) { QQuickWindowPrivate *p = QQuickWindowPrivate::get(d->window); bool maybeupdate = p->itemsToPolish.isEmpty(); - p->itemsToPolish.insert(this); + p->itemsToPolish.append(this); if (maybeupdate) d->window->maybeUpdate(); } } @@ -7839,6 +7923,7 @@ void QQuickItemLayer::updateMatrix() QQuickItemPrivate::ExtraData::ExtraData() : z(0), scale(1), rotation(0), opacity(1), contents(0), screenAttached(0), layoutDirectionAttached(0), + enterKeyAttached(0), keyHandler(0), layer(0), effectRefCount(0), hideRefCount(0), opacityNode(0), clipNode(0), rootNode(0), diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 463113386b..d92910ce9c 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -141,7 +141,6 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickItemLayer *layer READ layer DESIGNABLE false CONSTANT FINAL) - Q_ENUMS(TransformOrigin) Q_CLASSINFO("DefaultProperty", "data") Q_CLASSINFO("qt_HasQmlAccessors", "true") @@ -187,6 +186,7 @@ public: Left, Center, Right, BottomLeft, Bottom, BottomRight }; + Q_ENUM(TransformOrigin) QQuickItem(QQuickItem *parent = 0); virtual ~QQuickItem(); diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 64d8bd0ede..5e0246c32e 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -78,6 +78,7 @@ QT_BEGIN_NAMESPACE class QNetworkReply; class QQuickItemKeyFilter; class QQuickLayoutMirroringAttached; +class QQuickEnterKeyAttached; class QQuickScreenAttached; class QQuickContents : public QQuickItemChangeListener @@ -338,6 +339,7 @@ public: QQuickContents *contents; QQuickScreenAttached *screenAttached; QQuickLayoutMirroringAttached* layoutDirectionAttached; + QQuickEnterKeyAttached *enterKeyAttached; QQuickItemKeyFilter *keyHandler; mutable QQuickItemLayer *layer; #ifndef QT_NO_CURSOR @@ -645,8 +647,6 @@ class Q_QUICK_PRIVATE_EXPORT QQuickKeyNavigationAttached : public QObject, publi Q_PROPERTY(QQuickItem *backtab READ backtab WRITE setBacktab NOTIFY backtabChanged) Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) - Q_ENUMS(Priority) - public: QQuickKeyNavigationAttached(QObject * = 0); @@ -664,6 +664,7 @@ public: void setBacktab(QQuickItem *); enum Priority { BeforeItem, AfterItem }; + Q_ENUM(Priority) Priority priority() const; void setPriority(Priority); @@ -710,6 +711,27 @@ private: QQuickItemPrivate *itemPrivate; }; +class QQuickEnterKeyAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(Qt::EnterKeyType type READ type WRITE setType NOTIFY typeChanged) + +public: + explicit QQuickEnterKeyAttached(QObject *parent = Q_NULLPTR); + + Qt::EnterKeyType type() const; + void setType(Qt::EnterKeyType type); + + static QQuickEnterKeyAttached *qmlAttachedProperties(QObject *); +Q_SIGNALS: + void typeChanged(); +private: + friend class QQuickItemPrivate; + QQuickItemPrivate *itemPrivate; + + Qt::EnterKeyType keyType; +}; + class QQuickKeysAttachedPrivate : public QObjectPrivate { public: @@ -739,8 +761,6 @@ class QQuickKeysAttached : public QObject, public QQuickItemKeyFilter Q_PROPERTY(QQmlListProperty<QQuickItem> forwardTo READ forwardTo) Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) - Q_ENUMS(Priority) - public: QQuickKeysAttached(QObject *parent=0); ~QQuickKeysAttached(); @@ -755,6 +775,7 @@ public: } enum Priority { BeforeItem, AfterItem}; + Q_ENUM(Priority) Priority priority() const; void setPriority(Priority); @@ -895,5 +916,7 @@ QML_DECLARE_TYPE(QQuickKeyNavigationAttached) QML_DECLARE_TYPEINFO(QQuickKeyNavigationAttached, QML_HAS_ATTACHED_PROPERTIES) QML_DECLARE_TYPE(QQuickLayoutMirroringAttached) QML_DECLARE_TYPEINFO(QQuickLayoutMirroringAttached, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QQuickEnterKeyAttached) +QML_DECLARE_TYPEINFO(QQuickEnterKeyAttached, QML_HAS_ATTACHED_PROPERTIES) #endif // QQUICKITEM_P_H diff --git a/src/quick/items/qquickitemanimation_p.h b/src/quick/items/qquickitemanimation_p.h index 907687a2bd..9f0b3dccb8 100644 --- a/src/quick/items/qquickitemanimation_p.h +++ b/src/quick/items/qquickitemanimation_p.h @@ -136,7 +136,7 @@ public: BottomFirst, TopFirst }; - Q_ENUMS(Orientation) + Q_ENUM(Orientation) int duration() const; void setDuration(int); diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 5fbae66b6c..4df1ef038c 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -158,11 +158,6 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickPathView>(uri,major,minor,"PathView"); qmlRegisterUncreatableType<QQuickBasePositioner>(uri,major,minor,"Positioner", QStringLiteral("Positioner is an abstract type that is only available as an attached property.")); -#ifndef QT_NO_VALIDATOR - qmlRegisterType<QQuickIntValidator>(uri,major,minor,"IntValidator"); - qmlRegisterType<QQuickDoubleValidator>(uri,major,minor,"DoubleValidator"); - qmlRegisterType<QRegExpValidator>(uri,major,minor,"RegExpValidator"); -#endif qmlRegisterType<QQuickRectangle>(uri,major,minor,"Rectangle"); qmlRegisterType<QQuickRepeater>(uri,major,minor,"Repeater"); qmlRegisterType<QQuickRow>(uri,major,minor,"Row"); @@ -190,9 +185,6 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickCurve>(); qmlRegisterType<QQuickScaleGrid>(); qmlRegisterType<QQuickTextLine>(); -#ifndef QT_NO_VALIDATOR - qmlRegisterType<QValidator>(); -#endif qmlRegisterType<QQuickPen>(); qmlRegisterType<QQuickFlickableVisibleArea>(); qRegisterMetaType<QQuickAnchorLine>("QQuickAnchorLine"); @@ -269,6 +261,18 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickPinchArea, 1>(uri, 2, 5,"PinchArea"); qmlRegisterType<QQuickImage, 2>(uri, 2, 5,"Image"); qmlRegisterType<QQuickMouseArea, 2>(uri, 2, 5, "MouseArea"); + + qmlRegisterType<QQuickText, 6>(uri, 2, 6, "Text"); + qmlRegisterType<QQuickTextEdit, 6>(uri, 2, 6, "TextEdit"); + qmlRegisterType<QQuickTextInput, 6>(uri, 2, 6, "TextInput"); + qmlRegisterUncreatableType<QQuickBasePositioner, 6>(uri, 2, 6, "Positioner", + QStringLiteral("Positioner is an abstract type that is only available as an attached property.")); + qmlRegisterType<QQuickColumn, 6>(uri, 2, 6, "Column"); + qmlRegisterType<QQuickRow, 6>(uri, 2, 6, "Row"); + qmlRegisterType<QQuickGrid, 6>(uri, 2, 6, "Grid"); + qmlRegisterType<QQuickFlow, 6>(uri, 2, 6, "Flow"); + qmlRegisterUncreatableType<QQuickEnterKeyAttached, 6>(uri, 2, 6, "EnterKey", + QQuickEnterKeyAttached::tr("EnterKey is only available via attached properties")); } static void initResources() diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 01ef1e65f7..d4c8c3f8ee 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -71,19 +71,19 @@ FxViewItem::~FxViewItem() qreal FxViewItem::itemX() const { - return transitionableItem ? transitionableItem->itemX() : item->x(); + return transitionableItem ? transitionableItem->itemX() : (item ? item->x() : 0); } qreal FxViewItem::itemY() const { - return transitionableItem ? transitionableItem->itemY() : item->y(); + return transitionableItem ? transitionableItem->itemY() : (item ? item->y() : 0); } void FxViewItem::moveTo(const QPointF &pos, bool immediate) { if (transitionableItem) transitionableItem->moveTo(pos, immediate); - else + else if (item) item->setPosition(pos); } @@ -91,21 +91,26 @@ void FxViewItem::setVisible(bool visible) { if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning()) return; - QQuickItemPrivate::get(item)->setCulled(!visible); + if (item) + QQuickItemPrivate::get(item)->setCulled(!visible); } void FxViewItem::trackGeometry(bool track) { if (track) { if (!trackGeom) { - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry); + if (item) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry); + } trackGeom = true; } } else { if (trackGeom) { - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry); + if (item) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry); + } trackGeom = false; } } @@ -905,11 +910,7 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode) qreal pos = isContentFlowReversed() ? -position() - size() : position(); FxViewItem *item = visibleItem(idx); - qreal maxExtent; - if (layoutOrientation() == Qt::Vertical) - maxExtent = isContentFlowReversed() ? q->minYExtent()-size(): -q->maxYExtent(); - else - maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent(); + qreal maxExtent = calculatedMaxExtent(); if (!item) { qreal itemPos = positionAt(idx); changedVisibleIndex(idx); @@ -955,11 +956,7 @@ void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode) break; } pos = qMin(pos, maxExtent); - qreal minExtent; - if (layoutOrientation() == Qt::Vertical) - minExtent = isContentFlowReversed() ? q->maxYExtent()-size(): -q->minYExtent(); - else - minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent(); + qreal minExtent = calculatedMinExtent(); pos = qMax(pos, minExtent); moveReason = QQuickItemViewPrivate::Other; q->cancelFlick(); @@ -1130,6 +1127,29 @@ qreal QQuickItemViewPrivate::maxExtentForAxis(const AxisData &axisData, bool for return extent; } +qreal QQuickItemViewPrivate::calculatedMinExtent() const +{ + Q_Q(const QQuickItemView); + qreal minExtent; + if (layoutOrientation() == Qt::Vertical) + minExtent = isContentFlowReversed() ? q->maxYExtent() - size(): -q->minYExtent(); + else + minExtent = isContentFlowReversed() ? q->maxXExtent() - size(): -q->minXExtent(); + return minExtent; + +} + +qreal QQuickItemViewPrivate::calculatedMaxExtent() const +{ + Q_Q(const QQuickItemView); + qreal maxExtent; + if (layoutOrientation() == Qt::Vertical) + maxExtent = isContentFlowReversed() ? q->minYExtent() - size(): -q->maxYExtent(); + else + maxExtent = isContentFlowReversed() ? q->minXExtent() - size(): -q->maxXExtent(); + return maxExtent; +} + // for debugging only void QQuickItemViewPrivate::checkVisible() const { @@ -1199,7 +1219,7 @@ void QQuickItemView::destroyRemoved() for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin(); it != d->visibleItems.end();) { FxViewItem *item = *it; - if (item->index == -1 && item->attached->delayRemove() == false) { + if (item->index == -1 && (!item->attached || item->attached->delayRemove() == false)) { if (d->transitioner && d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)) { // don't remove from visibleItems until next layout() d->runDelayedRemoveTransition = true; @@ -1277,10 +1297,12 @@ void QQuickItemView::trackedPositionChanged() if (trackedPos < pos + d->highlightRangeStart) pos = trackedPos - d->highlightRangeStart; if (d->highlightRange != StrictlyEnforceRange) { - if (pos > d->endPosition() - d->size()) - pos = d->endPosition() - d->size(); - if (pos < d->startPosition()) - pos = d->startPosition(); + qreal maxExtent = d->calculatedMaxExtent(); + if (pos > maxExtent) + pos = maxExtent; + qreal minExtent = d->calculatedMinExtent(); + if (pos < minExtent) + pos = minExtent; } } else { if (d->trackedItem != d->currentItem) { @@ -1344,7 +1366,7 @@ void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &ol { Q_D(QQuickItemView); d->markExtentsDirty(); - if (isComponentComplete() && d->isValid()) + if (isComponentComplete() && (d->isValid() || !d->visibleItems.isEmpty())) d->forceLayoutPolish(); QQuickFlickable::geometryChanged(newGeometry, oldGeometry); } @@ -1641,7 +1663,8 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex) applyPendingChanges(); if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { if (currentItem) { - currentItem->attached->setIsCurrentItem(false); + if (currentItem->attached) + currentItem->attached->setIsCurrentItem(false); releaseItem(currentItem); currentItem = 0; currentIndex = modelIndex; @@ -1664,11 +1687,12 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex) int oldCurrentIndex = currentIndex; currentIndex = modelIndex; currentItem = createItem(modelIndex, false); - if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) + if (oldCurrentItem && oldCurrentItem->attached && (!currentItem || oldCurrentItem->item != currentItem->item)) oldCurrentItem->attached->setIsCurrentItem(false); if (currentItem) { currentItem->item->setFocus(true); - currentItem->attached->setIsCurrentItem(true); + if (currentItem->attached) + currentItem->attached->setIsCurrentItem(true); initializeCurrentItem(); } @@ -1806,7 +1830,7 @@ void QQuickItemViewPrivate::updateViewport() { Q_Q(QQuickItemView); qreal extra = headerSize() + footerSize(); - qreal contentSize = isValid() ? (endPosition() - startPosition()) : 0.0; + qreal contentSize = isValid() || !visibleItems.isEmpty() ? (endPosition() - startPosition()) : 0.0; if (layoutOrientation() == Qt::Vertical) q->setContentHeight(contentSize + extra); else @@ -1824,6 +1848,7 @@ void QQuickItemViewPrivate::layout() if (!isValid() && !visibleItems.count()) { clear(); setPosition(contentStartOffset()); + updateViewport(); if (transitioner) transitioner->setPopulateTransitionEnabled(false); inLayout = false; @@ -1967,7 +1992,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult QQmlChangeSet::Change removal; for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) { FxViewItem *item = *it; - if (item->index == -1 && !item->attached->delayRemove()) { + if (item->index == -1 && (!item->attached || !item->attached->delayRemove())) { removeItem(item, removal, &removalResult); removedCount++; it = visibleItems.erase(it); @@ -2007,8 +2032,10 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult } itemCount += insertions[i].count; } - for (int i=0; i<newItems.count(); i++) - newItems.at(i)->attached->emitAdd(); + for (int i=0; i<newItems.count(); i++) { + if (newItems.at(i)->attached) + newItems.at(i)->attached->emitAdd(); + } // for each item that was moved directly into the view as a result of a move(), // find the index it was moved from in order to set its initial position, so that we @@ -2040,7 +2067,8 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult if (currentChanges.currentChanged) { if (currentChanges.currentRemoved && currentItem) { - currentItem->attached->setIsCurrentItem(false); + if (currentItem->item && currentItem->attached) + currentItem->attached->setIsCurrentItem(false); releaseItem(currentItem); currentItem = 0; } @@ -2092,10 +2120,10 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &remo } else { // removed item visibleAffected = true; - if (!removal.isMove()) + if (!removal.isMove() && item->item && item->attached) item->attached->emitRemove(); - if (item->attached->delayRemove() && !removal.isMove()) { + if (item->item && item->attached && item->attached->delayRemove() && !removal.isMove()) { item->index = -1; QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection); ++it; @@ -2352,12 +2380,14 @@ bool QQuickItemViewPrivate::releaseItem(FxViewItem *item) item->trackGeometry(false); QQmlInstanceModel::ReleaseFlags flags = model->release(item->item); - if (flags == 0) { - // item was not destroyed, and we no longer reference it. - QQuickItemPrivate::get(item->item)->setCulled(true); - unrequestedItems.insert(item->item, model->indexOf(item->item, q)); - } else if (flags & QQmlInstanceModel::Destroyed) { - item->item->setParentItem(0); + if (item->item) { + if (flags == 0) { + // item was not destroyed, and we no longer reference it. + QQuickItemPrivate::get(item->item)->setCulled(true); + unrequestedItems.insert(item->item, model->indexOf(item->item, q)); + } else if (flags & QQmlInstanceModel::Destroyed) { + item->item->setParentItem(0); + } } delete item; return flags != QQmlInstanceModel::Referenced; @@ -2421,7 +2451,7 @@ void QQuickItemViewPrivate::updateUnrequestedIndexes() void QQuickItemViewPrivate::updateUnrequestedPositions() { - for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.begin(), cend = unrequestedItems.end(); it != cend; ++it) + for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.cbegin(), cend = unrequestedItems.cend(); it != cend; ++it) repositionPackageItemAt(it.key(), it.value()); } diff --git a/src/quick/items/qquickitemview_p.h b/src/quick/items/qquickitemview_p.h index 6e5ae032c2..3e28ff336d 100644 --- a/src/quick/items/qquickitemview_p.h +++ b/src/quick/items/qquickitemview_p.h @@ -88,11 +88,6 @@ class Q_AUTOTEST_EXPORT QQuickItemView : public QQuickFlickable Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd) Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged) - Q_ENUMS(HighlightRangeMode) - Q_ENUMS(PositionMode) - Q_ENUMS(VerticalLayoutDirection) - Q_ENUMS(LayoutDirection) - public: // this holds all layout enum values so they can be referred to by other enums // to ensure consistent values - e.g. QML references to GridView.TopToBottom flow @@ -103,11 +98,13 @@ public: VerticalTopToBottom, VerticalBottomToTop }; + Q_ENUM(LayoutDirection) enum VerticalLayoutDirection { TopToBottom = VerticalTopToBottom, BottomToTop = VerticalBottomToTop }; + Q_ENUM(VerticalLayoutDirection) QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent = 0); ~QQuickItemView(); @@ -185,6 +182,7 @@ public: virtual void setHighlightFollowsCurrentItem(bool); enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; + Q_ENUM(HighlightRangeMode) HighlightRangeMode highlightRangeMode() const; void setHighlightRangeMode(HighlightRangeMode mode); @@ -200,6 +198,7 @@ public: virtual void setHighlightMoveDuration(int); enum PositionMode { Beginning, Center, End, Visible, Contain, SnapPosition }; + Q_ENUM(PositionMode) Q_INVOKABLE void positionViewAtIndex(int index, int mode); Q_INVOKABLE int indexAt(qreal x, qreal y) const; diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index a8352b229e..0931db4e56 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -53,6 +53,8 @@ public: qreal itemX() const; qreal itemY() const; + inline qreal itemWidth() const { return item ? item->width() : 0; } + inline qreal itemHeight() const { return item ? item->height() : 0; } void moveTo(const QPointF &pos, bool immediate); void setVisible(bool visible); @@ -75,7 +77,7 @@ public: virtual bool contains(qreal x, qreal y) const = 0; - QQuickItem *item; + QPointer<QQuickItem> item; QQuickItemView *view; QQuickItemViewTransitionableItem *transitionableItem; QQuickItemViewAttached *attached; @@ -198,6 +200,8 @@ public: qreal minExtentForAxis(const AxisData &axisData, bool forXAxis) const; qreal maxExtentForAxis(const AxisData &axisData, bool forXAxis) const; + qreal calculatedMinExtent() const; + qreal calculatedMaxExtent() const; void applyPendingChanges(); bool applyModelChanges(ChangeResult *insertionResult, ChangeResult *removalResult); diff --git a/src/quick/items/qquickitemviewtransition.cpp b/src/quick/items/qquickitemviewtransition.cpp index 7fa6cdc161..6fa299bf03 100644 --- a/src/quick/items/qquickitemviewtransition.cpp +++ b/src/quick/items/qquickitemviewtransition.cpp @@ -34,6 +34,7 @@ #include "qquickitemviewtransition_p.h" #include <QtQuick/qquickitem.h> #include <QtQuick/private/qquicktransition_p.h> +#include <QtQuick/private/qquicktransitionmanager_p_p.h> QT_BEGIN_NAMESPACE diff --git a/src/quick/items/qquickitemviewtransition_p.h b/src/quick/items/qquickitemviewtransition_p.h index 170072a814..6f93697cc5 100644 --- a/src/quick/items/qquickitemviewtransition_p.h +++ b/src/quick/items/qquickitemviewtransition_p.h @@ -34,16 +34,20 @@ #ifndef QQUICKITEMVIEWTRANSITION_P_P_H #define QQUICKITEMVIEWTRANSITION_P_P_H -#include <private/qquicktransitionmanager_p_p.h> +#include <QtQuick/private/qtquickglobal_p.h> +#include <QtCore/qobject.h> +#include <QtCore/qpoint.h> +#include <QtQml/qqml.h> QT_BEGIN_NAMESPACE class QQuickItem; +class QQuickTransition; class QQuickItemViewTransitionableItem; class QQuickItemViewTransitionJob; -class QQuickItemViewTransitionChangeListener +class Q_QUICK_PRIVATE_EXPORT QQuickItemViewTransitionChangeListener { public: QQuickItemViewTransitionChangeListener() {} @@ -53,7 +57,7 @@ public: }; -class QQuickItemViewTransitioner +class Q_QUICK_PRIVATE_EXPORT QQuickItemViewTransitioner { public: enum TransitionType { @@ -113,7 +117,7 @@ private: /* An item that can be transitioned using QQuickViewTransitionJob. */ -class QQuickItemViewTransitionableItem +class Q_QUICK_PRIVATE_EXPORT QQuickItemViewTransitionableItem { public: QQuickItemViewTransitionableItem(QQuickItem *i); diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 20b6dd5b36..a1d765d6ec 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -267,18 +267,18 @@ public: } qreal itemPosition() const { if (view->orientation() == QQuickListView::Vertical) - return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -item->height()-itemY() : itemY()); + return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -itemHeight()-itemY() : itemY()); else - return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX()); + return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -itemWidth()-itemX() : itemX()); } qreal size() const Q_DECL_OVERRIDE { if (section()) - return (view->orientation() == QQuickListView::Vertical ? item->height()+section()->height() : item->width()+section()->width()); + return (view->orientation() == QQuickListView::Vertical ? itemHeight()+section()->height() : itemWidth()+section()->width()); else - return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width()); + return (view->orientation() == QQuickListView::Vertical ? itemHeight() : itemWidth()); } qreal itemSize() const { - return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width()); + return (view->orientation() == QQuickListView::Vertical ? itemHeight() : itemWidth()); } qreal sectionSize() const Q_DECL_OVERRIDE { if (section()) @@ -289,11 +289,11 @@ public: if (view->orientation() == QQuickListView::Vertical) { return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -itemY() - : itemY() + item->height()); + : itemY() + itemHeight()); } else { return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -itemX() - : itemX() + item->width()); + : itemX() + itemWidth()); } } void setPosition(qreal pos, bool immediate = false) { @@ -320,8 +320,8 @@ public: item->setWidth(size); } bool contains(qreal x, qreal y) const Q_DECL_OVERRIDE { - return (x >= itemX() && x < itemX() + item->width() && - y >= itemY() && y < itemY() + item->height()); + return (x >= itemX() && x < itemX() + itemWidth() && + y >= itemY() && y < itemY() + itemHeight()); } QQuickListView *view; @@ -332,7 +332,7 @@ private: if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) { if (section()) pos += section()->height(); - return QPointF(itemX(), -item->height() - pos); + return QPointF(itemX(), -itemHeight() - pos); } else { if (section()) pos += section()->height(); @@ -342,7 +342,7 @@ private: if (view->effectiveLayoutDirection() == Qt::RightToLeft) { if (section()) pos += section()->width(); - return QPointF(-item->width() - pos, itemY()); + return QPointF(-itemWidth() - pos, itemY()); } else { if (section()) pos += section()->width(); @@ -427,14 +427,24 @@ qreal QQuickListViewPrivate::lastPosition() const { qreal pos = 0; if (!visibleItems.isEmpty()) { - int invisibleCount = visibleItems.count() - visibleIndex; + int invisibleCount = INT_MIN; + int delayRemovedCount = 0; for (int i = visibleItems.count()-1; i >= 0; --i) { if (visibleItems.at(i)->index != -1) { - invisibleCount = model->count() - visibleItems.at(i)->index - 1; + // Find the invisible count after the last visible item with known index + invisibleCount = model->count() - (visibleItems.at(i)->index + 1 + delayRemovedCount); break; + } else if (visibleItems.at(i)->attached->delayRemove()) { + ++delayRemovedCount; } } - pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing); + if (invisibleCount == INT_MIN) { + // All visible items are in delayRemove state + invisibleCount = model->count(); + } + pos = (*(--visibleItems.constEnd()))->endPosition(); + if (invisibleCount > 0) + pos += invisibleCount * (averageSize + spacing); } else if (model && model->count()) { pos = (model->count() * averageSize + (model->count()-1) * spacing); } @@ -599,10 +609,11 @@ bool QQuickListViewPrivate::releaseItem(FxViewItem *item) if (!item || !model) return true; + QPointer<QQuickItem> it = item->item; QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached); bool released = QQuickItemViewPrivate::releaseItem(item); - if (released && att && att->m_sectionItem) { + if (released && it && att && att->m_sectionItem) { // We hold no more references to this item int i = 0; do { @@ -657,10 +668,11 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal while (modelIndex < model->count() && pos <= fillTo) { if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer)))) break; - qCDebug(lcItemViewDelegateLifecycle) << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer << "item" << item->item->objectName(); + qCDebug(lcItemViewDelegateLifecycle) << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer << "item" << (QObject *)(item->item); if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems() item->setPosition(pos, true); - QQuickItemPrivate::get(item->item)->setCulled(doBuffer); + if (item->item) + QQuickItemPrivate::get(item->item)->setCulled(doBuffer); pos += item->size() + spacing; visibleItems.append(item); ++modelIndex; @@ -673,12 +685,13 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) { if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer)))) break; - qCDebug(lcItemViewDelegateLifecycle) << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer << "item" << item->item->objectName(); + qCDebug(lcItemViewDelegateLifecycle) << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer << "item" << (QObject *)(item->item); --visibleIndex; visiblePos -= item->size() + spacing; if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems() item->setPosition(visiblePos, true); - QQuickItemPrivate::get(item->item)->setCulled(doBuffer); + if (item->item) + QQuickItemPrivate::get(item->item)->setCulled(doBuffer); visibleItems.prepend(item); changed = true; } @@ -709,7 +722,7 @@ bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer visibleIndex++; visibleItems.removeAt(index); if (item->transitionScheduledOrRunning()) { - qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item" << item->index << item->item->objectName(); + qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item" << item->index << (QObject *)(item->item); item->releaseAfterTransition = true; releasePendingTransition.append(item); } else { @@ -728,10 +741,10 @@ bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) { if (item->attached->delayRemove()) break; - qCDebug(lcItemViewDelegateLifecycle) << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position() << item->item->objectName(); + qCDebug(lcItemViewDelegateLifecycle) << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position() << (QObject *)(item->item); visibleItems.removeLast(); if (item->transitionScheduledOrRunning()) { - qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item" << item->index << item->item->objectName(); + qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item" << item->index << (QObject *)(item->item); item->releaseAfterTransition = true; releasePendingTransition.append(item); } else { @@ -1457,13 +1470,13 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte tempPosition -= bias; } FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart); - if (!topItem && strictHighlightRange && currentItem) { + if (strictHighlightRange && currentItem) { // StrictlyEnforceRange always keeps an item in range updateHighlight(); topItem = currentItem; } FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd); - if (!bottomItem && strictHighlightRange && currentItem) { + if (strictHighlightRange && currentItem) { // StrictlyEnforceRange always keeps an item in range updateHighlight(); bottomItem = currentItem; @@ -2844,7 +2857,8 @@ void QQuickListView::viewportMoved(Qt::Orientations orient) qreal to = d->isContentFlowReversed() ? -d->position()+d->displayMarginEnd : d->position()+d->size()+d->displayMarginEnd; for (int i = 0; i < d->visibleItems.count(); ++i) { FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i)); - QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to); + if (item->item) + QQuickItemPrivate::get(item->item)->setCulled(item->endPosition() < from || item->position() > to); } if (d->currentItem) QQuickItemPrivate::get(d->currentItem->item)->setCulled(d->currentItem->endPosition() < from || d->currentItem->position() > to); diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h index bcb4e18751..7f64e12bba 100644 --- a/src/quick/items/qquicklistview_p.h +++ b/src/quick/items/qquicklistview_p.h @@ -47,8 +47,6 @@ class Q_AUTOTEST_EXPORT QQuickViewSection : public QObject Q_PROPERTY(SectionCriteria criteria READ criteria WRITE setCriteria NOTIFY criteriaChanged) Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) Q_PROPERTY(int labelPositioning READ labelPositioning WRITE setLabelPositioning NOTIFY labelPositioningChanged) - Q_ENUMS(SectionCriteria) - Q_ENUMS(LabelPositioning) public: QQuickViewSection(QQuickListView *parent=0); @@ -56,6 +54,7 @@ public: void setProperty(const QString &); enum SectionCriteria { FullString, FirstCharacter }; + Q_ENUM(SectionCriteria) SectionCriteria criteria() const { return m_criteria; } void setCriteria(SectionCriteria); @@ -65,6 +64,7 @@ public: QString sectionString(const QString &value); enum LabelPositioning { InlineLabels = 0x01, CurrentLabelAtStart = 0x02, NextLabelAtEnd = 0x04 }; + Q_ENUM(LabelPositioning) int labelPositioning() { return m_labelPositioning; } void setLabelPositioning(int pos); @@ -106,10 +106,6 @@ class Q_AUTOTEST_EXPORT QQuickListView : public QQuickItemView Q_PROPERTY(HeaderPositioning headerPositioning READ headerPositioning WRITE setHeaderPositioning NOTIFY headerPositioningChanged REVISION 2) Q_PROPERTY(FooterPositioning footerPositioning READ footerPositioning WRITE setFooterPositioning NOTIFY footerPositioningChanged REVISION 2) - Q_ENUMS(Orientation) - Q_ENUMS(SnapMode) - Q_ENUMS(HeaderPositioning) - Q_ENUMS(FooterPositioning) Q_CLASSINFO("DefaultProperty", "data") public: @@ -120,6 +116,7 @@ public: void setSpacing(qreal spacing); enum Orientation { Horizontal = Qt::Horizontal, Vertical = Qt::Vertical }; + Q_ENUM(Orientation) Orientation orientation() const; void setOrientation(Orientation); @@ -140,14 +137,17 @@ public: void setHighlightMoveDuration(int) Q_DECL_OVERRIDE; enum SnapMode { NoSnap, SnapToItem, SnapOneItem }; + Q_ENUM(SnapMode) SnapMode snapMode() const; void setSnapMode(SnapMode mode); enum HeaderPositioning { InlineHeader, OverlayHeader, PullBackHeader }; + Q_ENUM(HeaderPositioning) HeaderPositioning headerPositioning() const; void setHeaderPositioning(HeaderPositioning positioning); enum FooterPositioning { InlineFooter, OverlayFooter, PullBackFooter }; + Q_ENUM(FooterPositioning) FooterPositioning footerPositioning() const; void setFooterPositioning(FooterPositioning positioning); diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index dd04568db2..d46e25d255 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -576,7 +576,6 @@ void QQuickLoader::setSource(QQmlV4Function *args) if (!ipv->isUndefined()) { d->disposeInitialPropertyValues(); d->initialPropertyValues.set(args->v4engine(), ipv); - d->qmlGlobalForIpv.set(args->v4engine(), args->qmlGlobal()); } setSource(sourceUrl, false); // already cleared and set ipv above. @@ -642,11 +641,11 @@ void QQuickLoaderPrivate::setInitialState(QObject *obj) QQmlComponentPrivate *d = QQmlComponentPrivate::get(component); Q_ASSERT(d && d->engine); - QV4::ExecutionEngine *v4 = qmlGlobalForIpv.engine(); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(d->engine); Q_ASSERT(v4); QV4::Scope scope(v4); QV4::ScopedValue ipv(scope, initialPropertyValues.value()); - d->initializeObjectWithInitialProperties(*qmlGlobalForIpv.valueRef(), ipv, obj); + d->initializeObjectWithInitialProperties(ipv, obj); } void QQuickLoaderIncubator::statusChanged(Status status) @@ -943,7 +942,7 @@ QV4::ReturnedValue QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV4Funct QV4::ScopedValue valuemap(scope, QV4::Primitive::undefinedValue()); if (args->length() >= 2) { QV4::ScopedValue v(scope, (*args)[1]); - if (!v->isObject() || v->asArrayObject()) { + if (!v->isObject() || v->as<QV4::ArrayObject>()) { *error = true; qmlInfo(loader) << QQuickLoader::tr("setSource: value is not an object"); } else { diff --git a/src/quick/items/qquickloader_p.h b/src/quick/items/qquickloader_p.h index 2c0e98de59..6ed4f2437b 100644 --- a/src/quick/items/qquickloader_p.h +++ b/src/quick/items/qquickloader_p.h @@ -42,7 +42,6 @@ class QQuickLoaderPrivate; class Q_AUTOTEST_EXPORT QQuickLoader : public QQuickImplicitSizeItem { Q_OBJECT - Q_ENUMS(Status) Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) @@ -69,6 +68,7 @@ public: void resetSourceComponent(); enum Status { Null, Ready, Loading, Error }; + Q_ENUM(Status) Status status() const; qreal progress() const; diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h index 621419d1a7..9677318b58 100644 --- a/src/quick/items/qquickloader_p_p.h +++ b/src/quick/items/qquickloader_p_p.h @@ -50,7 +50,7 @@ #include "qquickitemchangelistener_p.h" #include <qqmlincubator.h> -#include <private/qv4value_inl_p.h> +#include <private/qv4value_p.h> QT_BEGIN_NAMESPACE @@ -102,7 +102,6 @@ public: QQmlContext *itemContext; QQuickLoaderIncubator *incubator; QV4::PersistentValue initialPropertyValues; - QV4::PersistentValue qmlGlobalForIpv; bool updatingSize: 1; bool active : 1; bool loadingFromSource : 1; diff --git a/src/quick/items/qquickopenglinfo_p.h b/src/quick/items/qquickopenglinfo_p.h index 2a2e2a719b..511413c814 100644 --- a/src/quick/items/qquickopenglinfo_p.h +++ b/src/quick/items/qquickopenglinfo_p.h @@ -63,7 +63,6 @@ class QQuickOpenGLInfo : public QObject Q_PROPERTY(int minorVersion READ minorVersion NOTIFY minorVersionChanged FINAL) Q_PROPERTY(ContextProfile profile READ profile NOTIFY profileChanged FINAL) Q_PROPERTY(RenderableType renderableType READ renderableType NOTIFY renderableTypeChanged FINAL) - Q_ENUMS(ContextProfile RenderableType) public: QQuickOpenGLInfo(QQuickItem *item = 0); @@ -77,6 +76,7 @@ public: CoreProfile = QSurfaceFormat::CoreProfile, CompatibilityProfile = QSurfaceFormat::CompatibilityProfile }; + Q_ENUM(ContextProfile) ContextProfile profile() const; // keep in sync with QSurfaceFormat::RenderableType @@ -85,6 +85,7 @@ public: OpenGL = QSurfaceFormat::OpenGL, OpenGLES = QSurfaceFormat::OpenGLES }; + Q_ENUM(RenderableType) RenderableType renderableType() const; static QQuickOpenGLInfo *qmlAttachedProperties(QObject *object); diff --git a/src/quick/items/qquickpainteditem.h b/src/quick/items/qquickpainteditem.h index 356e4a46f6..28eb3398a0 100644 --- a/src/quick/items/qquickpainteditem.h +++ b/src/quick/items/qquickpainteditem.h @@ -43,7 +43,6 @@ class QQuickPaintedItemPrivate; class Q_QUICK_EXPORT QQuickPaintedItem : public QQuickItem { Q_OBJECT - Q_ENUMS(RenderTarget) Q_PROPERTY(QSize contentsSize READ contentsSize WRITE setContentsSize NOTIFY contentsSizeChanged) Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) @@ -58,6 +57,7 @@ public: FramebufferObject, InvertedYFramebufferObject }; + Q_ENUM(RenderTarget) enum PerformanceHint { FastFBOResizing = 0x1 diff --git a/src/quick/items/qquickpainteditem_p.h b/src/quick/items/qquickpainteditem_p.h index 3712e964f8..2759d9d683 100644 --- a/src/quick/items/qquickpainteditem_p.h +++ b/src/quick/items/qquickpainteditem_p.h @@ -42,7 +42,7 @@ QT_BEGIN_NAMESPACE class QQuickPaintedItemTextureProvider; class QSGPainterNode; -class QQuickPaintedItemPrivate : public QQuickItemPrivate +class Q_QUICK_PRIVATE_EXPORT QQuickPaintedItemPrivate : public QQuickItemPrivate { public: QQuickPaintedItemPrivate(); diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 58605f79dd..302532c3d1 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -98,7 +98,7 @@ QQuickPathViewPrivate::QQuickPathViewPrivate() , inRefill(false) , dragMargin(0), deceleration(100), maximumFlickVelocity(QML_FLICK_DEFAULTMAXVELOCITY) , moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset), flickDuration(0) - , firstIndex(-1), pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0) + , pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0) , moveReason(Other), moveDirection(Shortest), attType(0), highlightComponent(0), highlightItem(0) , moveHighlight(this, &QQuickPathViewPrivate::setHighlightPosition) , highlightPosition(0) @@ -447,7 +447,6 @@ void QQuickPathViewPrivate::regenerate() if (!isValid()) return; - firstIndex = -1; updateMappedRange(); q->refill(); } @@ -1473,7 +1472,7 @@ int QQuickPathView::indexAt(qreal x, qreal y) const QQuickItem *item = d->items.at(idx); QPointF p = item->mapFromItem(this, QPointF(x, y)); if (item->contains(p)) - return (d->firstIndex + idx) % d->modelCount; + return d->model->indexOf(item, 0); } return -1; @@ -1896,12 +1895,12 @@ void QQuickPathView::refill() int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount); // first move existing items and remove items off path - int idx = d->firstIndex; - qCDebug(lcItemViewDelegateLifecycle) << "firstIndex" << idx << "currentIndex" << d->currentIndex << "offset" << d->offset; + qCDebug(lcItemViewDelegateLifecycle) << "currentIndex" << d->currentIndex << "offset" << d->offset; QList<QQuickItem*>::iterator it = d->items.begin(); while (it != d->items.end()) { - qreal pos = d->positionOfIndex(idx); QQuickItem *item = *it; + int idx = d->model->indexOf(item, 0); + qreal pos = d->positionOfIndex(idx); if (lcItemViewDelegateLifecycle().isDebugEnabled()) { QQuickText *text = qmlobject_cast<QQuickText*>(item); if (text) @@ -1923,81 +1922,140 @@ void QQuickPathView::refill() if (!d->isInBound(pos, d->mappedRange - d->mappedCache, 1.0 + d->mappedCache)) { qCDebug(lcItemViewDelegateLifecycle) << "release" << idx << "@" << pos << ", !isInBound: lower" << (d->mappedRange - d->mappedCache) << "upper" << (1.0 + d->mappedCache); d->releaseItem(item); - if (it == d->items.begin()) { - if (++d->firstIndex >= d->modelCount) { - d->firstIndex = 0; - } - } it = d->items.erase(it); } else { ++it; } } - ++idx; - if (idx >= d->modelCount) - idx = 0; } - if (!d->items.count()) - d->firstIndex = -1; - bool waiting = false; if (d->modelCount) { - // add items to beginning and end + // add items as needed if (d->items.count() < count+d->cacheSize) { - int idx = qRound(d->modelCount - d->offset) % d->modelCount; + int endIdx = 0; + qreal endPos; + int startIdx = 0; qreal startPos = 0.0; - if (d->haveHighlightRange && (d->highlightRangeMode != QQuickPathView::NoHighlightRange - || d->snapMode != QQuickPathView::NoSnap)) - startPos = d->highlightRangeStart; - if (d->firstIndex >= 0) { - startPos = d->positionOfIndex(d->firstIndex); - idx = (d->firstIndex + d->items.count()) % d->modelCount; + if (d->items.count()) { + //Find the beginning and end, items may not be in sorted order + endPos = -1.0; + startPos = 2.0; + + for (int i = 0; i < d->items.count(); i++) { + int idx = d->model->indexOf(d->items[i], 0); + qreal curPos = d->positionOfIndex(idx); + if (curPos > endPos) { + endPos = curPos; + endIdx = idx; + } + + if (curPos < startPos) { + startPos = curPos; + startIdx = idx; + } + } + } else { + if (d->haveHighlightRange + && (d->highlightRangeMode != QQuickPathView::NoHighlightRange + || d->snapMode != QQuickPathView::NoSnap)) + startPos = d->highlightRangeStart; + // With no items, then "end" is just off the top so we populate via append + endIdx = (qRound(d->modelCount - d->offset) - 1) % d->modelCount; + endPos = d->positionOfIndex(endIdx); } - qreal pos = d->positionOfIndex(idx); - while ((d->isInBound(pos, startPos, 1.0 + d->mappedCache) || !d->items.count()) && d->items.count() < count+d->cacheSize) { - qCDebug(lcItemViewDelegateLifecycle) << "append" << idx << "@" << pos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count(); - QQuickItem *item = d->getItem(idx, idx+1, pos >= 1.0); + //Append + int idx = endIdx + 1; + if (idx >= d->modelCount) + idx = 0; + qreal nextPos = d->positionOfIndex(idx); + while ((d->isInBound(nextPos, endPos, 1.0 + d->mappedCache) || !d->items.count()) + && d->items.count() < count+d->cacheSize) { + qCDebug(lcItemViewDelegateLifecycle) << "append" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count(); + QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0); if (!item) { waiting = true; break; } + if (d->items.contains(item)) { + break; //Otherwise we'd "re-add" it, and get confused + } if (d->currentIndex == idx) { currentVisible = true; - d->currentItemOffset = pos; + d->currentItemOffset = nextPos; } - if (d->items.count() == 0) - d->firstIndex = idx; d->items.append(item); - d->updateItem(item, pos); + d->updateItem(item, nextPos); + endIdx = idx; + endPos = nextPos; ++idx; if (idx >= d->modelCount) idx = 0; - pos = d->positionOfIndex(idx); + nextPos = d->positionOfIndex(idx); } - idx = d->firstIndex - 1; + //Prepend + idx = startIdx - 1; if (idx < 0) idx = d->modelCount - 1; - pos = d->positionOfIndex(idx); - while (!waiting && d->isInBound(pos, d->mappedRange - d->mappedCache, startPos) && d->items.count() < count+d->cacheSize) { - qCDebug(lcItemViewDelegateLifecycle) << "prepend" << idx << "@" << pos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count(); - QQuickItem *item = d->getItem(idx, idx+1, pos >= 1.0); + nextPos = d->positionOfIndex(idx); + while (!waiting && d->isInBound(nextPos, d->mappedRange - d->mappedCache, startPos) + && d->items.count() < count+d->cacheSize) { + qCDebug(lcItemViewDelegateLifecycle) << "prepend" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count(); + QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0); if (!item) { waiting = true; break; } + if (d->items.contains(item)) { + break; //Otherwise we'd "re-add" it, and get confused + } if (d->currentIndex == idx) { currentVisible = true; - d->currentItemOffset = pos; + d->currentItemOffset = nextPos; } d->items.prepend(item); - d->updateItem(item, pos); - d->firstIndex = idx; - idx = d->firstIndex - 1; + d->updateItem(item, nextPos); + startIdx = idx; + startPos = nextPos; + --idx; if (idx < 0) idx = d->modelCount - 1; - pos = d->positionOfIndex(idx); + nextPos = d->positionOfIndex(idx); + } + + // In rare cases, when jumping around with pathCount close to modelCount, + // new items appear in the middle. This more generic addition iteration handles this + // Since this is the rare case, we try append/prepend first and only do this if + // there are gaps still left to fill. + if (!waiting && d->items.count() < count+d->cacheSize) { + qCDebug(lcItemViewDelegateLifecycle) << "Checking for pathview middle inserts, items count was" << d->items.count(); + idx = startIdx; + QQuickItem *lastItem = d->items[0]; + while (idx != endIdx) { + //This gets the reference from the delegate model, and will not re-create + QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0); + if (!item) { + waiting = true; + break; + } + if (!d->items.contains(item)) { //We found a hole + nextPos = d->positionOfIndex(idx); + qCDebug(lcItemViewDelegateLifecycle) << "middle insert" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count(); + if (d->currentIndex == idx) { + currentVisible = true; + d->currentItemOffset = nextPos; + } + int lastListIdx = d->items.indexOf(lastItem); + d->items.insert(lastListIdx + 1, item); + d->updateItem(item, nextPos); + } + + lastItem = item; + ++idx; + if (idx >= d->modelCount) + idx = 0; + } } } } @@ -2128,7 +2186,6 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) d->offset = qmlMod(d->modelCount - d->currentIndex, d->modelCount); changedOffset = true; } - d->firstIndex = -1; d->updateMappedRange(); d->scheduleLayout(); } @@ -2185,8 +2242,16 @@ void QQuickPathViewPrivate::createCurrentItem() { if (requestedIndex != -1) return; - int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount; - if (itemIndex < items.count()) { + + bool inItems = false; + for (int i = 0; i < items.count(); i++) { + if (model->indexOf(items[i], 0) == currentIndex) { + inItems = true; + break; + } + } + + if (inItems) { if ((currentItem = getItem(currentIndex, currentIndex))) { currentItem->setFocus(true); if (QQuickPathViewAttached *att = attached(currentItem)) diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h index 8062e07795..0f2e4a956c 100644 --- a/src/quick/items/qquickpathview_p.h +++ b/src/quick/items/qquickpathview_p.h @@ -78,10 +78,6 @@ class Q_AUTOTEST_EXPORT QQuickPathView : public QQuickItem Q_PROPERTY(int cacheItemCount READ cacheItemCount WRITE setCacheItemCount NOTIFY cacheItemCountChanged) - Q_ENUMS(HighlightRangeMode) - Q_ENUMS(SnapMode) - Q_ENUMS(PositionMode) - public: QQuickPathView(QQuickItem *parent=0); virtual ~QQuickPathView(); @@ -105,6 +101,7 @@ public: QQuickItem *highlightItem(); enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange }; + Q_ENUM(HighlightRangeMode) HighlightRangeMode highlightRangeMode() const; void setHighlightRangeMode(HighlightRangeMode mode); @@ -146,10 +143,12 @@ public: void setCacheItemCount(int); enum SnapMode { NoSnap, SnapToItem, SnapOneItem }; + Q_ENUM(SnapMode) SnapMode snapMode() const; void setSnapMode(SnapMode mode); enum PositionMode { Beginning, Center, End, Contain=4, SnapPosition }; // 3 == Visible in other views + Q_ENUM(PositionMode) Q_INVOKABLE void positionViewAtIndex(int index, int mode); Q_INVOKABLE int indexAt(qreal x, qreal y) const; Q_INVOKABLE QQuickItem *itemAt(qreal x, qreal y) const; diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h index 35ea8616a5..2a497881d4 100644 --- a/src/quick/items/qquickpathview_p_p.h +++ b/src/quick/items/qquickpathview_p_p.h @@ -155,7 +155,6 @@ public: QQuickTimeLine tl; QQuickTimeLineValueProxy<QQuickPathViewPrivate> moveOffset; int flickDuration; - int firstIndex; int pathItems; int requestedIndex; int cacheSize; diff --git a/src/quick/items/qquickpincharea_p.h b/src/quick/items/qquickpincharea_p.h index d2de59b5d6..602b2804cc 100644 --- a/src/quick/items/qquickpincharea_p.h +++ b/src/quick/items/qquickpincharea_p.h @@ -42,7 +42,6 @@ class Q_AUTOTEST_EXPORT QQuickPinch : public QObject { Q_OBJECT - Q_ENUMS(Axis) Q_PROPERTY(QQuickItem *target READ target WRITE setTarget RESET resetTarget) Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged) Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged) @@ -103,6 +102,7 @@ public: } enum Axis { NoDrag=0x00, XAxis=0x01, YAxis=0x02, XAndYAxis=0x03, XandYAxis=XAndYAxis }; + Q_ENUM(Axis) Axis axis() const { return m_axis; } void setAxis(Axis a) { if (a == m_axis) diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp index 887d317069..de2596b679 100644 --- a/src/quick/items/qquickpositioners.cpp +++ b/src/quick/items/qquickpositioners.cpp @@ -70,6 +70,10 @@ QQuickBasePositioner::PositionedItem::PositionedItem(QQuickItem *i) , index(-1) , isNew(false) , isVisible(true) + , topPadding(0) + , leftPadding(0) + , rightPadding(0) + , bottomPadding(0) { } @@ -116,6 +120,13 @@ void QQuickBasePositioner::PositionedItem::startTransition(QQuickItemViewTransit transitionableItem->startTransition(transitioner, index); } +void QQuickBasePositioner::PositionedItem::updatePadding(qreal lp, qreal tp, qreal rp, qreal bp) +{ + leftPadding = lp; + topPadding = tp; + rightPadding = rp; + bottomPadding = bp; +} QQuickBasePositioner::QQuickBasePositioner(PositionerType at, QQuickItem *parent) : QQuickImplicitSizeItem(*(new QQuickBasePositionerPrivate), parent) @@ -388,11 +399,8 @@ void QQuickBasePositioner::prePositioning() void QQuickBasePositioner::positionItem(qreal x, qreal y, PositionedItem *target) { - Q_D(QQuickBasePositioner); - if ( (target->itemX() != x || target->itemY() != y) - && d->type == Both) { + if ( target->itemX() != x || target->itemY() != y ) target->moveTo(QPointF(x, y)); - } } void QQuickBasePositioner::positionItemX(qreal x, PositionedItem *target) @@ -503,6 +511,185 @@ void QQuickBasePositioner::updateAttachedProperties(QQuickPositionerAttached *sp } } +qreal QQuickBasePositioner::padding() const +{ + Q_D(const QQuickBasePositioner); + return d->padding(); +} + +void QQuickBasePositioner::setPadding(qreal padding) +{ + Q_D(QQuickBasePositioner); + if (qFuzzyCompare(d->padding(), padding)) + return; + + d->extra.value().padding = padding; + d->setPositioningDirty(); + emit paddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitTopPadding) + emit topPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding) + emit leftPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitRightPadding) + emit rightPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding) + emit bottomPaddingChanged(); +} + +void QQuickBasePositioner::resetPadding() +{ + setPadding(0); +} + +qreal QQuickBasePositioner::topPadding() const +{ + Q_D(const QQuickBasePositioner); + if (d->extra.isAllocated() && d->extra->explicitTopPadding) + return d->extra->topPadding; + return d->padding(); +} + +void QQuickBasePositioner::setTopPadding(qreal padding) +{ + Q_D(QQuickBasePositioner); + d->setTopPadding(padding); +} + +void QQuickBasePositioner::resetTopPadding() +{ + Q_D(QQuickBasePositioner); + d->setTopPadding(0, true); +} + +qreal QQuickBasePositioner::leftPadding() const +{ + Q_D(const QQuickBasePositioner); + if (d->extra.isAllocated() && d->extra->explicitLeftPadding) + return d->extra->leftPadding; + return d->padding(); +} + +void QQuickBasePositioner::setLeftPadding(qreal padding) +{ + Q_D(QQuickBasePositioner); + d->setLeftPadding(padding); +} + +void QQuickBasePositioner::resetLeftPadding() +{ + Q_D(QQuickBasePositioner); + d->setLeftPadding(0, true); +} + +qreal QQuickBasePositioner::rightPadding() const +{ + Q_D(const QQuickBasePositioner); + if (d->extra.isAllocated() && d->extra->explicitRightPadding) + return d->extra->rightPadding; + return d->padding(); +} + +void QQuickBasePositioner::setRightPadding(qreal padding) +{ + Q_D(QQuickBasePositioner); + d->setRightPadding(padding); +} + +void QQuickBasePositioner::resetRightPadding() +{ + Q_D(QQuickBasePositioner); + d->setRightPadding(0, true); +} + +qreal QQuickBasePositioner::bottomPadding() const +{ + Q_D(const QQuickBasePositioner); + if (d->extra.isAllocated() && d->extra->explicitBottomPadding) + return d->extra->bottomPadding; + return d->padding(); +} + +void QQuickBasePositioner::setBottomPadding(qreal padding) +{ + Q_D(QQuickBasePositioner); + d->setBottomPadding(padding); +} + +void QQuickBasePositioner::resetBottomPadding() +{ + Q_D(QQuickBasePositioner); + d->setBottomPadding(0, true); +} + +QQuickBasePositionerPrivate::ExtraData::ExtraData() + : padding(0) + , topPadding(0) + , leftPadding(0) + , rightPadding(0) + , bottomPadding(0) + , explicitTopPadding(false) + , explicitLeftPadding(false) + , explicitRightPadding(false) + , explicitBottomPadding(false) +{ +} + +void QQuickBasePositionerPrivate::setTopPadding(qreal value, bool reset) +{ + Q_Q(QQuickBasePositioner); + qreal oldPadding = q->topPadding(); + if (!reset || extra.isAllocated()) { + extra.value().topPadding = value; + extra.value().explicitTopPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + setPositioningDirty(); + emit q->topPaddingChanged(); + } +} + +void QQuickBasePositionerPrivate::setLeftPadding(qreal value, bool reset) +{ + Q_Q(QQuickBasePositioner); + qreal oldPadding = q->leftPadding(); + if (!reset || extra.isAllocated()) { + extra.value().leftPadding = value; + extra.value().explicitLeftPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + setPositioningDirty(); + emit q->leftPaddingChanged(); + } +} + +void QQuickBasePositionerPrivate::setRightPadding(qreal value, bool reset) +{ + Q_Q(QQuickBasePositioner); + qreal oldPadding = q->rightPadding(); + if (!reset || extra.isAllocated()) { + extra.value().rightPadding = value; + extra.value().explicitRightPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + setPositioningDirty(); + emit q->rightPaddingChanged(); + } +} + +void QQuickBasePositionerPrivate::setBottomPadding(qreal value, bool reset) +{ + Q_Q(QQuickBasePositioner); + qreal oldPadding = q->bottomPadding(); + if (!reset || extra.isAllocated()) { + extra.value().bottomPadding = value; + extra.value().explicitBottomPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + setPositioningDirty(); + emit q->bottomPaddingChanged(); + } +} + /*! \qmltype Positioner \instantiates QQuickPositionerAttached @@ -640,6 +827,16 @@ void QQuickPositionerAttached::setIsLastItem(bool isLastItem) \sa Row, Grid, Flow, Positioner, ColumnLayout, {Qt Quick Examples - Positioners} */ /*! + \since 5.6 + \qmlproperty real QtQuick::Column::padding + \qmlproperty real QtQuick::Column::topPadding + \qmlproperty real QtQuick::Column::leftPadding + \qmlproperty real QtQuick::Column::bottomPadding + \qmlproperty real QtQuick::Column::rightPadding + + These properties hold the padding around the content. +*/ +/*! \qmlproperty Transition QtQuick::Column::populate This property holds the transition to be run for items that are part of @@ -715,20 +912,23 @@ 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 - qreal voffset = 0; + qreal voffset = topPadding(); + const qreal padding = leftPadding() + rightPadding(); + contentSize->setWidth(qMax(contentSize->width(), padding)); for (int ii = 0; ii < positionedItems.count(); ++ii) { PositionedItem &child = positionedItems[ii]; - positionItemY(voffset, &child); - contentSize->setWidth(qMax(contentSize->width(), child.item->width())); + positionItem(child.itemX() + leftPadding() - child.leftPadding, voffset, &child); + child.updatePadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); + contentSize->setWidth(qMax(contentSize->width(), child.item->width() + padding)); voffset += child.item->height(); voffset += spacing(); } - if (voffset != 0)//If we positioned any items, undo the spacing from the last item + if (voffset - topPadding() != 0)//If we positioned any items, undo the spacing from the last item voffset -= spacing(); - contentSize->setHeight(voffset); + contentSize->setHeight(voffset + bottomPadding()); } void QQuickColumn::reportConflictingAnchors() @@ -794,6 +994,16 @@ void QQuickColumn::reportConflictingAnchors() \sa Column, Grid, Flow, Positioner, RowLayout, {Qt Quick Examples - Positioners} */ /*! + \since 5.6 + \qmlproperty real QtQuick::Row::padding + \qmlproperty real QtQuick::Row::topPadding + \qmlproperty real QtQuick::Row::leftPadding + \qmlproperty real QtQuick::Row::bottomPadding + \qmlproperty real QtQuick::Row::rightPadding + + These properties hold the padding around the content. +*/ +/*! \qmlproperty Transition QtQuick::Row::populate This property holds the transition to be run for items that are part of @@ -940,27 +1150,34 @@ void QQuickRow::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 hoffset = 0; + qreal hoffset1 = leftPadding(); + qreal hoffset2 = rightPadding(); + if (!d->isLeftToRight()) + qSwap(hoffset1, hoffset2); + qreal hoffset = hoffset1; + const qreal padding = topPadding() + bottomPadding(); + contentSize->setHeight(qMax(contentSize->height(), padding)); QList<qreal> hoffsets; for (int ii = 0; ii < positionedItems.count(); ++ii) { PositionedItem &child = positionedItems[ii]; if (d->isLeftToRight()) { - positionItemX(hoffset, &child); + positionItem(hoffset, child.itemY() + topPadding() - child.topPadding, &child); + child.updatePadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); } else { hoffsets << hoffset; } - contentSize->setHeight(qMax(contentSize->height(), child.item->height())); + contentSize->setHeight(qMax(contentSize->height(), child.item->height() + padding)); hoffset += child.item->width(); hoffset += spacing(); } - if (hoffset != 0)//If we positioned any items, undo the extra spacing from the last item + if (hoffset - hoffset1 != 0)//If we positioned any items, undo the extra spacing from the last item hoffset -= spacing(); - contentSize->setWidth(hoffset); + contentSize->setWidth(hoffset + hoffset2); if (d->isLeftToRight()) return; @@ -976,7 +1193,8 @@ void QQuickRow::doPositioning(QSizeF *contentSize) for (int ii = 0; ii < positionedItems.count(); ++ii) { PositionedItem &child = positionedItems[ii]; hoffset = end - hoffsets[acc++] - child.item->width(); - positionItemX(hoffset, &child); + positionItem(hoffset, child.itemY() + topPadding() - child.topPadding, &child); + child.updatePadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); } } @@ -1044,6 +1262,16 @@ void QQuickRow::reportConflictingAnchors() \sa Flow, Row, Column, Positioner, GridLayout, {Qt Quick Examples - Positioners} */ /*! + \since 5.6 + \qmlproperty real QtQuick::Grid::padding + \qmlproperty real QtQuick::Grid::topPadding + \qmlproperty real QtQuick::Grid::leftPadding + \qmlproperty real QtQuick::Grid::bottomPadding + \qmlproperty real QtQuick::Grid::rightPadding + + These properties hold the padding around the content. +*/ +/*! \qmlproperty Transition QtQuick::Grid::populate This property holds the transition to be run for items that are part of @@ -1423,8 +1651,11 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) c = (numVisible+(m_rows-1))/m_rows; } - if (r == 0 || c == 0) - return; //Nothing to do + if (r == 0 || c == 0) { + contentSize->setHeight(topPadding() + bottomPadding()); + contentSize->setWidth(leftPadding() + rightPadding()); + return; //Nothing else to do + } QList<qreal> maxColWidth; QList<qreal> maxRowHeight; @@ -1476,6 +1707,7 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) widthSum += columnSpacing; widthSum += maxColWidth[j]; } + widthSum += leftPadding() + rightPadding(); qreal heightSum = 0; for (int i = 0; i < maxRowHeight.size(); i++) { @@ -1483,6 +1715,7 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) heightSum += rowSpacing; heightSum += maxRowHeight[i]; } + heightSum += topPadding() + bottomPadding(); contentSize->setHeight(heightSum); contentSize->setWidth(widthSum); @@ -1493,10 +1726,10 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) else end = widthSum; - qreal xoffset = 0; + qreal xoffset = leftPadding(); if (!d->isLeftToRight()) - xoffset = end; - qreal yoffset = 0; + xoffset = end - rightPadding(); + qreal yoffset = topPadding(); int curRow =0; int curCol =0; for (int i = 0; i < positionedItems.count(); ++i) { @@ -1518,6 +1751,7 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) alignYOffset += maxRowHeight[curRow] - child.item->height(); positionItem(childXOffset, alignYOffset, &child); + child.updatePadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); if (m_flow == LeftToRight) { if (d->isLeftToRight()) @@ -1529,9 +1763,9 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) if (!curCol) { yoffset += maxRowHeight[curRow]+rowSpacing; if (d->isLeftToRight()) - xoffset = 0; + xoffset = leftPadding(); else - xoffset = end; + xoffset = end - rightPadding(); curRow++; if (curRow>=r) break; @@ -1545,7 +1779,7 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) xoffset += maxColWidth[curCol]+columnSpacing; else xoffset -= maxColWidth[curCol]+columnSpacing; - yoffset = 0; + yoffset = topPadding(); curCol++; if (curCol>=c) break; @@ -1603,6 +1837,16 @@ void QQuickGrid::reportConflictingAnchors() \sa Column, Row, Grid, Positioner, {Qt Quick Examples - Positioners} */ /*! + \since 5.6 + \qmlproperty real QtQuick::Flow::padding + \qmlproperty real QtQuick::Flow::topPadding + \qmlproperty real QtQuick::Flow::leftPadding + \qmlproperty real QtQuick::Flow::bottomPadding + \qmlproperty real QtQuick::Flow::rightPadding + + These properties hold the padding around the content. +*/ +/*! \qmlproperty Transition QtQuick::Flow::populate This property holds the transition to be run for items that are part of @@ -1786,23 +2030,30 @@ void QQuickFlow::doPositioning(QSizeF *contentSize) //Precondition: All items in the positioned list have a valid item pointer and should be positioned Q_D(QQuickFlow); - qreal hoffset = 0; - qreal voffset = 0; + qreal hoffset1 = leftPadding(); + qreal hoffset2 = rightPadding(); + if (!d->isLeftToRight()) + qSwap(hoffset1, hoffset2); + qreal hoffset = hoffset1; + const qreal voffset1 = topPadding(); + qreal voffset = voffset1; qreal linemax = 0; QList<qreal> hoffsets; + contentSize->setWidth(qMax(contentSize->width(), hoffset1 + hoffset2)); + contentSize->setHeight(qMax(contentSize->height(), voffset + bottomPadding())); for (int i = 0; i < positionedItems.count(); ++i) { PositionedItem &child = positionedItems[i]; if (d->flow == LeftToRight) { - if (widthValid() && hoffset && hoffset + child.item->width() > width()) { - hoffset = 0; + if (widthValid() && hoffset != hoffset1 && hoffset + child.item->width() + hoffset2 > width()) { + hoffset = hoffset1; voffset += linemax + spacing(); linemax = 0; } } else { - if (heightValid() && voffset && voffset + child.item->height() > height()) { - voffset = 0; + if (heightValid() && voffset != voffset1 && voffset + child.item->height() + bottomPadding() > height()) { + voffset = voffset1; hoffset += linemax + spacing(); linemax = 0; } @@ -1810,13 +2061,16 @@ void QQuickFlow::doPositioning(QSizeF *contentSize) if (d->isLeftToRight()) { positionItem(hoffset, voffset, &child); + child.updatePadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); } else { hoffsets << hoffset; positionItemY(voffset, &child); + child.topPadding = topPadding(); + child.bottomPadding = bottomPadding(); } - contentSize->setWidth(qMax(contentSize->width(), hoffset + child.item->width())); - contentSize->setHeight(qMax(contentSize->height(), voffset + child.item->height())); + contentSize->setWidth(qMax(contentSize->width(), hoffset + child.item->width() + hoffset2)); + contentSize->setHeight(qMax(contentSize->height(), voffset + child.item->height() + bottomPadding())); if (d->flow == LeftToRight) { hoffset += child.item->width(); @@ -1828,6 +2082,7 @@ void QQuickFlow::doPositioning(QSizeF *contentSize) linemax = qMax(linemax, child.item->width()); } } + if (d->isLeftToRight()) return; @@ -1841,6 +2096,8 @@ void QQuickFlow::doPositioning(QSizeF *contentSize) PositionedItem &child = positionedItems[i]; hoffset = end - hoffsets[acc++] - child.item->width(); positionItemX(hoffset, &child); + child.leftPadding = leftPadding(); + child.rightPadding = rightPadding(); } } diff --git a/src/quick/items/qquickpositioners_p.h b/src/quick/items/qquickpositioners_p.h index ea779695eb..bc67701306 100644 --- a/src/quick/items/qquickpositioners_p.h +++ b/src/quick/items/qquickpositioners_p.h @@ -86,6 +86,12 @@ class Q_QUICK_PRIVATE_EXPORT QQuickBasePositioner : public QQuickImplicitSizeIte Q_PROPERTY(QQuickTransition *populate READ populate WRITE setPopulate NOTIFY populateChanged) Q_PROPERTY(QQuickTransition *move READ move WRITE setMove NOTIFY moveChanged) Q_PROPERTY(QQuickTransition *add READ add WRITE setAdd NOTIFY addChanged) + + Q_PROPERTY(qreal padding READ padding WRITE setPadding RESET resetPadding NOTIFY paddingChanged REVISION 6) + Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding RESET resetTopPadding NOTIFY topPaddingChanged REVISION 6) + Q_PROPERTY(qreal leftPadding READ leftPadding WRITE setLeftPadding RESET resetLeftPadding NOTIFY leftPaddingChanged REVISION 6) + 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) public: enum PositionerType { None = 0x0, Horizontal = 0x1, Vertical = 0x2, Both = 0x3 }; @@ -108,6 +114,26 @@ public: void updateAttachedProperties(QQuickPositionerAttached *specificProperty = 0, QQuickItem *specificPropertyOwner = 0) const; + qreal padding() const; + void setPadding(qreal padding); + void resetPadding(); + + qreal topPadding() const; + void setTopPadding(qreal padding); + void resetTopPadding(); + + qreal leftPadding() const; + void setLeftPadding(qreal padding); + void resetLeftPadding(); + + qreal rightPadding() const; + void setRightPadding(qreal padding); + void resetRightPadding(); + + qreal bottomPadding() const; + void setBottomPadding(qreal padding); + void resetBottomPadding(); + protected: QQuickBasePositioner(QQuickBasePositionerPrivate &dd, PositionerType at, QQuickItem *parent); void componentComplete() Q_DECL_OVERRIDE; @@ -120,6 +146,11 @@ Q_SIGNALS: void populateChanged(); void moveChanged(); void addChanged(); + Q_REVISION(6) void paddingChanged(); + Q_REVISION(6) void topPaddingChanged(); + Q_REVISION(6) void leftPaddingChanged(); + Q_REVISION(6) void rightPaddingChanged(); + Q_REVISION(6) void bottomPaddingChanged(); protected Q_SLOTS: void prePositioning(); @@ -144,11 +175,18 @@ protected: bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds); void startTransition(QQuickItemViewTransitioner *transitioner); + void updatePadding(qreal lp, qreal tp, qreal rp, qreal bp); + QQuickItem *item; QQuickItemViewTransitionableItem *transitionableItem; int index; bool isNew; bool isVisible; + + qreal topPadding; + qreal leftPadding; + qreal rightPadding; + qreal bottomPadding; }; QPODVector<PositionedItem,8> positionedItems; @@ -236,8 +274,8 @@ public: void setColumnSpacing(qreal); void resetColumnSpacing() { m_useColumnSpacing = false; } - Q_ENUMS(Flow) enum Flow { LeftToRight, TopToBottom }; + Q_ENUM(Flow) Flow flow() const; void setFlow(Flow); @@ -245,14 +283,14 @@ public: void setLayoutDirection (Qt::LayoutDirection); Qt::LayoutDirection effectiveLayoutDirection() const; - Q_ENUMS(HAlignment) - Q_ENUMS(VAlignment) enum HAlignment { AlignLeft = Qt::AlignLeft, AlignRight = Qt::AlignRight, AlignHCenter = Qt::AlignHCenter}; + Q_ENUM(HAlignment) enum VAlignment { AlignTop = Qt::AlignTop, AlignBottom = Qt::AlignBottom, AlignVCenter = Qt::AlignVCenter }; + Q_ENUM(VAlignment) HAlignment hItemAlign() const; void setHItemAlign(HAlignment align); @@ -301,8 +339,8 @@ class Q_AUTOTEST_EXPORT QQuickFlow: public QQuickBasePositioner public: QQuickFlow(QQuickItem *parent=0); - Q_ENUMS(Flow) enum Flow { LeftToRight, TopToBottom }; + Q_ENUM(Flow) Flow flow() const; void setFlow(Flow); diff --git a/src/quick/items/qquickpositioners_p_p.h b/src/quick/items/qquickpositioners_p_p.h index 3a8fe20351..6e1e15d4ef 100644 --- a/src/quick/items/qquickpositioners_p_p.h +++ b/src/quick/items/qquickpositioners_p_p.h @@ -51,6 +51,7 @@ #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> #include <QtCore/qstring.h> @@ -65,10 +66,26 @@ class QQuickBasePositionerPrivate : public QQuickImplicitSizeItemPrivate, public Q_DECLARE_PUBLIC(QQuickBasePositioner) public: + struct ExtraData { + ExtraData(); + + qreal padding; + qreal topPadding; + qreal leftPadding; + qreal rightPadding; + qreal bottomPadding; + bool explicitTopPadding : 1; + bool explicitLeftPadding : 1; + bool explicitRightPadding : 1; + bool explicitBottomPadding : 1; + }; + QLazilyAllocated<ExtraData> extra; + QQuickBasePositionerPrivate() : spacing(0), type(QQuickBasePositioner::None) , transitioner(0), positioningDirty(false) , doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight) + { } @@ -149,6 +166,12 @@ public: virtual void effectiveLayoutDirectionChange() { } + + 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); + void setRightPadding(qreal value, bool reset = false); + void setBottomPadding(qreal value, bool reset = false); }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickrectangle_p.h b/src/quick/items/qquickrectangle_p.h index 8a00dbaf92..16afa39bdd 100644 --- a/src/quick/items/qquickrectangle_p.h +++ b/src/quick/items/qquickrectangle_p.h @@ -125,7 +125,7 @@ private: }; class QQuickRectanglePrivate; -class Q_AUTOTEST_EXPORT QQuickRectangle : public QQuickItem +class Q_QUICK_PRIVATE_EXPORT QQuickRectangle : public QQuickItem { Q_OBJECT diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index 6b0b9c3a06..cc4cec443a 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -46,7 +46,6 @@ #include <QtQuick/QQuickWindow> #include <QtQuick/private/qquickwindow_p.h> -#include <private/qqmlprofilerservice_p.h> #include <QtCore/private/qobject_p.h> QT_BEGIN_NAMESPACE diff --git a/src/quick/items/qquickrepeater.cpp b/src/quick/items/qquickrepeater.cpp index 0168d73c8f..09b504b742 100644 --- a/src/quick/items/qquickrepeater.cpp +++ b/src/quick/items/qquickrepeater.cpp @@ -50,6 +50,7 @@ QQuickRepeaterPrivate::QQuickRepeaterPrivate() , delegateValidated(false) , itemCount(0) { + setTransparentForPositioner(true); } QQuickRepeaterPrivate::~QQuickRepeaterPrivate() @@ -360,8 +361,8 @@ void QQuickRepeater::clear() if (QQuickItem *item = d->deletables.at(i)) { if (complete) emit itemRemoved(i, item); - item->setParentItem(0); d->model->release(item); + item->setParentItem(0); } } } @@ -397,9 +398,17 @@ void QQuickRepeaterPrivate::requestItems() void QQuickRepeater::createdItem(int index, QObject *) { Q_D(QQuickRepeater); + QObject *object = d->model->object(index, false); + QQuickItem *item = qmlobject_cast<QQuickItem*>(object); + emit itemAdded(index, item); +} + +void QQuickRepeater::initItem(int index, QObject *object) +{ + Q_D(QQuickRepeater); + QQuickItem *item = qmlobject_cast<QQuickItem*>(object); + if (!d->deletables.at(index)) { - QObject *object = d->model->object(index, false); - QQuickItem *item = qmlobject_cast<QQuickItem*>(object); if (!item) { if (object) { d->model->release(object); @@ -425,17 +434,9 @@ void QQuickRepeater::createdItem(int index, QObject *) } item->stackBefore(after); } - emit itemAdded(index, item); } } -void QQuickRepeater::initItem(int, QObject *object) -{ - QQuickItem *item = qmlobject_cast<QQuickItem*>(object); - if (item) - item->setParentItem(parentItem()); -} - void QQuickRepeater::modelUpdated(const QQmlChangeSet &changeSet, bool reset) { Q_D(QQuickRepeater); @@ -465,8 +466,8 @@ void QQuickRepeater::modelUpdated(const QQmlChangeSet &changeSet, bool reset) d->deletables.remove(index); emit itemRemoved(index, item); if (item) { - item->setParentItem(0); d->model->release(item); + item->setParentItem(0); } --d->itemCount; } diff --git a/src/quick/items/qquickscalegrid_p_p.h b/src/quick/items/qquickscalegrid_p_p.h index 7e0c021c19..b12da7a706 100644 --- a/src/quick/items/qquickscalegrid_p_p.h +++ b/src/quick/items/qquickscalegrid_p_p.h @@ -47,7 +47,6 @@ QT_BEGIN_NAMESPACE class QQuickScaleGrid : public QObject { Q_OBJECT - Q_ENUMS(TileRule) Q_PROPERTY(int left READ left WRITE setLeft NOTIFY borderChanged) Q_PROPERTY(int top READ top WRITE setTop NOTIFY borderChanged) diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index 1c60d9feaa..75a8319615 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -92,8 +92,6 @@ class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem Q_PROPERTY(QString log READ log NOTIFY logChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(bool supportsAtlasTextures READ supportsAtlasTextures WRITE setSupportsAtlasTextures NOTIFY supportsAtlasTexturesChanged REVISION 1) - Q_ENUMS(CullMode) - Q_ENUMS(Status) public: enum CullMode @@ -102,6 +100,7 @@ public: BackFaceCulling = QQuickShaderEffectMaterial::BackFaceCulling, FrontFaceCulling = QQuickShaderEffectMaterial::FrontFaceCulling }; + Q_ENUM(CullMode) enum Status { @@ -109,6 +108,7 @@ public: Uncompiled, Error }; + Q_ENUM(Status) QQuickShaderEffect(QQuickItem *parent = 0); ~QQuickShaderEffect(); diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickshadereffectnode.cpp index de6cae0ea6..b84a4adaab 100644 --- a/src/quick/items/qquickshadereffectnode.cpp +++ b/src/quick/items/qquickshadereffectnode.cpp @@ -83,7 +83,9 @@ QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectM , m_compiled(false) , m_initialized(false) { - for (int i = 0; i < m_attributes.count(); ++i) + const int attributesCount = m_attributes.count(); + m_attributeNames.reserve(attributesCount + 1); + for (int i = 0; i < attributesCount; ++i) m_attributeNames.append(m_attributes.at(i).constData()); m_attributeNames.append(0); } diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h index 31e503be42..94bb315566 100644 --- a/src/quick/items/qquickshadereffectsource_p.h +++ b/src/quick/items/qquickshadereffectsource_p.h @@ -67,7 +67,6 @@ 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_ENUMS(Format WrapMode) public: enum WrapMode { ClampToEdge, @@ -75,12 +74,14 @@ public: RepeatVertically, Repeat }; + Q_ENUM(WrapMode) enum Format { Alpha = GL_ALPHA, RGB = GL_RGB, RGBA = GL_RGBA }; + Q_ENUM(Format) QQuickShaderEffectSource(QQuickItem *parent = 0); ~QQuickShaderEffectSource(); diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp index 579919db27..ac7fbc24af 100644 --- a/src/quick/items/qquickstateoperations.cpp +++ b/src/quick/items/qquickstateoperations.cpp @@ -358,7 +358,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() newBinding->setTarget(property); QQuickStateAction xa; xa.property = property; - xa.toBinding = QQmlAbstractBinding::getPointer(newBinding); + xa.toBinding = newBinding; xa.fromValue = xa.property.read(); xa.deletableToBinding = true; actions << xa; @@ -377,7 +377,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() newBinding->setTarget(property); QQuickStateAction ya; ya.property = property; - ya.toBinding = QQmlAbstractBinding::getPointer(newBinding); + ya.toBinding = newBinding; ya.fromValue = ya.property.read(); ya.deletableToBinding = true; actions << ya; @@ -396,7 +396,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() newBinding->setTarget(property); QQuickStateAction sa; sa.property = property; - sa.toBinding = QQmlAbstractBinding::getPointer(newBinding); + sa.toBinding = newBinding; sa.fromValue = sa.property.read(); sa.deletableToBinding = true; actions << sa; @@ -415,7 +415,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() newBinding->setTarget(property); QQuickStateAction ra; ra.property = property; - ra.toBinding = QQmlAbstractBinding::getPointer(newBinding); + ra.toBinding = newBinding; ra.fromValue = ra.property.read(); ra.deletableToBinding = true; actions << ra; @@ -434,7 +434,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() newBinding->setTarget(property); QQuickStateAction wa; wa.property = property; - wa.toBinding = QQmlAbstractBinding::getPointer(newBinding); + wa.toBinding = newBinding; wa.fromValue = wa.property.read(); wa.deletableToBinding = true; actions << wa; @@ -453,7 +453,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions() newBinding->setTarget(property); QQuickStateAction ha; ha.property = property; - ha.toBinding = QQmlAbstractBinding::getPointer(newBinding); + ha.toBinding = newBinding; ha.fromValue = ha.property.read(); ha.deletableToBinding = true; actions << ha; @@ -482,7 +482,7 @@ void QQuickParentChange::saveOriginals() saveCurrentValues(); }*/ -void QQuickParentChange::execute(Reason) +void QQuickParentChange::execute() { Q_D(QQuickParentChange); d->doChange(d->parent); @@ -493,7 +493,7 @@ bool QQuickParentChange::isReversable() return true; } -void QQuickParentChange::reverse(Reason) +void QQuickParentChange::reverse() { Q_D(QQuickParentChange); d->doChange(d->origParent, d->origStackBefore); @@ -765,12 +765,7 @@ class QQuickAnchorChangesPrivate : public QQuickStateOperationPrivate { public: QQuickAnchorChangesPrivate() - : target(0), anchorSet(new QQuickAnchorSet), - leftBinding(0), rightBinding(0), hCenterBinding(0), - topBinding(0), bottomBinding(0), vCenterBinding(0), baselineBinding(0), - origLeftBinding(0), origRightBinding(0), origHCenterBinding(0), - origTopBinding(0), origBottomBinding(0), origVCenterBinding(0), - origBaselineBinding(0) + : target(0), anchorSet(new QQuickAnchorSet) { } @@ -779,21 +774,21 @@ public: QQuickItem *target; QQuickAnchorSet *anchorSet; - QQmlBinding *leftBinding; - QQmlBinding *rightBinding; - QQmlBinding *hCenterBinding; - QQmlBinding *topBinding; - QQmlBinding *bottomBinding; - QQmlBinding *vCenterBinding; - QQmlBinding *baselineBinding; - - QQmlAbstractBinding *origLeftBinding; - QQmlAbstractBinding *origRightBinding; - QQmlAbstractBinding *origHCenterBinding; - QQmlAbstractBinding *origTopBinding; - QQmlAbstractBinding *origBottomBinding; - QQmlAbstractBinding *origVCenterBinding; - QQmlAbstractBinding *origBaselineBinding; + QExplicitlySharedDataPointer<QQmlBinding> leftBinding; + QExplicitlySharedDataPointer<QQmlBinding> rightBinding; + QExplicitlySharedDataPointer<QQmlBinding> hCenterBinding; + QExplicitlySharedDataPointer<QQmlBinding> topBinding; + QExplicitlySharedDataPointer<QQmlBinding> bottomBinding; + QExplicitlySharedDataPointer<QQmlBinding> vCenterBinding; + QExplicitlySharedDataPointer<QQmlBinding> baselineBinding; + + QQmlAbstractBinding::Ptr origLeftBinding; + QQmlAbstractBinding::Ptr origRightBinding; + QQmlAbstractBinding::Ptr origHCenterBinding; + QQmlAbstractBinding::Ptr origTopBinding; + QQmlAbstractBinding::Ptr origBottomBinding; + QQmlAbstractBinding::Ptr origVCenterBinding; + QQmlAbstractBinding::Ptr origBaselineBinding; QQuickAnchorLine rewindLeft; QQuickAnchorLine rewindRight; @@ -831,8 +826,6 @@ public: qreal origX; qreal origY; - QList<QQmlAbstractBinding*> oldBindings; - QQmlProperty leftProp; QQmlProperty rightProp; QQmlProperty hCenterProp; @@ -849,29 +842,6 @@ QQuickAnchorChanges::QQuickAnchorChanges(QObject *parent) QQuickAnchorChanges::~QQuickAnchorChanges() { - /* - if the anchorchanges is active at destruction, any non-active orig - bindings need to be destroyed - - the basic logic is that if both e.g. left and origLeft are present, - then we are active (otherwise left would have been destroyed), and - left is in use and origLeft needs to be cleaned up. - */ - Q_D(QQuickAnchorChanges); - if (d->leftBinding && d->origLeftBinding) - d->origLeftBinding->destroy(); - if (d->rightBinding && d->origRightBinding) - d->origRightBinding->destroy(); - if (d->hCenterBinding && d->origHCenterBinding) - d->origHCenterBinding->destroy(); - if (d->topBinding && d->origTopBinding) - d->origTopBinding->destroy(); - if (d->bottomBinding && d->origBottomBinding) - d->origBottomBinding->destroy(); - if (d->vCenterBinding && d->origVCenterBinding) - d->origVCenterBinding->destroy(); - if (d->baselineBinding && d->origBaselineBinding) - d->origBaselineBinding->destroy(); } QQuickAnchorChanges::ActionList QQuickAnchorChanges::actions() @@ -967,7 +937,7 @@ void QQuickAnchorChanges::setObject(QQuickItem *target) \endqml */ -void QQuickAnchorChanges::execute(Reason reason) +void QQuickAnchorChanges::execute() { Q_D(QQuickAnchorChanges); if (!d->target) @@ -978,94 +948,84 @@ void QQuickAnchorChanges::execute(Reason reason) if (d->applyOrigLeft) { if (!d->origLeftBinding) targetPrivate->anchors()->resetLeft(); - QQmlPropertyPrivate::setBinding(d->leftProp, d->origLeftBinding); + QQmlPropertyPrivate::setBinding(d->leftProp, d->origLeftBinding.data()); } if (d->applyOrigRight) { if (!d->origRightBinding) targetPrivate->anchors()->resetRight(); - QQmlPropertyPrivate::setBinding(d->rightProp, d->origRightBinding); + QQmlPropertyPrivate::setBinding(d->rightProp, d->origRightBinding.data()); } if (d->applyOrigHCenter) { if (!d->origHCenterBinding) targetPrivate->anchors()->resetHorizontalCenter(); - QQmlPropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding); + QQmlPropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding.data()); } if (d->applyOrigTop) { if (!d->origTopBinding) targetPrivate->anchors()->resetTop(); - QQmlPropertyPrivate::setBinding(d->topProp, d->origTopBinding); + QQmlPropertyPrivate::setBinding(d->topProp, d->origTopBinding.data()); } if (d->applyOrigBottom) { if (!d->origBottomBinding) targetPrivate->anchors()->resetBottom(); - QQmlPropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding); + QQmlPropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding.data()); } if (d->applyOrigVCenter) { if (!d->origVCenterBinding) targetPrivate->anchors()->resetVerticalCenter(); - QQmlPropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); + QQmlPropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding.data()); } if (d->applyOrigBaseline) { if (!d->origBaselineBinding) targetPrivate->anchors()->resetBaseline(); - QQmlPropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); - } - - //destroy old bindings - if (reason == ActualChange) { - for (int i = 0; i < d->oldBindings.size(); ++i) { - QQmlAbstractBinding *binding = d->oldBindings.at(i); - if (binding) - binding->destroy(); - } - d->oldBindings.clear(); + QQmlPropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding.data()); } //reset any anchors that have been specified as "undefined" if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::LeftAnchor) { targetPrivate->anchors()->resetLeft(); - QQmlPropertyPrivate::setBinding(d->leftProp, 0); + QQmlPropertyPrivate::removeBinding(d->leftProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::RightAnchor) { targetPrivate->anchors()->resetRight(); - QQmlPropertyPrivate::setBinding(d->rightProp, 0); + QQmlPropertyPrivate::removeBinding(d->rightProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::HCenterAnchor) { targetPrivate->anchors()->resetHorizontalCenter(); - QQmlPropertyPrivate::setBinding(d->hCenterProp, 0); + QQmlPropertyPrivate::removeBinding(d->hCenterProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::TopAnchor) { targetPrivate->anchors()->resetTop(); - QQmlPropertyPrivate::setBinding(d->topProp, 0); + QQmlPropertyPrivate::removeBinding(d->topProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::BottomAnchor) { targetPrivate->anchors()->resetBottom(); - QQmlPropertyPrivate::setBinding(d->bottomProp, 0); + QQmlPropertyPrivate::removeBinding(d->bottomProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::VCenterAnchor) { targetPrivate->anchors()->resetVerticalCenter(); - QQmlPropertyPrivate::setBinding(d->vCenterProp, 0); + QQmlPropertyPrivate::removeBinding(d->vCenterProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::BaselineAnchor) { targetPrivate->anchors()->resetBaseline(); - QQmlPropertyPrivate::setBinding(d->baselineProp, 0); + QQmlPropertyPrivate::removeBinding(d->baselineProp); } //set any anchors that have been specified if (d->leftBinding) - QQmlPropertyPrivate::setBinding(d->leftBinding->property(), d->leftBinding); + QQmlPropertyPrivate::setBinding(d->leftBinding.data()); if (d->rightBinding) - QQmlPropertyPrivate::setBinding(d->rightBinding->property(), d->rightBinding); + QQmlPropertyPrivate::setBinding(d->rightBinding.data()); if (d->hCenterBinding) - QQmlPropertyPrivate::setBinding(d->hCenterBinding->property(), d->hCenterBinding); + QQmlPropertyPrivate::setBinding(d->hCenterBinding.data()); if (d->topBinding) - QQmlPropertyPrivate::setBinding(d->topBinding->property(), d->topBinding); + QQmlPropertyPrivate::setBinding(d->topBinding.data()); if (d->bottomBinding) - QQmlPropertyPrivate::setBinding(d->bottomBinding->property(), d->bottomBinding); + QQmlPropertyPrivate::setBinding(d->bottomBinding.data()); if (d->vCenterBinding) - QQmlPropertyPrivate::setBinding(d->vCenterBinding->property(), d->vCenterBinding); + QQmlPropertyPrivate::setBinding(d->vCenterBinding.data()); if (d->baselineBinding) - QQmlPropertyPrivate::setBinding(d->baselineBinding->property(), d->baselineBinding); + QQmlPropertyPrivate::setBinding(d->baselineBinding.data()); } bool QQuickAnchorChanges::isReversable() @@ -1073,7 +1033,7 @@ bool QQuickAnchorChanges::isReversable() return true; } -void QQuickAnchorChanges::reverse(Reason reason) +void QQuickAnchorChanges::reverse() { Q_D(QQuickAnchorChanges); if (!d->target) @@ -1083,69 +1043,48 @@ void QQuickAnchorChanges::reverse(Reason reason) //reset any anchors set by the state if (d->leftBinding) { targetPrivate->anchors()->resetLeft(); - QQmlPropertyPrivate::setBinding(d->leftBinding->property(), 0); - if (reason == ActualChange) { - d->leftBinding->destroy(); d->leftBinding = 0; - } + QQmlPropertyPrivate::removeBinding(d->leftBinding.data()); } if (d->rightBinding) { targetPrivate->anchors()->resetRight(); - QQmlPropertyPrivate::setBinding(d->rightBinding->property(), 0); - if (reason == ActualChange) { - d->rightBinding->destroy(); d->rightBinding = 0; - } + QQmlPropertyPrivate::removeBinding(d->rightBinding.data()); } if (d->hCenterBinding) { targetPrivate->anchors()->resetHorizontalCenter(); - QQmlPropertyPrivate::setBinding(d->hCenterBinding->property(), 0); - if (reason == ActualChange) { - d->hCenterBinding->destroy(); d->hCenterBinding = 0; - } + QQmlPropertyPrivate::removeBinding(d->hCenterBinding.data()); } if (d->topBinding) { targetPrivate->anchors()->resetTop(); - QQmlPropertyPrivate::setBinding(d->topBinding->property(), 0); - if (reason == ActualChange) { - d->topBinding->destroy(); d->topBinding = 0; - } + QQmlPropertyPrivate::removeBinding(d->topBinding.data()); } if (d->bottomBinding) { targetPrivate->anchors()->resetBottom(); - QQmlPropertyPrivate::setBinding(d->bottomBinding->property(), 0); - if (reason == ActualChange) { - d->bottomBinding->destroy(); d->bottomBinding = 0; - } + QQmlPropertyPrivate::removeBinding(d->bottomBinding.data()); } if (d->vCenterBinding) { targetPrivate->anchors()->resetVerticalCenter(); - QQmlPropertyPrivate::setBinding(d->vCenterBinding->property(), 0); - if (reason == ActualChange) { - d->vCenterBinding->destroy(); d->vCenterBinding = 0; - } + QQmlPropertyPrivate::removeBinding(d->vCenterBinding.data()); } if (d->baselineBinding) { targetPrivate->anchors()->resetBaseline(); - QQmlPropertyPrivate::setBinding(d->baselineBinding->property(), 0); - if (reason == ActualChange) { - d->baselineBinding->destroy(); d->baselineBinding = 0; - } + QQmlPropertyPrivate::removeBinding(d->baselineBinding.data()); } //restore previous anchors if (d->origLeftBinding) - QQmlPropertyPrivate::setBinding(d->leftProp, d->origLeftBinding); + QQmlPropertyPrivate::setBinding(d->leftProp, d->origLeftBinding.data()); if (d->origRightBinding) - QQmlPropertyPrivate::setBinding(d->rightProp, d->origRightBinding); + QQmlPropertyPrivate::setBinding(d->rightProp, d->origRightBinding.data()); if (d->origHCenterBinding) - QQmlPropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding); + QQmlPropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding.data()); if (d->origTopBinding) - QQmlPropertyPrivate::setBinding(d->topProp, d->origTopBinding); + QQmlPropertyPrivate::setBinding(d->topProp, d->origTopBinding.data()); if (d->origBottomBinding) - QQmlPropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding); + QQmlPropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding.data()); if (d->origVCenterBinding) - QQmlPropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding); + QQmlPropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding.data()); if (d->origBaselineBinding) - QQmlPropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding); + QQmlPropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding.data()); //restore any absolute geometry changed by the state's anchors QQuickAnchors::Anchors stateVAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Vertical_Mask; @@ -1289,10 +1228,6 @@ void QQuickAnchorChanges::copyOriginals(QQuickStateActionEvent *other) d->origX = acp->origX; d->origY = acp->origY; - d->oldBindings.clear(); - d->oldBindings << acp->leftBinding << acp->rightBinding << acp->hCenterBinding - << acp->topBinding << acp->bottomBinding << acp->vCenterBinding << acp->baselineBinding; - //clear old values from other //### could this be generalized for all QQuickStateActionEvents, and called after copyOriginals? acp->leftBinding = 0; @@ -1333,31 +1268,31 @@ void QQuickAnchorChanges::clearBindings() d->anchorSet->d_func()->usedAnchors; if (d->applyOrigLeft || (combined & QQuickAnchors::LeftAnchor)) { targetPrivate->anchors()->resetLeft(); - QQmlPropertyPrivate::setBinding(d->leftProp, 0); + QQmlPropertyPrivate::removeBinding(d->leftProp); } if (d->applyOrigRight || (combined & QQuickAnchors::RightAnchor)) { targetPrivate->anchors()->resetRight(); - QQmlPropertyPrivate::setBinding(d->rightProp, 0); + QQmlPropertyPrivate::removeBinding(d->rightProp); } if (d->applyOrigHCenter || (combined & QQuickAnchors::HCenterAnchor)) { targetPrivate->anchors()->resetHorizontalCenter(); - QQmlPropertyPrivate::setBinding(d->hCenterProp, 0); + QQmlPropertyPrivate::removeBinding(d->hCenterProp); } if (d->applyOrigTop || (combined & QQuickAnchors::TopAnchor)) { targetPrivate->anchors()->resetTop(); - QQmlPropertyPrivate::setBinding(d->topProp, 0); + QQmlPropertyPrivate::removeBinding(d->topProp); } if (d->applyOrigBottom || (combined & QQuickAnchors::BottomAnchor)) { targetPrivate->anchors()->resetBottom(); - QQmlPropertyPrivate::setBinding(d->bottomProp, 0); + QQmlPropertyPrivate::removeBinding(d->bottomProp); } if (d->applyOrigVCenter || (combined & QQuickAnchors::VCenterAnchor)) { targetPrivate->anchors()->resetVerticalCenter(); - QQmlPropertyPrivate::setBinding(d->vCenterProp, 0); + QQmlPropertyPrivate::removeBinding(d->vCenterProp); } if (d->applyOrigBaseline || (combined & QQuickAnchors::BaselineAnchor)) { targetPrivate->anchors()->resetBaseline(); - QQmlPropertyPrivate::setBinding(d->baselineProp, 0); + QQmlPropertyPrivate::removeBinding(d->baselineProp); } } diff --git a/src/quick/items/qquickstateoperations_p.h b/src/quick/items/qquickstateoperations_p.h index 8d4231c5fa..1999e23a83 100644 --- a/src/quick/items/qquickstateoperations_p.h +++ b/src/quick/items/qquickstateoperations_p.h @@ -97,9 +97,9 @@ public: void saveOriginals() Q_DECL_OVERRIDE; //virtual void copyOriginals(QQuickStateActionEvent*); - void execute(Reason reason = ActualChange) Q_DECL_OVERRIDE; + void execute() Q_DECL_OVERRIDE; bool isReversable() Q_DECL_OVERRIDE; - void reverse(Reason reason = ActualChange) Q_DECL_OVERRIDE; + void reverse() Q_DECL_OVERRIDE; EventType type() const Q_DECL_OVERRIDE; bool override(QQuickStateActionEvent*other) Q_DECL_OVERRIDE; void rewind() Q_DECL_OVERRIDE; @@ -180,9 +180,9 @@ public: QQuickItem *object() const; void setObject(QQuickItem *); - void execute(Reason reason = ActualChange) Q_DECL_OVERRIDE; + void execute() Q_DECL_OVERRIDE; bool isReversable() Q_DECL_OVERRIDE; - void reverse(Reason reason = ActualChange) Q_DECL_OVERRIDE; + void reverse() Q_DECL_OVERRIDE; EventType type() const Q_DECL_OVERRIDE; bool override(QQuickStateActionEvent*other) Q_DECL_OVERRIDE; bool changesBindings() Q_DECL_OVERRIDE; diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 205571f2e7..924c455872 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -85,7 +85,16 @@ QQuickTextPrivate::QQuickTextPrivate() } QQuickTextPrivate::ExtraData::ExtraData() - : lineHeight(1.0) + : padding(0) + , topPadding(0) + , leftPadding(0) + , rightPadding(0) + , bottomPadding(0) + , explicitTopPadding(false) + , explicitLeftPadding(false) + , explicitRightPadding(false) + , explicitBottomPadding(false) + , lineHeight(1.0) , doc(0) , minimumPixelSize(12) , minimumPointSize(12) @@ -284,6 +293,74 @@ qreal QQuickTextPrivate::getImplicitHeight() const return implicitHeight; } +qreal QQuickTextPrivate::availableWidth() const +{ + Q_Q(const QQuickText); + return q->width() - q->leftPadding() - q->rightPadding(); +} + +qreal QQuickTextPrivate::availableHeight() const +{ + Q_Q(const QQuickText); + return q->height() - q->topPadding() - q->bottomPadding(); +} + +void QQuickTextPrivate::setTopPadding(qreal value, bool reset) +{ + Q_Q(QQuickText); + qreal oldPadding = q->topPadding(); + if (!reset || extra.isAllocated()) { + extra.value().topPadding = value; + extra.value().explicitTopPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + updateSize(); + emit q->topPaddingChanged(); + } +} + +void QQuickTextPrivate::setLeftPadding(qreal value, bool reset) +{ + Q_Q(QQuickText); + qreal oldPadding = q->leftPadding(); + if (!reset || extra.isAllocated()) { + extra.value().leftPadding = value; + extra.value().explicitLeftPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + updateSize(); + emit q->leftPaddingChanged(); + } +} + +void QQuickTextPrivate::setRightPadding(qreal value, bool reset) +{ + Q_Q(QQuickText); + qreal oldPadding = q->rightPadding(); + if (!reset || extra.isAllocated()) { + extra.value().rightPadding = value; + extra.value().explicitRightPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + updateSize(); + emit q->rightPaddingChanged(); + } +} + +void QQuickTextPrivate::setBottomPadding(qreal value, bool reset) +{ + Q_Q(QQuickText); + qreal oldPadding = q->bottomPadding(); + if (!reset || extra.isAllocated()) { + extra.value().bottomPadding = value; + extra.value().explicitBottomPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + updateSize(); + emit q->bottomPaddingChanged(); + } +} + /*! \qmlproperty bool QtQuick::Text::antialiasing @@ -327,9 +404,9 @@ void QQuickTextPrivate::updateLayout() formatModifiesFontSize = fontSizeModified; multilengthEos = -1; } else { - layout.clearAdditionalFormats(); + layout.clearFormats(); if (elideLayout) - elideLayout->clearAdditionalFormats(); + elideLayout->clearFormats(); QString tmp = text; multilengthEos = tmp.indexOf(QLatin1Char('\x9c')); if (multilengthEos != -1) { @@ -410,7 +487,7 @@ void QQuickTextPrivate::updateBaseline(qreal baseline, qreal dy) yoff = dy/2; } - q->setBaselineOffset(baseline + yoff); + q->setBaselineOffset(baseline + yoff + q->topPadding()); } void QQuickTextPrivate::updateSize() @@ -430,6 +507,9 @@ void QQuickTextPrivate::updateSize() return; } + qreal hPadding = q->leftPadding() + q->rightPadding(); + qreal vPadding = q->topPadding() + q->bottomPadding(); + if (text.isEmpty() && !isLineLaidOutConnected() && fontSizeMode() == QQuickText::FixedSize) { // How much more expensive is it to just do a full layout on an empty string here? // There may be subtle differences in the height and baseline calculations between @@ -442,8 +522,8 @@ void QQuickTextPrivate::updateSize() ? lineHeight() : fontHeight * lineHeight(); } - updateBaseline(fm.ascent(), q->height() - fontHeight); - q->setImplicitSize(0, fontHeight); + updateBaseline(fm.ascent(), q->height() - fontHeight - vPadding); + q->setImplicitSize(hPadding, fontHeight + vPadding); layedOutTextRect = QRectF(0, 0, 0, fontHeight); emit q->contentSizeChanged(); updateType = UpdatePaintNode; @@ -464,7 +544,7 @@ void QQuickTextPrivate::updateSize() layedOutTextRect = textRect; size = textRect.size(); - updateBaseline(baseline, q->height() - size.height()); + updateBaseline(baseline, q->height() - size.height() - vPadding); } else { widthExceeded = true; // always relayout rich text on width changes.. heightExceeded = false; // rich text layout isn't affected by height changes. @@ -488,15 +568,15 @@ void QQuickTextPrivate::updateSize() naturalWidth = extra->doc->idealWidth(); const bool wasInLayout = internalWidthUpdate; internalWidthUpdate = true; - q->setImplicitWidth(naturalWidth); + q->setImplicitWidth(naturalWidth + hPadding); internalWidthUpdate = wasInLayout; } if (internalWidthUpdate) return; extra->doc->setPageSize(QSizeF()); - if (q->widthValid() && (wrapMode != QQuickText::NoWrap || extra->doc->idealWidth() < q->width())) - extra->doc->setTextWidth(q->width()); + if (q->widthValid() && (wrapMode != QQuickText::NoWrap || extra->doc->idealWidth() < availableWidth())) + extra->doc->setTextWidth(availableWidth()); else extra->doc->setTextWidth(extra->doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug) @@ -505,7 +585,7 @@ void QQuickTextPrivate::updateSize() size = QSizeF(extra->doc->idealWidth(),dsize.height()); QFontMetricsF fm(font); - updateBaseline(fm.ascent(), q->height() - size.height()); + updateBaseline(fm.ascent(), q->height() - size.height() - vPadding); //### need to confirm cost of always setting these for richText internalWidthUpdate = true; @@ -513,11 +593,11 @@ void QQuickTextPrivate::updateSize() if (!q->widthValid()) iWidth = size.width(); if (iWidth > -1) - q->setImplicitSize(iWidth, size.height()); + q->setImplicitSize(iWidth + hPadding, size.height() + vPadding); internalWidthUpdate = false; if (iWidth == -1) - q->setImplicitHeight(size.height()); + q->setImplicitHeight(size.height() + vPadding); } if (layedOutTextRect.size() != previousSize) @@ -623,7 +703,7 @@ void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, // use the text item's width by default if it has one and wrap is on or text must be aligned if (q->widthValid() && (q->wrapMode() != QQuickText::NoWrap || q->effectiveHAlign() != QQuickText::AlignLeft)) - textLine->setWidth(q->width()); + textLine->setWidth(availableWidth()); else textLine->setWidth(INT_MAX); if (lineHeight() != 1.0) @@ -635,10 +715,10 @@ void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, } void QQuickTextPrivate::elideFormats( - const int start, const int length, int offset, QList<QTextLayout::FormatRange> *elidedFormats) + const int start, const int length, int offset, QVector<QTextLayout::FormatRange> *elidedFormats) { const int end = start + length; - QList<QTextLayout::FormatRange> formats = layout.additionalFormats(); + const QVector<QTextLayout::FormatRange> formats = layout.formats(); for (int i = 0; i < formats.count(); ++i) { QTextLayout::FormatRange format = formats.at(i); const int formatLength = qMin(format.start + format.length, end) - qMax(format.start, start); @@ -691,10 +771,11 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) && (q->heightValid() || maximumLineCountValid); if ((!requireImplicitSize || (implicitWidthValid && implicitHeightValid)) - && ((singlelineElide && q->width() <= 0.) || (multilineElide && q->heightValid() && q->height() <= 0.))) { + && ((singlelineElide && availableWidth() <= 0.) + || (multilineElide && q->heightValid() && availableHeight() <= 0.))) { // we are elided and we have a zero width or height - widthExceeded = q->widthValid() && q->width() <= 0.; - heightExceeded = q->heightValid() && q->height() <= 0.; + widthExceeded = q->widthValid() && availableWidth() <= 0.; + heightExceeded = q->heightValid() && availableHeight() <= 0.; if (!truncated) { truncated = true; @@ -730,7 +811,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) lineWidth = (q->widthValid() || implicitWidthValid) && q->width() > 0 ? q->width() : FLT_MAX; - qreal maxHeight = q->heightValid() ? q->height() : FLT_MAX; + qreal maxHeight = q->heightValid() ? availableHeight() : FLT_MAX; const bool customLayout = isLineLaidOutConnected(); const bool wasTruncated = truncated; @@ -751,8 +832,8 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) int scaledFontSize = largeFont; bool widthChanged = false; - widthExceeded = q->width() <= 0 && (singlelineElide || canWrap || horizontalFit); - heightExceeded = q->height() <= 0 && (multilineElide || verticalFit); + widthExceeded = availableWidth() <= 0 && (singlelineElide || canWrap || horizontalFit); + heightExceeded = availableHeight() <= 0 && (multilineElide || verticalFit); QRectF br; @@ -922,7 +1003,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) bool wasInLayout = internalWidthUpdate; internalWidthUpdate = true; - q->setImplicitSize(naturalWidth, naturalHeight); + q->setImplicitSize(naturalWidth + q->leftPadding() + q->rightPadding(), naturalHeight + q->topPadding() + q->bottomPadding()); internalWidthUpdate = wasInLayout; // Update any variables that are dependent on the validity of the width or height. @@ -939,8 +1020,11 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) const qreal oldWidth = lineWidth; const qreal oldHeight = maxHeight; - lineWidth = q->widthValid() && q->width() > 0 ? q->width() : naturalWidth; - maxHeight = q->heightValid() ? q->height() : FLT_MAX; + const qreal availWidth = availableWidth(); + const qreal availHeight = availableHeight(); + + lineWidth = q->widthValid() && availWidth > 0 ? availWidth : naturalWidth; + maxHeight = q->heightValid() ? availHeight : FLT_MAX; // If the width of the item has changed and it's possible the result of wrapping, // eliding, scaling has changed, or the text is not left aligned do another layout. @@ -993,7 +1077,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) && (q->heightValid() || (maximumLineCountValid && canWrap)); const qreal oldHeight = maxHeight; - maxHeight = q->heightValid() ? q->height() : FLT_MAX; + maxHeight = q->heightValid() ? availableHeight() : FLT_MAX; // If the height of the item has changed and it's possible the result of eliding, // line count truncation or scaling has changed, do another layout. if ((maxHeight < qMin(oldHeight, naturalHeight) || (heightExceeded && maxHeight > oldHeight)) @@ -1073,7 +1157,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) elideLayout->setCacheEnabled(true); } if (styledText) { - QList<QTextLayout::FormatRange> formats; + QVector<QTextLayout::FormatRange> formats; switch (elideMode) { case QQuickText::ElideRight: elideFormats(elideStart, elideText.length() - 1, 0, &formats); @@ -1096,7 +1180,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) default: break; } - elideLayout->setAdditionalFormats(formats); + elideLayout->setFormats(formats); } elideLayout->setFont(layout.font()); @@ -1361,6 +1445,16 @@ QQuickText::~QQuickText() */ /*! + \qmlproperty string QtQuick::Text::font.styleName + \since 5.6 + + Sets the style name of the font. + + The style name is case insensitive. If set, the font will be matched against style name instead + of the font properties \l weight, \l bold and \l italic. +*/ + +/*! \qmlproperty bool QtQuick::Text::font.bold Sets whether the font weight is bold. @@ -2279,7 +2373,7 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data d->updateType = QQuickTextPrivate::UpdateNone; - const qreal dy = QQuickTextUtil::alignedY(d->layedOutTextRect.height() + d->lineHeightOffset(), height(), d->vAlign); + const qreal dy = QQuickTextUtil::alignedY(d->layedOutTextRect.height() + d->lineHeightOffset(), d->availableHeight(), d->vAlign) + topPadding(); QQuickTextNode *node = 0; if (!oldNode) @@ -2296,11 +2390,11 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data const QColor linkColor = QColor::fromRgba(d->linkColor); if (d->richText) { - const qreal dx = QQuickTextUtil::alignedX(d->layedOutTextRect.width(), width(), effectiveHAlign()); + const qreal dx = QQuickTextUtil::alignedX(d->layedOutTextRect.width(), d->availableWidth(), effectiveHAlign()) + leftPadding(); d->ensureDoc(); node->addTextDocument(QPointF(dx, dy), d->extra->doc, color, d->style, styleColor, linkColor); } else if (d->layedOutTextRect.width() > 0) { - const qreal dx = QQuickTextUtil::alignedX(d->lineWidth, width(), effectiveHAlign()); + const qreal dx = QQuickTextUtil::alignedX(d->lineWidth, d->availableWidth(), effectiveHAlign()) + leftPadding(); int unelidedLineCount = d->lineCount; if (d->elideLayout) unelidedLineCount -= 1; @@ -2570,7 +2664,7 @@ QString QQuickTextPrivate::anchorAt(const QTextLayout *layout, const QPointF &mo QTextLine line = layout->lineAt(i); if (line.naturalTextRect().contains(mousePos)) { int charPos = line.xToCursor(mousePos.x(), QTextLine::CursorOnCharacter); - foreach (const QTextLayout::FormatRange &formatRange, layout->additionalFormats()) { + foreach (const QTextLayout::FormatRange &formatRange, layout->formats()) { if (formatRange.format.isAnchor() && charPos >= formatRange.start && charPos < formatRange.start + formatRange.length) { @@ -2587,14 +2681,15 @@ QString QQuickTextPrivate::anchorAt(const QPointF &mousePos) const { Q_Q(const QQuickText); QPointF translatedMousePos = mousePos; - translatedMousePos.ry() -= QQuickTextUtil::alignedY(layedOutTextRect.height() + lineHeightOffset(), q->height(), vAlign); + translatedMousePos.rx() -= q->leftPadding(); + translatedMousePos.ry() -= q->topPadding() + QQuickTextUtil::alignedY(layedOutTextRect.height() + lineHeightOffset(), availableHeight(), vAlign); if (styledText) { QString link = anchorAt(&layout, translatedMousePos); if (link.isEmpty() && elideLayout) link = anchorAt(elideLayout, translatedMousePos); return link; } else if (richText && extra.isAllocated() && extra->doc) { - translatedMousePos.rx() -= QQuickTextUtil::alignedX(layedOutTextRect.width(), q->width(), q->effectiveHAlign()); + translatedMousePos.rx() -= QQuickTextUtil::alignedX(layedOutTextRect.width(), availableWidth(), q->effectiveHAlign()); return extra->doc->documentLayout()->anchorAt(translatedMousePos); } return QString(); @@ -2812,4 +2907,125 @@ void QQuickText::invalidateFontCaches() } } +/*! + \since 5.6 + \qmlproperty real QtQuick::Text::padding + \qmlproperty real QtQuick::Text::topPadding + \qmlproperty real QtQuick::Text::leftPadding + \qmlproperty real QtQuick::Text::bottomPadding + \qmlproperty real QtQuick::Text::rightPadding + + These properties hold the padding around the content. This space is reserved + in addition to the contentWidth and contentHeight. +*/ +qreal QQuickText::padding() const +{ + Q_D(const QQuickText); + return d->padding(); +} + +void QQuickText::setPadding(qreal padding) +{ + Q_D(QQuickText); + if (qFuzzyCompare(d->padding(), padding)) + return; + + d->extra.value().padding = padding; + d->updateSize(); + emit paddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitTopPadding) + emit topPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding) + emit leftPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitRightPadding) + emit rightPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding) + emit bottomPaddingChanged(); +} + +void QQuickText::resetPadding() +{ + setPadding(0); +} + +qreal QQuickText::topPadding() const +{ + Q_D(const QQuickText); + if (d->extra.isAllocated() && d->extra->explicitTopPadding) + return d->extra->topPadding; + return d->padding(); +} + +void QQuickText::setTopPadding(qreal padding) +{ + Q_D(QQuickText); + d->setTopPadding(padding); +} + +void QQuickText::resetTopPadding() +{ + Q_D(QQuickText); + d->setTopPadding(0, true); +} + +qreal QQuickText::leftPadding() const +{ + Q_D(const QQuickText); + if (d->extra.isAllocated() && d->extra->explicitLeftPadding) + return d->extra->leftPadding; + return d->padding(); +} + +void QQuickText::setLeftPadding(qreal padding) +{ + Q_D(QQuickText); + d->setLeftPadding(padding); +} + +void QQuickText::resetLeftPadding() +{ + Q_D(QQuickText); + d->setLeftPadding(0, true); +} + +qreal QQuickText::rightPadding() const +{ + Q_D(const QQuickText); + if (d->extra.isAllocated() && d->extra->explicitRightPadding) + return d->extra->rightPadding; + return d->padding(); +} + +void QQuickText::setRightPadding(qreal padding) +{ + Q_D(QQuickText); + d->setRightPadding(padding); +} + +void QQuickText::resetRightPadding() +{ + Q_D(QQuickText); + d->setRightPadding(0, true); +} + +qreal QQuickText::bottomPadding() const +{ + Q_D(const QQuickText); + if (d->extra.isAllocated() && d->extra->explicitBottomPadding) + return d->extra->bottomPadding; + return d->padding(); +} + +void QQuickText::setBottomPadding(qreal padding) +{ + Q_D(QQuickText); + d->setBottomPadding(padding); +} + +void QQuickText::resetBottomPadding() +{ + Q_D(QQuickText); + d->setBottomPadding(0, true); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h index 283e3b510b..f92927c9c4 100644 --- a/src/quick/items/qquicktext_p.h +++ b/src/quick/items/qquicktext_p.h @@ -45,15 +45,6 @@ class QQuickTextLine; class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem { Q_OBJECT - Q_ENUMS(HAlignment) - Q_ENUMS(VAlignment) - Q_ENUMS(TextStyle) - Q_ENUMS(TextFormat) - Q_ENUMS(TextElideMode) - Q_ENUMS(WrapMode) - Q_ENUMS(LineHeightMode) - Q_ENUMS(FontSizeMode) - Q_ENUMS(RenderType) Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) @@ -84,6 +75,12 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem Q_PROPERTY(RenderType renderType READ renderType WRITE setRenderType NOTIFY renderTypeChanged) Q_PROPERTY(QString hoveredLink READ hoveredLink NOTIFY linkHovered REVISION 2) + Q_PROPERTY(qreal padding READ padding WRITE setPadding RESET resetPadding NOTIFY paddingChanged REVISION 6) + Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding RESET resetTopPadding NOTIFY topPaddingChanged REVISION 6) + Q_PROPERTY(qreal leftPadding READ leftPadding WRITE setLeftPadding RESET resetLeftPadding NOTIFY leftPaddingChanged REVISION 6) + 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) + public: QQuickText(QQuickItem *parent=0); ~QQuickText(); @@ -92,21 +89,26 @@ public: AlignRight = Qt::AlignRight, AlignHCenter = Qt::AlignHCenter, AlignJustify = Qt::AlignJustify }; + Q_ENUM(HAlignment) enum VAlignment { AlignTop = Qt::AlignTop, AlignBottom = Qt::AlignBottom, AlignVCenter = Qt::AlignVCenter }; + Q_ENUM(VAlignment) enum TextStyle { Normal, Outline, Raised, Sunken }; + Q_ENUM(TextStyle) enum TextFormat { PlainText = Qt::PlainText, RichText = Qt::RichText, AutoText = Qt::AutoText, StyledText = 4 }; + Q_ENUM(TextFormat) enum TextElideMode { ElideLeft = Qt::ElideLeft, ElideRight = Qt::ElideRight, ElideMiddle = Qt::ElideMiddle, ElideNone = Qt::ElideNone }; + Q_ENUM(TextElideMode) enum WrapMode { NoWrap = QTextOption::NoWrap, WordWrap = QTextOption::WordWrap, @@ -114,15 +116,19 @@ public: WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere }; + Q_ENUM(WrapMode) enum RenderType { QtRendering, NativeRendering }; + Q_ENUM(RenderType) enum LineHeightMode { ProportionalHeight, FixedHeight }; + Q_ENUM(LineHeightMode) enum FontSizeMode { FixedSize = 0x0, HorizontalFit = 0x01, VerticalFit = 0x02, Fit = HorizontalFit | VerticalFit }; + Q_ENUM(FontSizeMode) QString text() const; void setText(const QString &); @@ -204,6 +210,26 @@ public: Q_REVISION(3) Q_INVOKABLE QString linkAt(qreal x, qreal y) const; + qreal padding() const; + void setPadding(qreal padding); + void resetPadding(); + + qreal topPadding() const; + void setTopPadding(qreal padding); + void resetTopPadding(); + + qreal leftPadding() const; + void setLeftPadding(qreal padding); + void resetLeftPadding(); + + qreal rightPadding() const; + void setRightPadding(qreal padding); + void resetRightPadding(); + + qreal bottomPadding() const; + void setBottomPadding(qreal padding); + void resetBottomPadding(); + Q_SIGNALS: void textChanged(const QString &text); void linkActivated(const QString &link); @@ -231,6 +257,11 @@ Q_SIGNALS: void lineLaidOut(QQuickTextLine *line); void baseUrlChanged(); void renderTypeChanged(); + Q_REVISION(6) void paddingChanged(); + Q_REVISION(6) void topPaddingChanged(); + Q_REVISION(6) void leftPaddingChanged(); + Q_REVISION(6) void rightPaddingChanged(); + Q_REVISION(6) void bottomPaddingChanged(); protected: void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index cd14008728..f43df691b5 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -78,7 +78,7 @@ public: int lineHeightOffset() const; QString elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine = 0) const; - void elideFormats(int start, int length, int offset, QList<QTextLayout::FormatRange> *elidedFormats); + void elideFormats(int start, int length, int offset, QVector<QTextLayout::FormatRange> *elidedFormats); void processHoverEvent(QHoverEvent *event); @@ -87,6 +87,15 @@ public: struct ExtraData { ExtraData(); + qreal padding; + qreal topPadding; + qreal leftPadding; + qreal rightPadding; + qreal bottomPadding; + bool explicitTopPadding : 1; + bool explicitLeftPadding : 1; + bool explicitRightPadding : 1; + bool explicitBottomPadding : 1; qreal lineHeight; QQuickTextDocumentWithImageResources *doc; QString activeLink; @@ -160,6 +169,15 @@ public: qreal getImplicitWidth() const Q_DECL_OVERRIDE; qreal getImplicitHeight() const Q_DECL_OVERRIDE; + qreal availableWidth() const; + qreal availableHeight() const; + + 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); + void setRightPadding(qreal value, bool reset = false); + void setBottomPadding(qreal value, bool reset = false); + void ensureDoc(); QRectF setupTextLayout(qreal * const baseline); diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index 6929bb44a2..7bc5fab677 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -1313,7 +1313,7 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) QTextLayout *layout = block.layout(); if (isGettingInput) layout->setPreeditArea(cursor.position() - block.position(), e->preeditString()); - QList<QTextLayout::FormatRange> overrides; + QVector<QTextLayout::FormatRange> overrides; const int oldPreeditCursor = preeditCursor; preeditCursor = e->preeditString().length(); hasImState = !e->preeditString().isEmpty(); @@ -1336,7 +1336,7 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) } } } - layout->setAdditionalFormats(overrides); + layout->setFormats(overrides); cursor.endEditBlock(); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index cd1cf5eef1..dc4e301a36 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -172,6 +172,13 @@ QQuickTextEdit::QQuickTextEdit(QQuickItem *parent) d->init(); } +QQuickTextEdit::QQuickTextEdit(QQuickTextEditPrivate &dd, QQuickItem *parent) +: QQuickImplicitSizeItem(dd, parent) +{ + Q_D(QQuickTextEdit); + d->init(); +} + QString QQuickTextEdit::text() const { Q_D(const QQuickTextEdit); @@ -199,6 +206,17 @@ QString QQuickTextEdit::text() const */ /*! + \qmlproperty string QtQuick::TextEdit::font.styleName + \since 5.6 + + Sets the style name of the font. + + The style name is case insensitive. If set, the font will be matched against style name instead + of the font properties \l weight, \l bold and \l italic. +*/ + + +/*! \qmlproperty bool QtQuick::TextEdit::font.bold Sets whether the font weight is bold. @@ -699,6 +717,62 @@ Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints() const } #endif +void QQuickTextEditPrivate::setTopPadding(qreal value, bool reset) +{ + Q_Q(QQuickTextEdit); + qreal oldPadding = q->topPadding(); + if (!reset || extra.isAllocated()) { + extra.value().topPadding = value; + extra.value().explicitTopPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + q->updateSize(); + emit q->topPaddingChanged(); + } +} + +void QQuickTextEditPrivate::setLeftPadding(qreal value, bool reset) +{ + Q_Q(QQuickTextEdit); + qreal oldPadding = q->leftPadding(); + if (!reset || extra.isAllocated()) { + extra.value().leftPadding = value; + extra.value().explicitLeftPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + q->updateSize(); + emit q->leftPaddingChanged(); + } +} + +void QQuickTextEditPrivate::setRightPadding(qreal value, bool reset) +{ + Q_Q(QQuickTextEdit); + qreal oldPadding = q->rightPadding(); + if (!reset || extra.isAllocated()) { + extra.value().rightPadding = value; + extra.value().explicitRightPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + q->updateSize(); + emit q->rightPaddingChanged(); + } +} + +void QQuickTextEditPrivate::setBottomPadding(qreal value, bool reset) +{ + Q_Q(QQuickTextEdit); + qreal oldPadding = q->bottomPadding(); + if (!reset || extra.isAllocated()) { + extra.value().bottomPadding = value; + extra.value().explicitBottomPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + q->updateSize(); + emit q->bottomPaddingChanged(); + } +} + QQuickTextEdit::VAlignment QQuickTextEdit::vAlign() const { Q_D(const QQuickTextEdit); @@ -1658,6 +1732,8 @@ void QQuickTextEdit::mousePressEvent(QMouseEvent *event) #ifndef QT_NO_IM if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) qGuiApp->inputMethod()->show(); +#else + Q_UNUSED(hadActiveFocus); #endif } if (!event->isAccepted()) @@ -1793,6 +1869,14 @@ void QQuickTextEdit::invalidateFontCaches() } } +inline void resetEngine(QQuickTextNodeEngine *engine, const QColor& textColor, const QColor& selectedTextColor, const QColor& selectionColor) +{ + *engine = QQuickTextNodeEngine(); + engine->setTextColor(textColor); + engine->setSelectedTextColor(selectedTextColor); + engine->setSelectionColor(selectionColor); +} + QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) { Q_UNUSED(updatePaintNodeData); @@ -1818,6 +1902,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * while (nodeIterator != d->textNodeMap.end() && !(*nodeIterator)->dirty()) ++nodeIterator; + QQuickTextNodeEngine engine; + QQuickTextNodeEngine frameDecorationsEngine; if (!oldNode || nodeIterator < d->textNodeMap.end()) { @@ -1837,6 +1923,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * // FIXME: the text decorations could probably be handled separately (only updated for affected textFrames) rootNode->resetFrameDecorations(d->createTextNode()); + resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor); QQuickTextNode *node = 0; @@ -1856,11 +1943,12 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * while (!frames.isEmpty()) { QTextFrame *textFrame = frames.takeFirst(); frames.append(textFrame->childFrames()); - rootNode->frameDecorationsNode->m_engine->addFrameDecorations(d->document, textFrame); + frameDecorationsEngine.addFrameDecorations(d->document, textFrame); if (textFrame->lastPosition() < firstDirtyPos || (firstCleanNode && textFrame->firstPosition() >= firstCleanNode->startPos())) continue; node = d->createTextNode(); + resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor); if (textFrame->firstPosition() > textFrame->lastPosition() && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) { @@ -1869,8 +1957,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * ProtectedLayoutAccessor *a = static_cast<ProtectedLayoutAccessor *>(d->document->documentLayout()); QTextCharFormat format = a->formatAccessor(pos); QTextBlock block = textFrame->firstCursorPosition().block(); - node->m_engine->setCurrentLine(block.layout()->lineForTextPosition(pos - block.position())); - node->m_engine->addTextObject(QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document, + engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position())); + engine.addTextObject(QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document, pos, textFrame->frameFormat().position()); nodeStart = pos; } else { @@ -1888,13 +1976,13 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * if (block.position() < firstDirtyPos) continue; - if (!node->m_engine->hasContents()) { + if (!engine.hasContents()) { nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft(); updateNodeTransform(node, nodeOffset); nodeStart = block.position(); } - node->m_engine->addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1); + engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1); currentNodeSize += block.length(); if ((it.atEnd()) || (firstCleanNode && block.next().position() >= firstCleanNode->startPos())) // last node that needed replacing or last block of the frame @@ -1903,15 +1991,16 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * QList<int>::const_iterator lowerBound = std::lower_bound(frameBoundaries.constBegin(), frameBoundaries.constEnd(), block.next().position()); if (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart) { currentNodeSize = 0; - d->addCurrentTextNodeToRoot(rootNode, node, nodeIterator, nodeStart); + d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart); node = d->createTextNode(); + resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor); nodeStart = block.next().position(); } } } - d->addCurrentTextNodeToRoot(rootNode, node, nodeIterator, nodeStart); + d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart); } - rootNode->frameDecorationsNode->m_engine->addToSceneGraph(rootNode->frameDecorationsNode, QQuickText::Normal, QColor()); + frameDecorationsEngine.addToSceneGraph(rootNode->frameDecorationsNode, QQuickText::Normal, QColor()); // Now prepend the frame decorations since we want them rendered first, with the text nodes and cursor in front. rootNode->prependChildNode(rootNode->frameDecorationsNode); @@ -2016,6 +2105,19 @@ bool QQuickTextEdit::isInputMethodComposing() const #endif // QT_NO_IM } +QQuickTextEditPrivate::ExtraData::ExtraData() + : padding(0) + , topPadding(0) + , leftPadding(0) + , rightPadding(0) + , bottomPadding(0) + , explicitTopPadding(false) + , explicitLeftPadding(false) + , explicitRightPadding(false) + , explicitBottomPadding(false) +{ +} + void QQuickTextEditPrivate::init() { Q_Q(QQuickTextEdit); @@ -2099,7 +2201,7 @@ void QQuickTextEdit::markDirtyNodesForRange(int start, int end, int charDelta) } // mark the affected nodes as dirty - while (it != d->textNodeMap.constEnd()) { + while (it != d->textNodeMap.end()) { if ((*it)->startPos() <= end) (*it)->setDirty(); else if (charDelta) @@ -2225,7 +2327,7 @@ void QQuickTextEdit::updateSize() return; } - qreal naturalWidth = d->implicitWidth; + qreal naturalWidth = d->implicitWidth - leftPadding() - rightPadding(); qreal newWidth = d->document->idealWidth(); // ### assumes that if the width is set, the text will fill to edges @@ -2243,13 +2345,13 @@ void QQuickTextEdit::updateSize() const bool wasInLayout = d->inLayout; d->inLayout = true; - setImplicitWidth(naturalWidth); + setImplicitWidth(naturalWidth + leftPadding() + rightPadding()); d->inLayout = wasInLayout; if (d->inLayout) // probably the result of a binding loop, but by letting it return; // get this far we'll get a warning to that effect. } if (d->document->textWidth() != width()) { - d->document->setTextWidth(width()); + d->document->setTextWidth(width() - leftPadding() - rightPadding()); newWidth = d->document->idealWidth(); } //### need to confirm cost of always setting these @@ -2264,12 +2366,12 @@ void QQuickTextEdit::updateSize() // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed. if (!widthValid() && !d->requireImplicitWidth) - setImplicitSize(newWidth, newHeight); + setImplicitSize(newWidth + leftPadding() + rightPadding(), newHeight + topPadding() + bottomPadding()); else - setImplicitHeight(newHeight); + setImplicitHeight(newHeight + topPadding() + bottomPadding()); - d->xoff = qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width(), effectiveHAlign())); - d->yoff = QQuickTextUtil::alignedY(d->document->size().height(), height(), d->vAlign); + d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign())); + d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign); setBaselineOffset(fm.ascent() + d->yoff + d->textMargin); QSizeF size(newWidth, newHeight); @@ -2416,16 +2518,19 @@ void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event) qGuiApp->inputMethod()->show(); q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), q, SLOT(q_updateAlignment())); +#endif } else { +#ifndef QT_NO_IM q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), q, SLOT(q_updateAlignment())); #endif + emit q->editingFinished(); } } -void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QSGTransformNode *root, QQuickTextNode *node, TextNodeIterator &it, int startPos) +void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QQuickTextNode *node, TextNodeIterator &it, int startPos) { - node->m_engine->addToSceneGraph(node, QQuickText::Normal, QColor()); + engine->addToSceneGraph(node, QQuickText::Normal, QColor()); it = textNodeMap.insert(it, new TextNode(startPos, node)); ++it; root->appendChildNode(node); @@ -2436,7 +2541,6 @@ QQuickTextNode *QQuickTextEditPrivate::createTextNode() Q_Q(QQuickTextEdit); QQuickTextNode* node = new QQuickTextNode(q); node->setUseNativeRenderer(renderType == QQuickTextEdit::NativeRendering); - node->initEngine(color, selectedTextColor, selectionColor); return node; } @@ -2588,6 +2692,15 @@ bool QQuickTextEditPrivate::isLinkHoveredConnected() */ /*! + \qmlsignal QtQuick::TextEdit::editingFinished() + \since 5.6 + + This signal is emitted when the text edit loses focus. + + The corresponding handler is \c onEditingFinished. +*/ + +/*! \qmlproperty string QtQuick::TextEdit::hoveredLink \since 5.2 @@ -2680,7 +2793,132 @@ void QQuickTextEdit::append(const QString &text) QString QQuickTextEdit::linkAt(qreal x, qreal y) const { Q_D(const QQuickTextEdit); - return d->control->anchorAt(QPointF(x, y)); + return d->control->anchorAt(QPointF(x + topPadding(), y + leftPadding())); +} + +/*! + \since 5.6 + \qmlproperty real QtQuick::TextEdit::padding + \qmlproperty real QtQuick::TextEdit::topPadding + \qmlproperty real QtQuick::TextEdit::leftPadding + \qmlproperty real QtQuick::TextEdit::bottomPadding + \qmlproperty real QtQuick::TextEdit::rightPadding + + These properties hold the padding around the content. This space is reserved + in addition to the contentWidth and contentHeight. +*/ +qreal QQuickTextEdit::padding() const +{ + Q_D(const QQuickTextEdit); + return d->padding(); +} + +void QQuickTextEdit::setPadding(qreal padding) +{ + Q_D(QQuickTextEdit); + if (qFuzzyCompare(d->padding(), padding)) + return; + + d->extra.value().padding = padding; + updateSize(); + if (isComponentComplete()) { + d->updateType = QQuickTextEditPrivate::UpdatePaintNode; + update(); + } + emit paddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitTopPadding) + emit topPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding) + emit leftPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitRightPadding) + emit rightPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding) + emit bottomPaddingChanged(); +} + +void QQuickTextEdit::resetPadding() +{ + setPadding(0); +} + +qreal QQuickTextEdit::topPadding() const +{ + Q_D(const QQuickTextEdit); + if (d->extra.isAllocated() && d->extra->explicitTopPadding) + return d->extra->topPadding; + return d->padding(); +} + +void QQuickTextEdit::setTopPadding(qreal padding) +{ + Q_D(QQuickTextEdit); + d->setTopPadding(padding); +} + +void QQuickTextEdit::resetTopPadding() +{ + Q_D(QQuickTextEdit); + d->setTopPadding(0, true); +} + +qreal QQuickTextEdit::leftPadding() const +{ + Q_D(const QQuickTextEdit); + if (d->extra.isAllocated() && d->extra->explicitLeftPadding) + return d->extra->leftPadding; + return d->padding(); +} + +void QQuickTextEdit::setLeftPadding(qreal padding) +{ + Q_D(QQuickTextEdit); + d->setLeftPadding(padding); +} + +void QQuickTextEdit::resetLeftPadding() +{ + Q_D(QQuickTextEdit); + d->setLeftPadding(0, true); +} + +qreal QQuickTextEdit::rightPadding() const +{ + Q_D(const QQuickTextEdit); + if (d->extra.isAllocated() && d->extra->explicitRightPadding) + return d->extra->rightPadding; + return d->padding(); +} + +void QQuickTextEdit::setRightPadding(qreal padding) +{ + Q_D(QQuickTextEdit); + d->setRightPadding(padding); +} + +void QQuickTextEdit::resetRightPadding() +{ + Q_D(QQuickTextEdit); + d->setRightPadding(0, true); +} + +qreal QQuickTextEdit::bottomPadding() const +{ + Q_D(const QQuickTextEdit); + if (d->extra.isAllocated() && d->extra->explicitBottomPadding) + return d->extra->bottomPadding; + return d->padding(); +} + +void QQuickTextEdit::setBottomPadding(qreal padding) +{ + Q_D(QQuickTextEdit); + d->setBottomPadding(padding); +} + +void QQuickTextEdit::resetBottomPadding() +{ + Q_D(QQuickTextEdit); + d->setBottomPadding(0, true); } QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index bf6763f772..cfe599c0d3 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -47,12 +47,6 @@ class QTextBlock; class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem { Q_OBJECT - Q_ENUMS(VAlignment) - Q_ENUMS(HAlignment) - Q_ENUMS(TextFormat) - Q_ENUMS(WrapMode) - Q_ENUMS(SelectionMode) - Q_ENUMS(RenderType) Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) @@ -91,8 +85,13 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged) Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl RESET resetBaseUrl NOTIFY baseUrlChanged) Q_PROPERTY(RenderType renderType READ renderType WRITE setRenderType NOTIFY renderTypeChanged) - Q_PROPERTY(QQuickTextDocument *textDocument READ textDocument FINAL REVISION 1) + Q_PROPERTY(QQuickTextDocument *textDocument READ textDocument CONSTANT FINAL REVISION 1) Q_PROPERTY(QString hoveredLink READ hoveredLink NOTIFY linkHovered REVISION 2) + Q_PROPERTY(qreal padding READ padding WRITE setPadding RESET resetPadding NOTIFY paddingChanged REVISION 6) + Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding RESET resetTopPadding NOTIFY topPaddingChanged REVISION 6) + Q_PROPERTY(qreal leftPadding READ leftPadding WRITE setLeftPadding RESET resetLeftPadding NOTIFY leftPaddingChanged REVISION 6) + 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) public: QQuickTextEdit(QQuickItem *parent=0); @@ -103,18 +102,21 @@ public: AlignHCenter = Qt::AlignHCenter, AlignJustify = Qt::AlignJustify }; + Q_ENUM(HAlignment) enum VAlignment { AlignTop = Qt::AlignTop, AlignBottom = Qt::AlignBottom, AlignVCenter = Qt::AlignVCenter }; + Q_ENUM(VAlignment) enum TextFormat { PlainText = Qt::PlainText, RichText = Qt::RichText, AutoText = Qt::AutoText }; + Q_ENUM(TextFormat) enum WrapMode { NoWrap = QTextOption::NoWrap, WordWrap = QTextOption::WordWrap, @@ -122,15 +124,18 @@ public: WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere }; + Q_ENUM(WrapMode) enum SelectionMode { SelectCharacters, SelectWords }; + Q_ENUM(SelectionMode) enum RenderType { QtRendering, NativeRendering }; + Q_ENUM(RenderType) QString text() const; void setText(const QString &); @@ -247,6 +252,26 @@ public: Q_REVISION(3) Q_INVOKABLE QString linkAt(qreal x, qreal y) const; + qreal padding() const; + void setPadding(qreal padding); + void resetPadding(); + + qreal topPadding() const; + void setTopPadding(qreal padding); + void resetTopPadding(); + + qreal leftPadding() const; + void setLeftPadding(qreal padding); + void resetLeftPadding(); + + qreal rightPadding() const; + void setRightPadding(qreal padding); + void resetRightPadding(); + + qreal bottomPadding() const; + void setBottomPadding(qreal padding); + void resetBottomPadding(); + Q_SIGNALS: void textChanged(); void contentSizeChanged(); @@ -283,6 +308,12 @@ Q_SIGNALS: void baseUrlChanged(); void inputMethodHintsChanged(); void renderTypeChanged(); + Q_REVISION(6) void editingFinished(); + Q_REVISION(6) void paddingChanged(); + Q_REVISION(6) void topPaddingChanged(); + Q_REVISION(6) void leftPaddingChanged(); + Q_REVISION(6) void rightPaddingChanged(); + Q_REVISION(6) void bottomPaddingChanged(); public Q_SLOTS: void selectAll(); @@ -321,6 +352,8 @@ private: void invalidateFontCaches(); protected: + QQuickTextEdit(QQuickTextEditPrivate &dd, QQuickItem *parent = 0); + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index 0cf0f46532..a763f9e56e 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -47,18 +47,19 @@ #include "qquicktextedit_p.h" #include "qquickimplicitsizeitem_p_p.h" -#include "qquicktextcontrol_p.h" #include <QtQml/qqml.h> #include <QtCore/qlist.h> +#include <private/qlazilyallocated_p.h> QT_BEGIN_NAMESPACE class QTextLayout; class QQuickTextDocumentWithImageResources; class QQuickTextControl; class QQuickTextNode; +class QQuickTextNodeEngine; -class QQuickTextEditPrivate : public QQuickImplicitSizeItemPrivate +class Q_QUICK_PRIVATE_EXPORT QQuickTextEditPrivate : public QQuickImplicitSizeItemPrivate { public: Q_DECLARE_PUBLIC(QQuickTextEdit) @@ -81,10 +82,26 @@ public: }; typedef QList<Node*>::iterator TextNodeIterator; + struct ExtraData { + ExtraData(); + + qreal padding; + qreal topPadding; + qreal leftPadding; + qreal rightPadding; + qreal bottomPadding; + bool explicitTopPadding : 1; + bool explicitLeftPadding : 1; + bool explicitRightPadding : 1; + bool explicitBottomPadding : 1; + }; + QLazilyAllocated<ExtraData> extra; + QQuickTextEditPrivate() : color(QRgb(0xFF000000)), selectionColor(QRgb(0xFF000080)), selectedTextColor(QRgb(0xFFFFFFFF)) - , textMargin(0.0), xoff(0), yoff(0), font(sourceFont), cursorComponent(0), cursorItem(0), document(0), control(0) + , textMargin(0.0), xoff(0), yoff(0) + , font(sourceFont), cursorComponent(0), cursorItem(0), document(0), control(0) , quickDocument(0), lastSelectionStart(0), lastSelectionEnd(0), lineCount(0) , hAlign(QQuickTextEdit::AlignLeft), vAlign(QQuickTextEdit::AlignTop) , format(QQuickTextEdit::PlainText), wrapMode(QQuickTextEdit::NoWrap) @@ -124,13 +141,19 @@ public: void setNativeCursorEnabled(bool) {} void handleFocusEvent(QFocusEvent *event); - void addCurrentTextNodeToRoot(QSGTransformNode *, QQuickTextNode*, TextNodeIterator&, int startPos); + void addCurrentTextNodeToRoot(QQuickTextNodeEngine *, QSGTransformNode *, QQuickTextNode*, TextNodeIterator&, int startPos); QQuickTextNode* createTextNode(); #ifndef QT_NO_IM Qt::InputMethodHints effectiveInputMethodHints() const; #endif + 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); + void setRightPadding(qreal value, bool reset = false); + void setBottomPadding(qreal value, bool reset = false); + QColor color; QColor selectionColor; QColor selectedTextColor; diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 5c67d914a5..c29acf3c83 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -90,6 +90,13 @@ QQuickTextInput::QQuickTextInput(QQuickItem* parent) d->init(); } +QQuickTextInput::QQuickTextInput(QQuickTextInputPrivate &dd, QQuickItem *parent) +: QQuickImplicitSizeItem(dd, parent) +{ + Q_D(QQuickTextInput); + d->init(); +} + QQuickTextInput::~QQuickTextInput() { } @@ -222,6 +229,16 @@ QString QQuickTextInputPrivate::realText() const */ /*! + \qmlproperty string QtQuick::TextInput::font.styleName + \since 5.6 + + Sets the style name of the font. + + The style name is case insensitive. If set, the font will be matched against style name instead + of the font properties \l weight, \l bold and \l italic. +*/ + +/*! \qmlproperty bool QtQuick::TextInput::font.bold Sets whether the font weight is bold. @@ -786,8 +803,8 @@ QRectF QQuickTextInput::cursorRectangle() const QTextLine l = d->m_textLayout.lineForTextPosition(c); if (!l.isValid()) return QRectF(); - qreal x = l.cursorToX(c) - d->hscroll; - qreal y = l.y() - d->vscroll; + qreal x = l.cursorToX(c) - d->hscroll + leftPadding(); + qreal y = l.y() - d->vscroll + topPadding(); return QRectF(x, y, 1, l.height()); } @@ -910,189 +927,6 @@ void QQuickTextInput::setAutoScroll(bool b) emit autoScrollChanged(d->autoScroll); } -#ifndef QT_NO_VALIDATOR - -/*! - \qmltype IntValidator - \instantiates QIntValidator - \inqmlmodule QtQuick - \ingroup qtquick-text-utility - \brief Defines a validator for integer values - - The IntValidator type provides a validator for integer values. - - If no \l locale is set IntValidator uses the \l {QLocale::setDefault()}{default locale} to - interpret the number and will accept locale specific digits, group separators, and positive - and negative signs. In addition, IntValidator is always guaranteed to accept a number - formatted according to the "C" locale. -*/ - - -QQuickIntValidator::QQuickIntValidator(QObject *parent) - : QIntValidator(parent) -{ -} - -/*! - \qmlproperty string QtQuick::IntValidator::locale - - This property holds the name of the locale used to interpret the number. - - \sa {QtQml::Qt::locale()}{Qt.locale()} -*/ - -QString QQuickIntValidator::localeName() const -{ - return locale().name(); -} - -void QQuickIntValidator::setLocaleName(const QString &name) -{ - if (locale().name() != name) { - setLocale(QLocale(name)); - emit localeNameChanged(); - } -} - -void QQuickIntValidator::resetLocaleName() -{ - QLocale defaultLocale; - if (locale() != defaultLocale) { - setLocale(defaultLocale); - emit localeNameChanged(); - } -} - -/*! - \qmlproperty int QtQuick::IntValidator::top - - This property holds the validator's highest acceptable value. - By default, this property's value is derived from the highest signed integer available (typically 2147483647). -*/ -/*! - \qmlproperty int QtQuick::IntValidator::bottom - - This property holds the validator's lowest acceptable value. - By default, this property's value is derived from the lowest signed integer available (typically -2147483647). -*/ - -/*! - \qmltype DoubleValidator - \instantiates QDoubleValidator - \inqmlmodule QtQuick - \ingroup qtquick-text-utility - \brief Defines a validator for non-integer numbers - - The DoubleValidator type provides a validator for non-integer numbers. - - Input is accepted if it contains a double that is within the valid range - and is in the correct format. - - Input is accepected but invalid if it contains a double that is outside - the range or is in the wrong format; e.g. with too many digits after the - decimal point or is empty. - - Input is rejected if it is not a double. - - Note: If the valid range consists of just positive doubles (e.g. 0.0 to - 100.0) and input is a negative double then it is rejected. If \l notation - is set to DoubleValidator.StandardNotation, and the input contains more - digits before the decimal point than a double in the valid range may have, - it is also rejected. If \l notation is DoubleValidator.ScientificNotation, - and the input is not in the valid range, it is accecpted but invalid. The - value may yet become valid by changing the exponent. -*/ - -QQuickDoubleValidator::QQuickDoubleValidator(QObject *parent) - : QDoubleValidator(parent) -{ -} - -/*! - \qmlproperty string QtQuick::DoubleValidator::locale - - This property holds the name of the locale used to interpret the number. - - \sa {QtQml::Qt::locale()}{Qt.locale()} -*/ - -QString QQuickDoubleValidator::localeName() const -{ - return locale().name(); -} - -void QQuickDoubleValidator::setLocaleName(const QString &name) -{ - if (locale().name() != name) { - setLocale(QLocale(name)); - emit localeNameChanged(); - } -} - -void QQuickDoubleValidator::resetLocaleName() -{ - QLocale defaultLocale; - if (locale() != defaultLocale) { - setLocale(defaultLocale); - emit localeNameChanged(); - } -} - -#endif // QT_NO_VALIDATOR - -/*! - \qmlproperty real QtQuick::DoubleValidator::top - - This property holds the validator's maximum acceptable value. - By default, this property contains a value of infinity. -*/ -/*! - \qmlproperty real QtQuick::DoubleValidator::bottom - - This property holds the validator's minimum acceptable value. - By default, this property contains a value of -infinity. -*/ -/*! - \qmlproperty int QtQuick::DoubleValidator::decimals - - This property holds the validator's maximum number of digits after the decimal point. - By default, this property contains a value of 1000. -*/ -/*! - \qmlproperty enumeration QtQuick::DoubleValidator::notation - This property holds the notation of how a string can describe a number. - - The possible values for this property are: - - \list - \li DoubleValidator.StandardNotation - \li DoubleValidator.ScientificNotation (default) - \endlist - - If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2). -*/ - -/*! - \qmltype RegExpValidator - \instantiates QRegExpValidator - \inqmlmodule QtQuick - \ingroup qtquick-text-utility - \brief Provides a string validator - - The RegExpValidator type provides a validator, which counts as valid any string which - matches a specified regular expression. -*/ -/*! - \qmlproperty regExp QtQuick::RegExpValidator::regExp - - This property holds the regular expression used for validation. - - Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular expression - matching "a". - - By default, this property contains a regular expression with the pattern .* that matches any string. -*/ - /*! \qmlproperty Validator QtQuick::TextInput::validator @@ -1489,8 +1323,9 @@ void QQuickTextInput::positionAt(QQmlV4Function *args) const int QQuickTextInputPrivate::positionAt(qreal x, qreal y, QTextLine::CursorPosition position) const { - x += hscroll; - y += vscroll; + Q_Q(const QQuickTextInput); + x += hscroll - q->leftPadding(); + y += vscroll - q->topPadding(); QTextLine line = m_textLayout.lineAt(0); for (int i = 1; i < m_textLayout.lineCount(); ++i) { QTextLine nextLine = m_textLayout.lineAt(i); @@ -1748,7 +1583,7 @@ void QQuickTextInputPrivate::ensureVisible(int position, int preeditCursor, int { Q_Q(QQuickTextInput); QTextLine textLine = m_textLayout.lineForTextPosition(position + preeditCursor); - const qreal width = qMax<qreal>(0, q->width()); + const qreal width = qMax<qreal>(0, q->width() - q->leftPadding() - q->rightPadding()); qreal cix = 0; qreal widthUsed = 0; if (textLine.isValid()) { @@ -1811,7 +1646,7 @@ void QQuickTextInputPrivate::updateVerticalScroll() #ifndef QT_NO_IM const int preeditLength = m_textLayout.preeditAreaText().length(); #endif - const qreal height = qMax<qreal>(0, q->height()); + const qreal height = qMax<qreal>(0, q->height() - q->topPadding() - q->bottomPadding()); qreal heightUsed = contentSize.height(); qreal previousScroll = vscroll; @@ -1879,14 +1714,15 @@ void QQuickTextInput::invalidateFontCaches() void QQuickTextInput::ensureActiveFocus() { - Q_D(QQuickTextInput); - bool hadActiveFocus = hasActiveFocus(); forceActiveFocus(); #ifndef QT_NO_IM + Q_D(QQuickTextInput); // re-open input panel on press if already focused if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly) qGuiApp->inputMethod()->show(); +#else + Q_UNUSED(hadActiveFocus); #endif } @@ -1920,13 +1756,13 @@ QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData node->deleteContent(); node->setMatrix(QMatrix4x4()); - QPointF offset(0, 0); + QPointF offset(leftPadding(), topPadding()); if (d->autoScroll && d->m_textLayout.lineCount() > 0) { QFontMetricsF fm(d->font); // the y offset is there to keep the baseline constant in case we have script changes in the text. - offset = -QPointF(d->hscroll, d->vscroll + d->m_textLayout.lineAt(0).ascent() - fm.ascent()); + offset += -QPointF(d->hscroll, d->vscroll + d->m_textLayout.lineAt(0).ascent() - fm.ascent()); } else { - offset = -QPointF(d->hscroll, d->vscroll); + offset += -QPointF(d->hscroll, d->vscroll); } if (!d->m_textLayout.text().isEmpty() @@ -2672,6 +2508,19 @@ bool QQuickTextInput::isInputMethodComposing() const #endif } +QQuickTextInputPrivate::ExtraData::ExtraData() + : padding(0) + , topPadding(0) + , leftPadding(0) + , rightPadding(0) + , bottomPadding(0) + , explicitTopPadding(false) + , explicitLeftPadding(false) + , explicitRightPadding(false) + , explicitBottomPadding(false) +{ +} + void QQuickTextInputPrivate::init() { Q_Q(QQuickTextInput); @@ -2882,7 +2731,7 @@ qreal QQuickTextInputPrivate::getImplicitWidth() const QTextLine line = layout.createLine(); line.setLineWidth(INT_MAX); - d->implicitWidth = qCeil(line.naturalTextWidth()); + d->implicitWidth = qCeil(line.naturalTextWidth()) + q->leftPadding() + q->rightPadding(); layout.endLayout(); } @@ -2890,6 +2739,62 @@ qreal QQuickTextInputPrivate::getImplicitWidth() const return implicitWidth; } +void QQuickTextInputPrivate::setTopPadding(qreal value, bool reset) +{ + Q_Q(QQuickTextInput); + qreal oldPadding = q->topPadding(); + if (!reset || extra.isAllocated()) { + extra.value().topPadding = value; + extra.value().explicitTopPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + updateLayout(); + emit q->topPaddingChanged(); + } +} + +void QQuickTextInputPrivate::setLeftPadding(qreal value, bool reset) +{ + Q_Q(QQuickTextInput); + qreal oldPadding = q->leftPadding(); + if (!reset || extra.isAllocated()) { + extra.value().leftPadding = value; + extra.value().explicitLeftPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + updateLayout(); + emit q->leftPaddingChanged(); + } +} + +void QQuickTextInputPrivate::setRightPadding(qreal value, bool reset) +{ + Q_Q(QQuickTextInput); + qreal oldPadding = q->rightPadding(); + if (!reset || extra.isAllocated()) { + extra.value().rightPadding = value; + extra.value().explicitRightPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + updateLayout(); + emit q->rightPaddingChanged(); + } +} + +void QQuickTextInputPrivate::setBottomPadding(qreal value, bool reset) +{ + Q_Q(QQuickTextInput); + qreal oldPadding = q->bottomPadding(); + if (!reset || extra.isAllocated()) { + extra.value().bottomPadding = value; + extra.value().explicitBottomPadding = !reset; + } + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) { + updateLayout(); + emit q->bottomPaddingChanged(); + } +} + void QQuickTextInputPrivate::updateLayout() { Q_Q(QQuickTextInput); @@ -2915,12 +2820,12 @@ void QQuickTextInputPrivate::updateLayout() line.setLineWidth(INT_MAX); const bool wasInLayout = inLayout; inLayout = true; - q->setImplicitWidth(qCeil(line.naturalTextWidth())); + q->setImplicitWidth(qCeil(line.naturalTextWidth()) + q->leftPadding() + q->rightPadding()); inLayout = wasInLayout; if (inLayout) // probably the result of a binding loop, but by letting it return; // get this far we'll get a warning to that effect. } - qreal lineWidth = q->widthValid() ? q->width() : INT_MAX; + qreal lineWidth = q->widthValid() ? q->width() - q->leftPadding() - q->rightPadding() : INT_MAX; qreal height = 0; qreal width = 0; do { @@ -2947,9 +2852,9 @@ void QQuickTextInputPrivate::updateLayout() q->update(); if (!requireImplicitWidth && !q->widthValid()) - q->setImplicitSize(width, height); + q->setImplicitSize(width + q->leftPadding() + q->rightPadding(), height + q->topPadding() + q->bottomPadding()); else - q->setImplicitHeight(height); + q->setImplicitHeight(height + q->topPadding() + q->bottomPadding()); updateBaselineOffset(); @@ -2971,13 +2876,13 @@ void QQuickTextInputPrivate::updateBaselineOffset() QFontMetricsF fm(font); qreal yoff = 0; if (q->heightValid()) { - const qreal surplusHeight = q->height() - contentSize.height(); + const qreal surplusHeight = q->height() - contentSize.height() - q->topPadding() - q->bottomPadding(); if (vAlign == QQuickTextInput::AlignBottom) yoff = surplusHeight; else if (vAlign == QQuickTextInput::AlignVCenter) yoff = surplusHeight/2; } - q->setBaselineOffset(fm.ascent() + yoff); + q->setBaselineOffset(fm.ascent() + yoff + q->topPadding()); } #ifndef QT_NO_CLIPBOARD @@ -3345,7 +3250,7 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) m_preeditCursor = event->preeditString().length(); hasImState = !event->preeditString().isEmpty(); bool cursorVisible = true; - QList<QTextLayout::FormatRange> formats; + QVector<QTextLayout::FormatRange> formats; for (int i = 0; i < event->attributes().size(); ++i) { const QInputMethodEvent::Attribute &a = event->attributes().at(i); if (a.type == QInputMethodEvent::Cursor) { @@ -3364,7 +3269,7 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) } } } - m_textLayout.setAdditionalFormats(formats); + m_textLayout.setFormats(formats); updateDisplayText(/*force*/ true); if ((cursorPositionChanged && !emitCursorPositionChanged()) @@ -4273,6 +4178,21 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event) return; } + if (m_blinkPeriod > 0) { + if (m_blinkTimer) + q->killTimer(m_blinkTimer); + + m_blinkTimer = q->startTimer(m_blinkPeriod / 2); + + if (m_blinkStatus == 0) { + m_blinkStatus = 1; + + updateType = UpdatePaintNode; + q->polish(); + q->update(); + } + } + if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing && !m_readOnly @@ -4508,5 +4428,126 @@ void QQuickTextInput::ensureVisible(int position) updateCursorRectangle(false); } +/*! + \since 5.6 + \qmlproperty real QtQuick::TextInput::padding + \qmlproperty real QtQuick::TextInput::topPadding + \qmlproperty real QtQuick::TextInput::leftPadding + \qmlproperty real QtQuick::TextInput::bottomPadding + \qmlproperty real QtQuick::TextInput::rightPadding + + These properties hold the padding around the content. This space is reserved + in addition to the contentWidth and contentHeight. +*/ +qreal QQuickTextInput::padding() const +{ + Q_D(const QQuickTextInput); + return d->padding(); +} + +void QQuickTextInput::setPadding(qreal padding) +{ + Q_D(QQuickTextInput); + if (qFuzzyCompare(d->padding(), padding)) + return; + + d->extra.value().padding = padding; + d->updateLayout(); + emit paddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitTopPadding) + emit topPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding) + emit leftPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitRightPadding) + emit rightPaddingChanged(); + if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding) + emit bottomPaddingChanged(); +} + +void QQuickTextInput::resetPadding() +{ + setPadding(0); +} + +qreal QQuickTextInput::topPadding() const +{ + Q_D(const QQuickTextInput); + if (d->extra.isAllocated() && d->extra->explicitTopPadding) + return d->extra->topPadding; + return d->padding(); +} + +void QQuickTextInput::setTopPadding(qreal padding) +{ + Q_D(QQuickTextInput); + d->setTopPadding(padding); +} + +void QQuickTextInput::resetTopPadding() +{ + Q_D(QQuickTextInput); + d->setTopPadding(0, true); +} + +qreal QQuickTextInput::leftPadding() const +{ + Q_D(const QQuickTextInput); + if (d->extra.isAllocated() && d->extra->explicitLeftPadding) + return d->extra->leftPadding; + return d->padding(); +} + +void QQuickTextInput::setLeftPadding(qreal padding) +{ + Q_D(QQuickTextInput); + d->setLeftPadding(padding); +} + +void QQuickTextInput::resetLeftPadding() +{ + Q_D(QQuickTextInput); + d->setLeftPadding(0, true); +} + +qreal QQuickTextInput::rightPadding() const +{ + Q_D(const QQuickTextInput); + if (d->extra.isAllocated() && d->extra->explicitRightPadding) + return d->extra->rightPadding; + return d->padding(); +} + +void QQuickTextInput::setRightPadding(qreal padding) +{ + Q_D(QQuickTextInput); + d->setRightPadding(padding); +} + +void QQuickTextInput::resetRightPadding() +{ + Q_D(QQuickTextInput); + d->setRightPadding(0, true); +} + +qreal QQuickTextInput::bottomPadding() const +{ + Q_D(const QQuickTextInput); + if (d->extra.isAllocated() && d->extra->explicitBottomPadding) + return d->extra->bottomPadding; + return d->padding(); +} + +void QQuickTextInput::setBottomPadding(qreal padding) +{ + Q_D(QQuickTextInput); + d->setBottomPadding(padding); +} + +void QQuickTextInput::resetBottomPadding() +{ + Q_D(QQuickTextInput); + d->setBottomPadding(0, true); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index 3bcbe0fa25..b91149f5f3 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -45,13 +45,6 @@ class QValidator; class Q_QUICK_PRIVATE_EXPORT QQuickTextInput : public QQuickImplicitSizeItem { Q_OBJECT - Q_ENUMS(HAlignment) - Q_ENUMS(VAlignment) - Q_ENUMS(WrapMode) - Q_ENUMS(EchoMode) - Q_ENUMS(SelectionMode) - Q_ENUMS(CursorPosition) - Q_ENUMS(RenderType) Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) Q_PROPERTY(int length READ length NOTIFY textChanged) @@ -96,6 +89,12 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextInput : public QQuickImplicitSizeItem Q_PROPERTY(qreal contentHeight READ contentHeight NOTIFY contentSizeChanged) Q_PROPERTY(RenderType renderType READ renderType WRITE setRenderType NOTIFY renderTypeChanged) + Q_PROPERTY(qreal padding READ padding WRITE setPadding RESET resetPadding NOTIFY paddingChanged REVISION 6) + Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding RESET resetTopPadding NOTIFY topPaddingChanged REVISION 6) + Q_PROPERTY(qreal leftPadding READ leftPadding WRITE setLeftPadding RESET resetLeftPadding NOTIFY leftPaddingChanged REVISION 6) + 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) + public: QQuickTextInput(QQuickItem * parent=0); ~QQuickTextInput(); @@ -108,18 +107,21 @@ public: Password, PasswordEchoOnEdit }; + Q_ENUM(EchoMode) enum HAlignment { AlignLeft = Qt::AlignLeft, AlignRight = Qt::AlignRight, AlignHCenter = Qt::AlignHCenter }; + Q_ENUM(HAlignment) enum VAlignment { AlignTop = Qt::AlignTop, AlignBottom = Qt::AlignBottom, AlignVCenter = Qt::AlignVCenter }; + Q_ENUM(VAlignment) enum WrapMode { NoWrap = QTextOption::NoWrap, @@ -128,20 +130,24 @@ public: WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere }; + Q_ENUM(WrapMode) enum SelectionMode { SelectCharacters, SelectWords }; + Q_ENUM(SelectionMode) enum CursorPosition { CursorBetweenCharacters, CursorOnCharacter }; + Q_ENUM(CursorPosition) enum RenderType { QtRendering, NativeRendering }; + Q_ENUM(RenderType) //Auxilliary functions needed to control the TextInput from QML Q_INVOKABLE void positionAt(QQmlV4Function *args) const; @@ -260,6 +266,26 @@ public: qreal contentWidth() const; qreal contentHeight() const; + qreal padding() const; + void setPadding(qreal padding); + void resetPadding(); + + qreal topPadding() const; + void setTopPadding(qreal padding); + void resetTopPadding(); + + qreal leftPadding() const; + void setLeftPadding(qreal padding); + void resetLeftPadding(); + + qreal rightPadding() const; + void setRightPadding(qreal padding); + void resetRightPadding(); + + qreal bottomPadding() const; + void setBottomPadding(qreal padding); + void resetBottomPadding(); + Q_SIGNALS: void textChanged(); void cursorPositionChanged(); @@ -300,12 +326,19 @@ Q_SIGNALS: void contentSizeChanged(); void inputMethodHintsChanged(); void renderTypeChanged(); + Q_REVISION(6) void paddingChanged(); + Q_REVISION(6) void topPaddingChanged(); + Q_REVISION(6) void leftPaddingChanged(); + Q_REVISION(6) void rightPaddingChanged(); + Q_REVISION(6) void bottomPaddingChanged(); private: void invalidateFontCaches(); void ensureActiveFocus(); protected: + QQuickTextInput(QQuickTextInputPrivate &dd, QQuickItem *parent = 0); + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; @@ -360,46 +393,8 @@ private: Q_DECLARE_PRIVATE(QQuickTextInput) }; -#ifndef QT_NO_VALIDATOR -class Q_AUTOTEST_EXPORT QQuickIntValidator : public QIntValidator -{ - Q_OBJECT - Q_PROPERTY(QString locale READ localeName WRITE setLocaleName RESET resetLocaleName NOTIFY localeNameChanged) -public: - QQuickIntValidator(QObject *parent = 0); - - QString localeName() const; - void setLocaleName(const QString &name); - void resetLocaleName(); - -Q_SIGNALS: - void localeNameChanged(); -}; - -class Q_AUTOTEST_EXPORT QQuickDoubleValidator : public QDoubleValidator -{ - Q_OBJECT - Q_PROPERTY(QString locale READ localeName WRITE setLocaleName RESET resetLocaleName NOTIFY localeNameChanged) -public: - QQuickDoubleValidator(QObject *parent = 0); - - QString localeName() const; - void setLocaleName(const QString &name); - void resetLocaleName(); - -Q_SIGNALS: - void localeNameChanged(); -}; -#endif - QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickTextInput) -#ifndef QT_NO_VALIDATOR -QML_DECLARE_TYPE(QValidator) -QML_DECLARE_TYPE(QQuickIntValidator) -QML_DECLARE_TYPE(QQuickDoubleValidator) -QML_DECLARE_TYPE(QRegExpValidator) -#endif #endif // QQUICKTEXTINPUT_P_H diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 3038573bb3..cf0a6f5273 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -47,6 +47,7 @@ #include <QtGui/qpalette.h> #include <QtGui/qtextlayout.h> #include <QtGui/qstylehints.h> +#include <private/qlazilyallocated_p.h> #include "qplatformdefs.h" @@ -64,13 +65,28 @@ QT_BEGIN_NAMESPACE class QQuickTextNode; -class Q_AUTOTEST_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPrivate +class Q_QUICK_PRIVATE_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPrivate { public: Q_DECLARE_PUBLIC(QQuickTextInput) typedef QQuickTextInput Public; + struct ExtraData { + ExtraData(); + + qreal padding; + qreal topPadding; + qreal leftPadding; + qreal rightPadding; + qreal bottomPadding; + bool explicitTopPadding : 1; + bool explicitLeftPadding : 1; + bool explicitRightPadding : 1; + bool explicitBottomPadding : 1; + }; + QLazilyAllocated<ExtraData> extra; + QQuickTextInputPrivate() : hscroll(0) , vscroll(0) @@ -420,6 +436,12 @@ public: qreal getImplicitWidth() const Q_DECL_OVERRIDE; + 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); + void setRightPadding(qreal value, bool reset = false); + void setBottomPadding(qreal value, bool reset = false); + private: void removeSelectedText(); void internalSetText(const QString &txt, int pos = -1, bool edited = true); diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp index 010a443d18..d40dedd798 100644 --- a/src/quick/items/qquicktextnode.cpp +++ b/src/quick/items/qquicktextnode.cpp @@ -186,17 +186,6 @@ void QQuickTextNode::clearCursor() m_cursorNode = 0; } -void QQuickTextNode::initEngine(const QColor& textColor, const QColor& selectedTextColor, const QColor& selectionColor, const QColor& anchorColor, const QPointF &position) -{ - m_engine.reset(new QQuickTextNodeEngine); - m_engine->m_hasContents = false; - m_engine->setTextColor(textColor); - m_engine->setSelectedTextColor(selectedTextColor); - m_engine->setSelectionColor(selectionColor); - m_engine->setAnchorColor(anchorColor); - m_engine->setPosition(position); -} - void QQuickTextNode::addRectangleNode(const QRectF &rect, const QColor &color) { QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext(); @@ -224,7 +213,12 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex const QColor &selectionColor, const QColor &selectedTextColor, int selectionStart, int selectionEnd) { - initEngine(textColor, selectedTextColor, selectionColor, anchorColor); + QQuickTextNodeEngine engine; + engine.setTextColor(textColor); + engine.setSelectedTextColor(selectedTextColor); + engine.setSelectionColor(selectionColor); + engine.setAnchorColor(anchorColor); + engine.setPosition(position); QList<QTextFrame *> frames; frames.append(textDocument->rootFrame()); @@ -232,7 +226,7 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex QTextFrame *textFrame = frames.takeFirst(); frames.append(textFrame->childFrames()); - m_engine->addFrameDecorations(textDocument, textFrame); + engine.addFrameDecorations(textDocument, textFrame); if (textFrame->firstPosition() > textFrame->lastPosition() && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) { @@ -242,23 +236,23 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex QRectF rect = a->frameBoundingRect(textFrame); QTextBlock block = textFrame->firstCursorPosition().block(); - m_engine->setCurrentLine(block.layout()->lineForTextPosition(pos - block.position())); - m_engine->addTextObject(rect.topLeft(), format, QQuickTextNodeEngine::Unselected, textDocument, + engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position())); + engine.addTextObject(rect.topLeft(), format, QQuickTextNodeEngine::Unselected, textDocument, pos, textFrame->frameFormat().position()); } else { QTextFrame::iterator it = textFrame->begin(); while (!it.atEnd()) { - Q_ASSERT(!m_engine->currentLine().isValid()); + Q_ASSERT(!engine.currentLine().isValid()); QTextBlock block = it.currentBlock(); - m_engine->addTextBlock(textDocument, block, position, textColor, anchorColor, selectionStart, selectionEnd); + engine.addTextBlock(textDocument, block, position, textColor, anchorColor, selectionStart, selectionEnd); ++it; } } } - m_engine->addToSceneGraph(this, style, styleColor); + engine.addToSceneGraph(this, style, styleColor); } void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color, @@ -268,7 +262,12 @@ void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLay int selectionStart, int selectionEnd, int lineStart, int lineCount) { - initEngine(color, selectedTextColor, selectionColor, anchorColor, position); + QQuickTextNodeEngine engine; + engine.setTextColor(color); + engine.setSelectedTextColor(selectedTextColor); + engine.setSelectionColor(selectionColor); + engine.setAnchorColor(anchorColor); + engine.setPosition(position); #ifndef QT_NO_IM int preeditLength = textLayout->preeditAreaText().length(); @@ -276,7 +275,7 @@ void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLay #endif QVarLengthArray<QTextLayout::FormatRange> colorChanges; - m_engine->mergeFormats(textLayout, &colorChanges); + engine.mergeFormats(textLayout, &colorChanges); lineCount = lineCount >= 0 ? qMin(lineStart + lineCount, textLayout->lineCount()) @@ -297,11 +296,11 @@ void QQuickTextNode::addTextLayout(const QPointF &position, QTextLayout *textLay } #endif - m_engine->setCurrentLine(line); - m_engine->addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd); + engine.setCurrentLine(line); + engine.addGlyphsForRanges(colorChanges, start, end, selectionStart, selectionEnd); } - m_engine->addToSceneGraph(this, style, styleColor); + engine.addToSceneGraph(this, style, styleColor); } void QQuickTextNode::deleteContent() diff --git a/src/quick/items/qquicktextnode_p.h b/src/quick/items/qquicktextnode_p.h index c7b9804ea6..31cc23bf2a 100644 --- a/src/quick/items/qquicktextnode_p.h +++ b/src/quick/items/qquicktextnode_p.h @@ -101,14 +101,10 @@ public: void setUseNativeRenderer(bool on) { m_useNativeRenderer = on; } private: - void initEngine(const QColor &textColor, const QColor &selectedTextColor, const QColor &selectionColor, const QColor& anchorColor = QColor() - , const QPointF &position = QPointF()); - QSGRectangleNode *m_cursorNode; QList<QSGTexture *> m_textures; QQuickItem *m_ownerElement; bool m_useNativeRenderer; - QScopedPointer<QQuickTextNodeEngine> m_engine; friend class QQuickTextEdit; friend class QQuickTextEditPrivate; diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index efe79b382e..2b7f94d8bf 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -882,7 +882,7 @@ void QQuickTextNodeEngine::mergeFormats(QTextLayout *textLayout, QVarLengthArray if (textLayout == 0) return; - QList<QTextLayout::FormatRange> additionalFormats = textLayout->additionalFormats(); + QVector<QTextLayout::FormatRange> additionalFormats = textLayout->formats(); for (int i=0; i<additionalFormats.size(); ++i) { QTextLayout::FormatRange additionalFormat = additionalFormats.at(i); if (additionalFormat.format.hasProperty(QTextFormat::ForegroundBrush) diff --git a/src/quick/items/qquicktextnodeengine_p.h b/src/quick/items/qquicktextnodeengine_p.h index 2f6cacf601..f9ebe43183 100644 --- a/src/quick/items/qquicktextnodeengine_p.h +++ b/src/quick/items/qquicktextnodeengine_p.h @@ -231,8 +231,6 @@ private: QList<TextDecoration> m_lines; QVector<BinaryTreeNode> m_processedNodes; - QList<QPair<QRectF, QImage> > m_images; - bool m_hasSelection : 1; bool m_hasContents : 1; friend class QQuickTextNode; diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp index 5b5413a4ba..0b3cfa17b5 100644 --- a/src/quick/items/qquickview.cpp +++ b/src/quick/items/qquickview.cpp @@ -38,8 +38,9 @@ #include "qquickitem_p.h" #include "qquickitemchangelistener_p.h" +#include <private/qqmldebugconnector_p.h> #include <private/qquickprofiler_p.h> -#include <private/qqmlinspectorservice_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmlmemoryprofiler_p.h> #include <QtQml/qqmlengine.h> @@ -86,8 +87,9 @@ void QQuickViewPrivate::init(QQmlEngine* e) rootItemMarker.set(v4, v); } - if (QQmlDebugService::isDebuggingEnabled()) - QQmlInspectorService::instance()->addView(q); + QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>(); + if (service) + service->addView(q); } QQuickViewPrivate::QQuickViewPrivate() @@ -97,8 +99,9 @@ QQuickViewPrivate::QQuickViewPrivate() QQuickViewPrivate::~QQuickViewPrivate() { - if (QQmlDebugService::isDebuggingEnabled()) - QQmlInspectorService::instance()->removeView(q_func()); + QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>(); + if (service) + service->removeView(q_func()); } void QQuickViewPrivate::execute() @@ -345,6 +348,9 @@ QQuickView::Status QQuickView::status() const if (!d->component) return QQuickView::Null; + if (d->component->status() == QQmlComponent::Ready && !d->root) + return QQuickView::Error; + return QQuickView::Status(d->component->status()); } @@ -364,6 +370,10 @@ QList<QQmlError> QQuickView::errors() const QQmlError error; error.setDescription(QLatin1String("QQuickView: invalid qml engine.")); errs << error; + } else if (d->component->status() == QQmlComponent::Ready && !d->root) { + QQmlError error; + error.setDescription(QLatin1String("QQuickView: invalid root object.")); + errs << error; } return errs; @@ -501,14 +511,15 @@ void QQuickViewPrivate::setRootObject(QObject *obj) if (QQuickItem *sgItem = qobject_cast<QQuickItem *>(obj)) { root = sgItem; sgItem->setParentItem(q->QQuickWindow::contentItem()); + } else if (qobject_cast<QWindow *>(obj)) { + qWarning() << "QQuickView does not support using windows as a root item." << endl + << endl + << "If you wish to create your root window from QML, consider using QQmlApplicationEngine instead." << endl; } else { qWarning() << "QQuickView only supports loading of root objects that derive from QQuickItem." << endl << endl - << "If your example is using QML 2, (such as qmlscene) and the .qml file you" << endl - << "loaded has 'import QtQuick 1.0' or 'import Qt 4.7', this error will occur." << endl - << endl - << "To load files with 'import QtQuick 1.0' or 'import Qt 4.7', use the" << endl - << "QDeclarativeView class in the Qt Quick 1 module." << endl; + << "Ensure your QML code is written for QtQuick 2, and uses a root that is or" << endl + << "inherits from QtQuick's Item (not a Timer, QtObject, etc)." << endl; delete obj; root = 0; } diff --git a/src/quick/items/qquickview.h b/src/quick/items/qquickview.h index f094c5a216..80da0ba4f1 100644 --- a/src/quick/items/qquickview.h +++ b/src/quick/items/qquickview.h @@ -53,7 +53,6 @@ class Q_QUICK_EXPORT QQuickView : public QQuickWindow Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QUrl source READ source WRITE setSource DESIGNABLE true) - Q_ENUMS(ResizeMode Status) public: explicit QQuickView(QWindow *parent = 0); QQuickView(QQmlEngine* engine, QWindow *parent); @@ -68,10 +67,12 @@ public: QQuickItem *rootObject() const; enum ResizeMode { SizeViewToRootObject, SizeRootObjectToView }; + Q_ENUM(ResizeMode) ResizeMode resizeMode() const; void setResizeMode(ResizeMode); enum Status { Null, Ready, Loading, Error }; + Q_ENUM(Status) Status status() const; QList<QQmlError> errors() const; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 5958edf29f..99dbf1d83e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -64,7 +64,6 @@ #include <QtQuick/private/qquickpixmapcache_p.h> -#include <private/qqmlprofilerservice_p.h> #include <private/qqmlmemoryprofiler_p.h> #include <private/qopenglvertexarrayobject_p.h> @@ -83,8 +82,8 @@ bool QQuickWindowPrivate::defaultAlphaBuffer = false; void QQuickWindowPrivate::updateFocusItemTransform() { - Q_Q(QQuickWindow); #ifndef QT_NO_IM + Q_Q(QQuickWindow); QQuickItem *focus = q->activeFocusItem(); if (focus && QGuiApplication::focusObject() == focus) { QQuickItemPrivate *focusPrivate = QQuickItemPrivate::get(focus); @@ -259,8 +258,7 @@ void QQuickWindowPrivate::polishItems() // the user. int recursionSafeguard = INT_MAX; while (!itemsToPolish.isEmpty() && --recursionSafeguard > 0) { - QQuickItem *item = *itemsToPolish.begin(); - itemsToPolish.remove(item); + QQuickItem *item = itemsToPolish.takeLast(); QQuickItemPrivate::get(item)->polishScheduled = false; item->updatePolish(); } @@ -2385,7 +2383,9 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem if (target->childMouseEventFilter(item, targetEvent.data())) { qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target; QVector<int> touchIds; - for (int i = 0; i < targetEvent->touchPoints().size(); ++i) + const int touchPointCount = targetEvent->touchPoints().size(); + touchIds.reserve(touchPointCount); + for (int i = 0; i < touchPointCount; ++i) touchIds.append(targetEvent->touchPoints().at(i).id()); target->grabTouchPoints(touchIds); if (mouseGrabberItem) { @@ -2729,7 +2729,7 @@ static inline QSGNode *qquickitem_before_paintNode(QQuickItemPrivate *d) QQuickItem *before = 0; for (int i=0; i<childItems.size(); ++i) { QQuickItemPrivate *dd = QQuickItemPrivate::get(childItems.at(i)); - // Perform the same check as the in buildOrderNodeList below. + // Perform the same check as the in fetchNextNode below. if (dd->z() < 0 && (dd->explicitVisible || (dd->extra.isAllocated() && dd->extra->effectRefCount))) before = childItems.at(i); else @@ -2738,13 +2738,9 @@ static inline QSGNode *qquickitem_before_paintNode(QQuickItemPrivate *d) return Q_UNLIKELY(before) ? QQuickItemPrivate::get(before)->itemNode() : 0; } -static QVector<QSGNode *> buildOrderedNodeList(QQuickItemPrivate *itemPriv) +static QSGNode *fetchNextNode(QQuickItemPrivate *itemPriv, int &ii, bool &returnedPaintNode) { QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems(); - QVector<QSGNode *> desiredNodes; - desiredNodes.reserve(orderedChildren.size() + 1); // + 1 for the paintNode - - int ii = 0; for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) { QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii)); @@ -2752,11 +2748,14 @@ static QVector<QSGNode *> buildOrderedNodeList(QQuickItemPrivate *itemPriv) (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount)) continue; - desiredNodes.append(childPrivate->itemNode()); + ii++; + return childPrivate->itemNode(); } - if (itemPriv->paintNode) - desiredNodes.append(itemPriv->paintNode); + if (itemPriv->paintNode && !returnedPaintNode) { + returnedPaintNode = true; + return itemPriv->paintNode; + } for (; ii < orderedChildren.count(); ++ii) { QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii)); @@ -2764,10 +2763,11 @@ static QVector<QSGNode *> buildOrderedNodeList(QQuickItemPrivate *itemPriv) (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount)) continue; - desiredNodes.append(childPrivate->itemNode()); + ii++; + return childPrivate->itemNode(); } - return desiredNodes; + return 0; } void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) @@ -2870,7 +2870,10 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) } if (dirty & QQuickItemPrivate::ChildrenUpdateMask) { - QVector<QSGNode *> desiredNodes = buildOrderedNodeList(itemPriv); + int ii = 0; + bool fetchedPaintNode = false; + QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems(); + int desiredNodesSize = orderedChildren.size() + (itemPriv->paintNode ? 1 : 0); // now start making current state match the promised land of // desiredNodes. in the case of our current state matching desiredNodes @@ -2888,14 +2891,9 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) int added = 0; int removed = 0; int replaced = 0; -#if defined(CHILDRENUPDATE_DEBUG) - // This is slow! Do not do this in a normal/profiling build! - int initialCount = groupNode->childCount(); -#endif - - while (currentNode && desiredNodesProcessed < desiredNodes.size()) { - QSGNode *desiredNode = desiredNodes.at(desiredNodesProcessed); + QSGNode *desiredNode = 0; + while (currentNode && (desiredNode = fetchNextNode(itemPriv, ii, fetchedPaintNode))) { // uh oh... reality and our utopic paradise are diverging! // we need to reconcile this... if (currentNode != desiredNode) { @@ -2919,9 +2917,8 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) // if we didn't process as many nodes as in the new list, then we have // more nodes at the end of desiredNodes to append to our list. // this will be the case when adding new nodes, for instance. - if (desiredNodesProcessed < desiredNodes.size()) { - for (int i = desiredNodesProcessed; i < desiredNodes.size(); ++i) { - QSGNode *desiredNode = desiredNodes.at(i); + if (desiredNodesProcessed < desiredNodesSize) { + while ((desiredNode = fetchNextNode(itemPriv, ii, fetchedPaintNode))) { if (desiredNode->parent()) desiredNode->parent()->removeChildNode(desiredNode); groupNode->appendChildNode(desiredNode); @@ -2938,10 +2935,6 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) removed++; } } - -#if defined(CHILDRENUPDATE_DEBUG) - qDebug() << "Done children update for " << itemPriv << "- before:" << initialCount << "after:" << groupNode->childCount() << "added:" << added << "removed:" << removed << "replaced:" << replaced; -#endif } if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) { @@ -3307,12 +3300,7 @@ QOpenGLFramebufferObject *QQuickWindow::renderTarget() const QImage QQuickWindow::grabWindow() { Q_D(QQuickWindow); - if (!isVisible()) { - - if (d->context->openglContext()) { - qWarning("QQuickWindow::grabWindow: scene graph already in use"); - return QImage(); - } + if (!isVisible() && !d->context->openglContext()) { if (!handle() || !size().isValid()) { qWarning("QQuickWindow::grabWindow: window must be created and have a valid size"); @@ -3382,6 +3370,11 @@ QQmlIncubationController *QQuickWindow::incubationController() const will delete the GL texture when the texture object is deleted. \value TextureCanUseAtlas The image can be uploaded into a texture atlas. + + \value TextureIsOpaque The texture will return false for + QSGTexture::hasAlphaChannel() and will not be blended. This flag was added + in Qt 5.6. + */ /*! @@ -3586,12 +3579,21 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const The caller of the function is responsible for deleting the returned texture. The actual GL texture will be deleted when the texture object is deleted. - When \a options contains TextureCanUseAtlas the engine may put the image + When \a options contains TextureCanUseAtlas, the engine may put the image into a texture atlas. Textures in an atlas need to rely on QSGTexture::normalizedTextureSubRect() for their geometry and will not support QSGTexture::Repeat. Other values from CreateTextureOption are ignored. + When \a options contains TextureIsOpaque, the engine will create an RGB + texture which returns false for QSGTexture::hasAlphaChannel(). Opaque + textures will in most cases be faster to render. When this flag is not set, + the texture will have an alpha channel based on the image's format. + + When \a options contains TextureHasMipmaps, the engine will create a + texture which can use mipmap filtering. Mipmapped textures can not be in + an atlas. + The returned texture will be using \c GL_TEXTURE_2D as texture target and \c GL_RGBA as internal format. Reimplement QSGTexture to create textures with different parameters. @@ -3613,14 +3615,13 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const { Q_D(const QQuickWindow); - if (d->context) { - if (options & TextureCanUseAtlas) - return d->context->createTexture(image); - else - return d->context->createTextureNoAtlas(image); - } - else - return 0; + if (!d->context) + return 0; + uint flags = 0; + if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas; + if (options & TextureHasMipmaps) flags |= QSGRenderContext::CreateTexture_Mipmap; + if (!(options & TextureIsOpaque)) flags |= QSGRenderContext::CreateTexture_Alpha; + return d->context->createTexture(image, flags); } @@ -4115,6 +4116,7 @@ void QQuickWindow::resetOpenGLState() \value BeforeRenderingStage Before rendering. \value AfterRenderingStage After rendering. \value AfterSwapStage After the frame is swapped. + \value NoStage As soon as possible. This value was added in Qt 5.6. \sa {Scene Graph and Rendering} */ @@ -4140,8 +4142,17 @@ void QQuickWindow::resetOpenGLState() If the rendering is happening on a different thread, then the job will happen on the rendering thread. - \note This function does not trigger rendering; the job - will be stored run until rendering is triggered elsewhere. + If \a stage is \l NoStage, \a job will be run at the earliest opportunity + whenever the render thread is not busy rendering a frame. If there is no + OpenGL context available or the window is not exposed at the time the job is + either posted or handled, it is deleted without executing the run() method. + If a non-threaded renderer is in use, the run() method of the job is executed + synchronously. + The OpenGL context is changed to the renderer context before executing a + \l NoStage job. + + \note This function does not trigger rendering; the jobs targeting any other + stage than NoStage will be stored run until rendering is triggered elsewhere. To force the job to run earlier, call QQuickWindow::update(); \sa beforeRendering(), afterRendering(), beforeSynchronizing(), @@ -4153,16 +4164,22 @@ void QQuickWindow::scheduleRenderJob(QRunnable *job, RenderStage stage) Q_D(QQuickWindow); d->renderJobMutex.lock(); - if (stage == BeforeSynchronizingStage) + if (stage == BeforeSynchronizingStage) { d->beforeSynchronizingJobs << job; - else if (stage == AfterSynchronizingStage) + } else if (stage == AfterSynchronizingStage) { d->afterSynchronizingJobs << job; - else if (stage == BeforeRenderingStage) + } else if (stage == BeforeRenderingStage) { d->beforeRenderingJobs << job; - else if (stage == AfterRenderingStage) + } else if (stage == AfterRenderingStage) { d->afterRenderingJobs << job; - else if (stage == AfterSwapStage) + } else if (stage == AfterSwapStage) { d->afterSwapJobs << job; + } else if (stage == NoStage) { + if (isExposed()) + d->windowManager->postJob(this, job); + else + delete job; + } d->renderJobMutex.unlock(); } diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index f7a1956120..d5bf9fba81 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -69,7 +69,8 @@ public: TextureHasAlphaChannel = 0x0001, TextureHasMipmaps = 0x0002, TextureOwnsGLTexture = 0x0004, - TextureCanUseAtlas = 0x0008 + TextureCanUseAtlas = 0x0008, + TextureIsOpaque = 0x0010 }; enum RenderStage { @@ -77,7 +78,8 @@ public: AfterSynchronizingStage, BeforeRenderingStage, AfterRenderingStage, - AfterSwapStage + AfterSwapStage, + NoStage }; Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption) @@ -85,7 +87,7 @@ public: enum SceneGraphError { ContextNotAvailable = 1 }; - Q_ENUMS(SceneGraphError) + Q_ENUM(SceneGraphError) QQuickWindow(QWindow *parent = 0); explicit QQuickWindow(QQuickRenderControl *renderControl); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 605a36fb1d..0d33d2398a 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -200,7 +200,7 @@ public: QQuickItem *dirtyItemList; QList<QSGNode *> cleanupNodeList; - QSet<QQuickItem *> itemsToPolish; + QVector<QQuickItem *> itemsToPolish; void updateDirtyNodes(); void cleanupNodes(); diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp index 524ee02952..ecf6865895 100644 --- a/src/quick/qtquick2.cpp +++ b/src/quick/qtquick2.cpp @@ -38,7 +38,8 @@ #include <private/qquickitemsmodule_p.h> #include <private/qquickaccessiblefactory_p.h> -#include <private/qqmlenginedebugservice_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmldebugstatesdelegate_p.h> #include <private/qqmlbinding_p.h> #include <private/qqmlcontext_p.h> @@ -133,7 +134,6 @@ void QQmlQtQuick2DebugStatesDelegate::updateBinding(QQmlContext *context, QQmlContextData::get(context), fileName, line, column); newBinding->setTarget(property); - newBinding->setNotifyOnValueChanged(true); } state->changeBindingInRevertList(object, propertyName, newBinding); @@ -187,11 +187,13 @@ void QQmlQtQuick2Module::defineModule() QAccessible::installFactory(&qQuickAccessibleFactory); #endif - if (QQmlDebugService::isDebuggingEnabled()) { - QQmlEngineDebugService::instance()->setStatesDelegate( - new QQmlQtQuick2DebugStatesDelegate); - QQuickProfiler::initialize(); - } + QQmlEngineDebugService *debugService = QQmlDebugConnector::service<QQmlEngineDebugService>(); + if (debugService) + debugService->setStatesDelegate(new QQmlQtQuick2DebugStatesDelegate); + + QQmlProfilerService *profilerService = QQmlDebugConnector::service<QQmlProfilerService>(); + if (profilerService) + QQuickProfiler::initialize(profilerService); } void QQmlQtQuick2Module::undefineModule() diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 42b9f526d0..8632ea0b52 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -198,9 +198,9 @@ ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *mate void ShaderManager::invalidated() { - qDeleteAll(stockShaders.values()); + qDeleteAll(stockShaders); stockShaders.clear(); - qDeleteAll(rewrittenShaders.values()); + qDeleteAll(rewrittenShaders); rewrittenShaders.clear(); delete blitProgram; blitProgram = 0; @@ -487,6 +487,11 @@ void Updater::visitGeometryNode(Node *n) if (e->batch) renderer->invalidateBatchAndOverlappingRenderOrders(e->batch); } + if (n->dirtyState & QSGNode::DirtyMaterial) { + Element *e = n->element(); + if (e->batch && e->batch->isMaterialCompatible(e) == BatchBreaksOnCompare) + renderer->invalidateBatchAndOverlappingRenderOrders(e->batch); + } } SHADOWNODE_TRAVERSE(n) visitNode(*child); @@ -1081,6 +1086,9 @@ void Renderer::nodeWasRemoved(Node *node) if (e) { e->removed = true; m_elementsToDelete.add(e); + + if (m_renderNodeElements.isEmpty()) + m_useDepthBuffer = context()->openglContext()->format().depthBufferSize() > 0; } } @@ -1213,10 +1221,7 @@ void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) if (e->isMaterialBlended != blended) { m_rebuild |= Renderer::FullRebuild; e->isMaterialBlended = blended; - } else if (e->batch) { - if (e->batch->isMaterialCompatible(e) == BatchBreaksOnCompare) - invalidateBatchAndOverlappingRenderOrders(e->batch); - } else { + } else if (!e->batch) { m_rebuild |= Renderer::BuildBatches; } } @@ -2554,8 +2559,15 @@ void Renderer::render() QSGNodeDumper::dump(rootNode()); } - if (Q_UNLIKELY(debug_render() || debug_build())) { + QElapsedTimer timer; + quint64 timeRenderLists = 0; + quint64 timePrepareOpaque = 0; + quint64 timePrepareAlpha = 0; + quint64 timeSorting = 0; + quint64 timeUploadOpaque = 0; + quint64 timeUploadAlpha = 0; + if (Q_UNLIKELY(debug_render() || debug_build())) { QByteArray type("rebuild:"); if (m_rebuild == 0) type += " none"; @@ -2571,6 +2583,7 @@ void Renderer::render() } qDebug() << "Renderer::render()" << this << type; + timer.start(); } if (m_vao) @@ -2597,6 +2610,7 @@ void Renderer::render() } } } + if (Q_UNLIKELY(debug_render())) timeRenderLists = timer.restart(); for (int i=0; i<m_opaqueBatches.size(); ++i) m_opaqueBatches.at(i)->cleanupRemovedElements(); @@ -2609,7 +2623,9 @@ void Renderer::render() if (m_rebuild & BuildBatches) { prepareOpaqueBatches(); + if (Q_UNLIKELY(debug_render())) timePrepareOpaque = timer.restart(); prepareAlphaBatches(); + if (Q_UNLIKELY(debug_render())) timePrepareAlpha = timer.restart(); if (Q_UNLIKELY(debug_build())) { qDebug() << "Opaque Batches:"; @@ -2629,8 +2645,11 @@ void Renderer::render() } } } + } else { + if (Q_UNLIKELY(debug_render())) timePrepareOpaque = timePrepareAlpha = timer.restart(); } + deleteRemovedElements(); if (m_rebuild != 0) { @@ -2646,6 +2665,8 @@ void Renderer::render() m_zRange = 1.0 / (m_nextRenderOrder); } + if (Q_UNLIKELY(debug_render())) timeSorting = timer.restart(); + int largestVBO = 0; #ifdef QSG_SEPARATE_INDEX_BUFFER int largestIBO = 0; @@ -2660,6 +2681,8 @@ void Renderer::render() #endif uploadBatch(b); } + if (Q_UNLIKELY(debug_render())) timeUploadOpaque = timer.restart(); + if (Q_UNLIKELY(debug_upload())) qDebug() << "Uploading Alpha Batches:"; for (int i=0; i<m_alphaBatches.size(); ++i) { @@ -2670,6 +2693,7 @@ void Renderer::render() largestIBO = qMax(b->ibo.size, largestIBO); #endif } + if (Q_UNLIKELY(debug_render())) timeUploadAlpha = timer.restart(); if (largestVBO * 2 < m_vertexUploadPool.size()) m_vertexUploadPool.resize(largestVBO * 2); @@ -2680,6 +2704,15 @@ void Renderer::render() renderBatches(); + if (Q_UNLIKELY(debug_render())) { + qDebug(" -> times: build: %d, prepare(opaque/alpha): %d/%d, sorting: %d, upload(opaque/alpha): %d/%d, render: %d", + (int) timeRenderLists, + (int) timePrepareOpaque, (int) timePrepareAlpha, + (int) timeSorting, + (int) timeUploadOpaque, (int) timeUploadAlpha, + (int) timer.elapsed()); + } + m_rebuild = 0; m_renderOrderRebuildLower = -1; m_renderOrderRebuildUpper = -1; diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h index 6b494dbaeb..d19fa0e17d 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h @@ -100,6 +100,7 @@ template <typename Type, int PageSize> class Allocator { public: Allocator() + : m_freePage(0) { pages.push_back(new AllocatorPage<Type, PageSize>()); } @@ -112,14 +113,21 @@ public: Type *allocate() { AllocatorPage<Type, PageSize> *p = 0; - for (int i=0; i<pages.size(); ++i) { + for (int i = m_freePage; i < pages.size(); i++) { if (pages.at(i)->available > 0) { p = pages.at(i); + m_freePage = i; break; } } + + // we couldn't find a free page from m_freePage to the last page. + // either there is no free pages, or there weren't any in the area we + // scanned: rescanning is expensive, so let's just assume there isn't + // one. when an item is released, we'll reset m_freePage anyway. if (!p) { p = new AllocatorPage<Type, PageSize>(); + m_freePage = pages.count(); pages.push_back(p); } uint pos = p->blocks[PageSize - p->available]; @@ -151,6 +159,9 @@ public: delete page; page = pages.back(); } + + // Reset the free page to force a scan for a new free point. + m_freePage = 0; } void release(Type *t) @@ -172,6 +183,7 @@ public: } QVector<AllocatorPage<Type, PageSize> *> pages; + int m_freePage; }; @@ -500,8 +512,8 @@ public: ShaderManager(QSGRenderContext *ctx) : blitProgram(0), visualizeProgram(0), context(ctx) { } ~ShaderManager() { - qDeleteAll(rewrittenShaders.values()); - qDeleteAll(stockShaders.values()); + qDeleteAll(rewrittenShaders); + qDeleteAll(stockShaders); } public Q_SLOTS: diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 4f5c4efe14..bf97133e97 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -165,7 +165,9 @@ void QSGDistanceFieldGlyphCache::update() Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphAdaptationLayerFrame); QList<QDistanceField> distanceFields; - for (int i = 0; i < m_pendingGlyphs.size(); ++i) { + const int pendingGlyphsSize = m_pendingGlyphs.size(); + distanceFields.reserve(pendingGlyphsSize); + for (int i = 0; i < pendingGlyphsSize; ++i) { GlyphData &gd = glyphData(m_pendingGlyphs.at(i)); distanceFields.append(QDistanceField(gd.path, m_pendingGlyphs.at(i), diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 418d571ae6..150f8475d8 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -41,7 +41,6 @@ #include <QtQuick/private/qsgdefaultglyphnode_p.h> #include <QtQuick/private/qsgdistancefieldglyphnode_p.h> #include <QtQuick/private/qsgdistancefieldglyphnode_p_p.h> -#include <QtQuick/private/qsgshareddistancefieldglyphcache_p.h> #include <QtQuick/private/qsgatlastexture_p.h> #include <QtQuick/private/qsgrenderloop_p.h> #include <QtQuick/private/qsgdefaultlayer_p.h> @@ -60,15 +59,10 @@ #include <QtQuick/private/qsgtexture_p.h> #include <QtGui/private/qguiapplication_p.h> #include <QtCore/private/qabstractanimation_p.h> -#include <qpa/qplatformintegration.h> - -#include <qpa/qplatformsharedgraphicscache.h> #include <private/qobject_p.h> #include <qmutex.h> -#include <private/qqmlprofilerservice_p.h> - DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) /* @@ -572,35 +566,7 @@ QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRaw QSGDistanceFieldGlyphCache *cache = m_distanceFieldCacheManager->cache(font); if (!cache) { - QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration(); - if (platformIntegration != 0 - && platformIntegration->hasCapability(QPlatformIntegration::SharedGraphicsCache)) { - QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine; - if (!fe->faceId().filename.isEmpty()) { - QByteArray keyName = fe->faceId().filename; - if (font.style() != QFont::StyleNormal) - keyName += QByteArray(" I"); - if (font.weight() != QFont::Normal) - keyName += ' ' + QByteArray::number(font.weight()); - keyName += QByteArray(" DF"); - QPlatformSharedGraphicsCache *sharedGraphicsCache = - platformIntegration->createPlatformSharedGraphicsCache(keyName); - - if (sharedGraphicsCache != 0) { - sharedGraphicsCache->ensureCacheInitialized(keyName, - QPlatformSharedGraphicsCache::OpenGLTexture, - QPlatformSharedGraphicsCache::Alpha8); - - cache = new QSGSharedDistanceFieldGlyphCache(keyName, - sharedGraphicsCache, - m_distanceFieldCacheManager, - openglContext(), - font); - } - } - } - if (!cache) - cache = new QSGDefaultDistanceFieldGlyphCache(m_distanceFieldCacheManager, openglContext(), font); + cache = new QSGDefaultDistanceFieldGlyphCache(m_distanceFieldCacheManager, openglContext(), font); m_distanceFieldCacheManager->insertCache(font, cache); } @@ -676,7 +642,7 @@ void QSGRenderContext::invalidate() qDeleteAll(m_texturesToDelete); m_texturesToDelete.clear(); - qDeleteAll(m_textures.values()); + qDeleteAll(m_textures); m_textures.clear(); /* The cleanup of the atlas textures is a bit intriguing. @@ -767,22 +733,26 @@ QSGDepthStencilBufferManager *QSGRenderContext::depthStencilBufferManager() will be called with \a image as argument. */ -QSGTexture *QSGRenderContext::createTexture(const QImage &image) const +QSGTexture *QSGRenderContext::createTexture(const QImage &image, uint flags) const { - if (!openglContext()) - return 0; - QSGTexture *t = m_atlasManager->create(image); - if (t) - return t; - return createTextureNoAtlas(image); -} + bool atlas = flags & CreateTexture_Atlas; + bool mipmap = flags & CreateTexture_Mipmap; + bool alpha = flags & CreateTexture_Alpha; -QSGTexture *QSGRenderContext::createTextureNoAtlas(const QImage &image) const -{ - QSGPlainTexture *t = new QSGPlainTexture(); - if (!image.isNull()) - t->setImage(image); - return t; + // The atlas implementation is only supported from the render thread and + // does not support mipmaps. + if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) { + QSGTexture *t = m_atlasManager->create(image, alpha); + if (t) + return t; + } + + QSGPlainTexture *texture = new QSGPlainTexture(); + texture->setImage(image); + if (texture->hasAlphaChannel() && !alpha) + texture->setHasAlphaChannel(false); + + return texture; } /*! diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index d1897f20f9..49b6f6e2a0 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -88,6 +88,12 @@ class Q_QUICK_PRIVATE_EXPORT QSGRenderContext : public QObject { Q_OBJECT public: + enum CreateTextureFlags { + CreateTexture_Alpha = 0x1, + CreateTexture_Atlas = 0x2, + CreateTexture_Mipmap = 0x4 + }; + QSGRenderContext(QSGContext *context); ~QSGRenderContext(); @@ -107,8 +113,8 @@ public: virtual QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font); QSGTexture *textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window); - virtual QSGTexture *createTexture(const QImage &image) const; - virtual QSGTexture *createTextureNoAtlas(const QImage &image) const; + virtual QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const; + virtual QSGRenderer *createRenderer(); virtual void compile(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0); diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp index c84e6628a5..14bc0fad07 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -104,8 +104,11 @@ char const *const *QSGTextMaskShader::attributeNames() const } QSGTextMaskShader::QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat) - : QSGMaterialShader(*new QSGMaterialShaderPrivate), - m_glyphFormat(glyphFormat) + : QSGMaterialShader(*new QSGMaterialShaderPrivate) + , m_matrix_id(-1) + , m_color_id(-1) + , m_textureScale_id(-1) + , m_glyphFormat(glyphFormat) { setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/scenegraph/shaders/textmask.vert")); setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/scenegraph/shaders/textmask.frag")); @@ -435,7 +438,9 @@ void QSGTextMaskMaterial::populate(const QPointF &p, { Q_ASSERT(m_font.isValid()); QVector<QFixedPoint> fixedPointPositions; - for (int i=0; i<glyphPositions.size(); ++i) + const int glyphPositionsSize = glyphPositions.size(); + fixedPointPositions.reserve(glyphPositionsSize); + for (int i=0; i < glyphPositionsSize; ++i) fixedPointPositions.append(QFixedPoint::fromPointF(glyphPositions.at(i))); QTextureGlyphCache *cache = glyphCache(); diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp index 7e602fc0bd..4630e45ecf 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp @@ -200,9 +200,11 @@ void QSGDistanceFieldGlyphNode::updateGeometry() const QVector<QPointF> positions = m_glyphs.positions(); qreal fontPixelSize = m_glyphs.rawFont().pixelSize(); - QVector<QSGGeometry::TexturedPoint2D> vp; + // The template parameters here are assuming that most strings are short, 64 + // characters or less. + QVarLengthArray<QSGGeometry::TexturedPoint2D, 256> vp; vp.reserve(indexes.size() * 4); - QVector<ushort> ip; + QVarLengthArray<ushort, 384> ip; ip.reserve(indexes.size() * 6); qreal maxTexMargin = m_glyph_cache->distanceFieldRadius(); diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp index 422e31ad96..6f4b85a5b9 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp @@ -80,6 +80,11 @@ QSGDistanceFieldTextMaterialShader::QSGDistanceFieldTextMaterialShader() : QSGMaterialShader(), m_fontScale(1.0) , m_matrixScale(1.0) + , m_matrix_id(-1) + , m_textureScale_id(-1) + , m_alphaMin_id(-1) + , m_alphaMax_id(-1) + , m_color_id(-1) , m_lastAlphaMin(-1) , m_lastAlphaMax(-1) { @@ -260,6 +265,7 @@ protected: DistanceFieldStyledTextMaterialShader::DistanceFieldStyledTextMaterialShader() : QSGDistanceFieldTextMaterialShader() + , m_styleColor_id(-1) { } @@ -330,6 +336,8 @@ protected: DistanceFieldOutlineTextMaterialShader::DistanceFieldOutlineTextMaterialShader() : DistanceFieldStyledTextMaterialShader() + , m_outlineAlphaMax0_id(-1) + , m_outlineAlphaMax1_id(-1) { setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/scenegraph/shaders/distancefieldoutlinetext.frag")); } @@ -411,6 +419,7 @@ protected: DistanceFieldShiftedStyleTextMaterialShader::DistanceFieldShiftedStyleTextMaterialShader() : DistanceFieldStyledTextMaterialShader() + , m_shift_id(-1) { setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/scenegraph/shaders/distancefieldshiftedtext.vert")); setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/scenegraph/shaders/distancefieldshiftedtext.frag")); @@ -489,6 +498,8 @@ private: QSGHiQSubPixelDistanceFieldTextMaterialShader::QSGHiQSubPixelDistanceFieldTextMaterialShader() : QSGDistanceFieldTextMaterialShader() + , m_fontScale_id(-1) + , m_vecDelta_id(-1) { setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/scenegraph/shaders/hiqsubpixeldistancefieldtext.vert")); setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/scenegraph/shaders/hiqsubpixeldistancefieldtext.frag")); diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 4d3f34c71c..3059b750f2 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -102,6 +102,22 @@ void QSGRenderLoop::cleanup() s_instance = 0; } +/*! + * Non-threaded render loops immediately run the job if there is a context. + */ +void QSGRenderLoop::postJob(QQuickWindow *window, QRunnable *job) +{ + Q_ASSERT(window); + Q_ASSERT(job); + + if (window->openglContext()) { + window->openglContext()->makeCurrent(window); + job->run(); + } + + delete job; +} + class QSGGuiThreadRenderLoop : public QSGRenderLoop { Q_OBJECT diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h index 4293015b96..3336731fda 100644 --- a/src/quick/scenegraph/qsgrenderloop_p.h +++ b/src/quick/scenegraph/qsgrenderloop_p.h @@ -45,6 +45,7 @@ class QQuickWindow; class QSGContext; class QSGRenderContext; class QAnimationDriver; +class QRunnable; class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop : public QObject { @@ -72,6 +73,7 @@ public: virtual QSGRenderContext *createRenderContext(QSGContext *) const = 0; virtual void releaseResources(QQuickWindow *window) = 0; + virtual void postJob(QQuickWindow *window, QRunnable *job); void addWindow(QQuickWindow *win) { m_windows.insert(win); } void removeWindow(QQuickWindow *win) { m_windows.remove(win); } diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp deleted file mode 100644 index f1cc9d1a86..0000000000 --- a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp +++ /dev/null @@ -1,655 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#define EGL_EGLEXT_PROTOTYPES -#define GL_GLEXT_PROTOTYPES -#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#endif - -#include "qsgshareddistancefieldglyphcache_p.h" - -#include <QtCore/qhash.h> -#include <QtCore/qthread.h> -#include <QtCore/qcoreapplication.h> - -#include <qpa/qplatformsharedgraphicscache.h> - -#include <QtQuick/qquickwindow.h> - -// #define QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG - -QT_BEGIN_NAMESPACE - -namespace { - - class QSGInvokeEvent: public QEvent - { - public: - QSGInvokeEvent(QPlatformSharedGraphicsCache *cache, - const QByteArray &cacheId = QByteArray(), - const QVector<quint32> &glyphIds = QVector<quint32>(), - bool inSceneGraphUpdate = false) - : QEvent(User) - , m_cache(cache) - , m_cacheId(cacheId) - , m_glyphIds(glyphIds) - , m_inSceneGraphUpdate(inSceneGraphUpdate) - {} - - bool inSceneGraphUpdate() const { return m_inSceneGraphUpdate; } - QPlatformSharedGraphicsCache *cache() const { return m_cache; } - - virtual void invoke() = 0; - protected: - QPlatformSharedGraphicsCache *m_cache; - QByteArray m_cacheId; - QVector<quint32> m_glyphIds; - bool m_inSceneGraphUpdate; - }; - - class QSGReleaseItemsEvent: public QSGInvokeEvent - { - public: - QSGReleaseItemsEvent(QPlatformSharedGraphicsCache *cache, - const QByteArray &cacheId, - const QVector<quint32> &glyphIds, - bool inSceneGraphUpdate) - : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate) - { - } - - void invoke() - { - m_cache->releaseItems(m_cacheId, m_glyphIds); - } - }; - - class QSGRequestItemsEvent: public QSGInvokeEvent - { - public: - QSGRequestItemsEvent(QPlatformSharedGraphicsCache *cache, - const QByteArray &cacheId, - const QVector<quint32> &glyphIds, - bool inSceneGraphUpdate) - : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate) - { - } - - void invoke() - { - m_cache->requestItems(m_cacheId, m_glyphIds); - } - }; - - class QSGInsertItemsEvent: public QSGInvokeEvent - { - public: - QSGInsertItemsEvent(QPlatformSharedGraphicsCache *cache, - const QByteArray &cacheId, - const QVector<quint32> &glyphIds, - const QVector<QImage> &images, - bool inSceneGraphUpdate) - : QSGInvokeEvent(cache, cacheId, glyphIds, inSceneGraphUpdate) - , m_images(images) - { - } - - void invoke() - { - m_cache->insertItems(m_cacheId, m_glyphIds, m_images); - } - - private: - QVector<QImage> m_images; - }; - - class QSGEndRequestBatchEvent: public QSGInvokeEvent - { - public: - QSGEndRequestBatchEvent(QPlatformSharedGraphicsCache *cache) - : QSGInvokeEvent(cache) - { - } - - void invoke() - { - if (m_cache->requestBatchStarted()) - m_cache->endRequestBatch(); - } - }; - - class QSGMainThreadInvoker: public QObject - { - public: - bool event(QEvent *e) - { - if (e->type() == QEvent::User) { - Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread()); - - QSGInvokeEvent *invokeEvent = static_cast<QSGInvokeEvent *>(e); - if (invokeEvent->inSceneGraphUpdate()) { - QPlatformSharedGraphicsCache *cache = invokeEvent->cache(); - if (!cache->requestBatchStarted()) - cache->beginRequestBatch(); - } - - static_cast<QSGInvokeEvent *>(e)->invoke(); - return true; - } - return QObject::event(e); - } - - static QSGMainThreadInvoker *instance() - { - if (m_invoker == 0) { - m_invoker = new QSGMainThreadInvoker; - m_invoker->moveToThread(QCoreApplication::instance()->thread()); - } - - return m_invoker; - } - - private: - static QSGMainThreadInvoker *m_invoker; - }; - - QSGMainThreadInvoker* QSGMainThreadInvoker::m_invoker = 0; -} - -QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId, - QPlatformSharedGraphicsCache *sharedGraphicsCache, - QSGDistanceFieldGlyphCacheManager *man, - QOpenGLContext *c, - const QRawFont &font) - : QSGDistanceFieldGlyphCache(man, c, font) - , m_cacheId(cacheId) - , m_sharedGraphicsCache(sharedGraphicsCache) - , m_isInSceneGraphUpdate(false) - , m_hasPostedEvents(false) -{ -#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) - qDebug("QSGSharedDistanceFieldGlyphCache with id %s created in thread %p", - cacheId.constData(), QThread::currentThreadId()); -#endif - - Q_ASSERT(sizeof(glyph_t) == sizeof(quint32)); - Q_ASSERT(sharedGraphicsCache != 0); - - connect(sharedGraphicsCache, SIGNAL(itemsMissing(QByteArray,QVector<quint32>)), - this, SLOT(reportItemsMissing(QByteArray,QVector<quint32>)), - Qt::DirectConnection); - connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)), - this, SLOT(reportItemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)), - Qt::DirectConnection); - connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)), - this, SLOT(reportItemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)), - Qt::DirectConnection); - connect(sharedGraphicsCache, SIGNAL(itemsInvalidated(QByteArray,QVector<quint32>)), - this, SLOT(reportItemsInvalidated(QByteArray,QVector<quint32>)), - Qt::DirectConnection); - - Q_ASSERT(c); - QQuickWindow *window = static_cast<QQuickWindow *>(c->surface()); - Q_ASSERT(window != 0); - - connect(window, SIGNAL(beforeSynchronizing()), this, SLOT(sceneGraphUpdateStarted()), - Qt::DirectConnection); - connect(window, SIGNAL(beforeRendering()), this, SLOT(sceneGraphUpdateDone()), - Qt::DirectConnection); -} - -QSGSharedDistanceFieldGlyphCache::~QSGSharedDistanceFieldGlyphCache() -{ - { - QHash<glyph_t, void *>::const_iterator it = m_bufferForGlyph.constBegin(); - while (it != m_bufferForGlyph.constEnd()) { - m_sharedGraphicsCache->dereferenceBuffer(it.value()); - ++it; - } - } - - { - QHash<quint32, PendingGlyph>::const_iterator it = m_pendingReadyGlyphs.constBegin(); - while (it != m_pendingReadyGlyphs.constEnd()) { - m_sharedGraphicsCache->dereferenceBuffer(it.value().buffer); - ++it; - } - } -} - -void QSGSharedDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs) -{ - typedef QSet<glyph_t>::const_iterator GlyphSetConstIt; - - QMutexLocker locker(&m_pendingGlyphsMutex); - -#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) - qDebug("QSGSharedDistanceFieldGlyphCache::requestGlyphs() called for %s (%d glyphs)", - m_cacheId.constData(), glyphs.size()); -#endif - - m_requestedGlyphsThatHaveNotBeenReturned.unite(glyphs); - m_requestedGlyphs.unite(glyphs); - - QVector<quint32> glyphsVector; - glyphsVector.reserve(glyphs.size()); - - for (GlyphSetConstIt it = glyphs.constBegin(), cend = glyphs.constEnd(); it != cend; ++it) { - Q_ASSERT(!m_bufferForGlyph.contains(*it)); - glyphsVector.append(*it); - } - - m_hasPostedEvents = true; - QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance(); - QCoreApplication::postEvent(invoker, new QSGRequestItemsEvent(m_sharedGraphicsCache, - m_cacheId, - glyphsVector, - m_isInSceneGraphUpdate)); -} - -void QSGSharedDistanceFieldGlyphCache::waitForGlyphs() -{ - Q_ASSERT(!m_isInSceneGraphUpdate); - if (m_isInSceneGraphUpdate) { - qWarning("QSGSharedDistanceFieldGlyphCache::waitForGlyphs: Called from inside " - "scenegraph update. Will freeze."); - } - - { - QMutexLocker locker(&m_pendingGlyphsMutex); - while (!m_requestedGlyphsThatHaveNotBeenReturned.isEmpty()) - m_pendingGlyphsCondition.wait(&m_pendingGlyphsMutex); - } -} - -void QSGSharedDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField> &glyphs) -{ - { - QMutexLocker locker(&m_pendingGlyphsMutex); -#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) - qDebug("QSGSharedDistanceFieldGlyphCache::storeGlyphs() called for %s (%d glyphs)", - m_cacheId.constData(), glyphs.size()); -#endif - - int glyphCount = glyphs.size(); - QVector<quint32> glyphIds(glyphCount); - QVector<QImage> images(glyphCount); - for (int i = 0; i < glyphs.size(); ++i) { - const QDistanceField &df = glyphs.at(i); - m_requestedGlyphsThatHaveNotBeenReturned.insert(df.glyph()); - glyphIds[i] = df.glyph(); - // ### TODO: Handle QDistanceField in QPlatformSharedGraphicsCache - images[i] = df.toImage(QImage::Format_Indexed8); - } - - m_hasPostedEvents = true; - QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance(); - QCoreApplication::postEvent(invoker, new QSGInsertItemsEvent(m_sharedGraphicsCache, - m_cacheId, - glyphIds, - images, - m_isInSceneGraphUpdate)); - } - - processPendingGlyphs(); -} - -void QSGSharedDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs) -{ - Q_UNUSED(glyphs); - - // Intentionally empty. Not required in this implementation, since the glyphs are reference - // counted outside and releaseGlyphs() will only be called when there are no more references. -} - -void QSGSharedDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs) -{ - typedef QSet<glyph_t>::const_iterator GlyphSetConstIt; - -#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) - qDebug("QSGSharedDistanceFieldGlyphCache::releaseGlyphs() called for %s (%d glyphs)", - m_cacheId.constData(), glyphs.size()); -#endif - - m_requestedGlyphs.subtract(glyphs); - - QVector<quint32> glyphsVector; - glyphsVector.reserve(glyphs.size()); - - for (GlyphSetConstIt glyphsIt = glyphs.constBegin(), cend = glyphs.constEnd(); glyphsIt != cend; ++glyphsIt) { - QHash<glyph_t, void *>::iterator bufferIt = m_bufferForGlyph.find(*glyphsIt); - if (bufferIt != m_bufferForGlyph.end()) { - void *buffer = bufferIt.value(); - removeGlyph(*glyphsIt); - m_bufferForGlyph.erase(bufferIt); - Q_ASSERT(!m_bufferForGlyph.contains(*glyphsIt)); - - if (!m_sharedGraphicsCache->dereferenceBuffer(buffer)) { -#if !defined(QT_NO_DEBUG) - bufferIt = m_bufferForGlyph.begin(); - while (bufferIt != m_bufferForGlyph.end()) { - Q_ASSERT(bufferIt.value() != buffer); - ++bufferIt; - } -#endif - } - } - - glyphsVector.append(*glyphsIt); - } - - m_hasPostedEvents = true; - QSGMainThreadInvoker *mainThreadInvoker = QSGMainThreadInvoker::instance(); - QCoreApplication::postEvent(mainThreadInvoker, new QSGReleaseItemsEvent(m_sharedGraphicsCache, - m_cacheId, - glyphsVector, - m_isInSceneGraphUpdate)); -} - -void QSGSharedDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement) -{ - Owner &owner = m_registeredOwners[ownerElement]; - if (owner.ref == 0) { - owner.item = ownerElement; - - bool ok = connect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess())); - Q_ASSERT_X(ok, Q_FUNC_INFO, "QML element that owns a glyph node must have triggerPreprocess() slot"); - Q_UNUSED(ok); - } - ++owner.ref; -} - -void QSGSharedDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement) -{ - QHash<QQuickItem *, Owner>::iterator it = m_registeredOwners.find(ownerElement); - if (it != m_registeredOwners.end() && --it->ref <= 0) { - if (it->item) - disconnect(this, SIGNAL(glyphsPending()), ownerElement, SLOT(triggerPreprocess())); - m_registeredOwners.erase(it); - } -} - -namespace { - struct TextureContent { - QSize size; - QVector<glyph_t> glyphs; - }; -} - -void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs() -{ - Q_ASSERT(QThread::currentThread() == thread()); - - waitForGlyphs(); - - { - QMutexLocker locker(&m_pendingGlyphsMutex); - if (m_pendingMissingGlyphs.isEmpty() - && m_pendingReadyGlyphs.isEmpty() - && m_pendingInvalidatedGlyphs.isEmpty()) { - return; - } - - { - QVector<glyph_t> pendingMissingGlyphs; - pendingMissingGlyphs.reserve(m_pendingMissingGlyphs.size()); - - QSet<glyph_t>::const_iterator it = m_pendingMissingGlyphs.constBegin(); - while (it != m_pendingMissingGlyphs.constEnd()) { - pendingMissingGlyphs.append(*it); - ++it; - } - - markGlyphsToRender(pendingMissingGlyphs); - } - - { - QVector<glyph_t> filteredPendingInvalidatedGlyphs; - filteredPendingInvalidatedGlyphs.reserve(m_pendingInvalidatedGlyphs.size()); - - QSet<glyph_t>::const_iterator it = m_pendingInvalidatedGlyphs.constBegin(); - while (it != m_pendingInvalidatedGlyphs.constEnd()) { - bool rerequestGlyph = false; - - // The glyph was invalidated right after being posted as ready, we throw away - // the ready glyph and rerequest it to be certain - QHash<quint32, PendingGlyph>::iterator pendingGlyphIt = m_pendingReadyGlyphs.find(*it); - if (pendingGlyphIt != m_pendingReadyGlyphs.end()) { - m_sharedGraphicsCache->dereferenceBuffer(pendingGlyphIt.value().buffer); - pendingGlyphIt = m_pendingReadyGlyphs.erase(pendingGlyphIt); - rerequestGlyph = true; - } - - void *bufferId = m_bufferForGlyph.value(*it, 0); - if (bufferId != 0) { - m_sharedGraphicsCache->dereferenceBuffer(bufferId); - m_bufferForGlyph.remove(*it); - rerequestGlyph = true; - } - - if (rerequestGlyph) - filteredPendingInvalidatedGlyphs.append(*it); - - ++it; - } - - // If this cache is still using the glyphs, reset the texture held by them, and mark them - // to be rendered again since they are still needed. - if (!filteredPendingInvalidatedGlyphs.isEmpty()) { - setGlyphsTexture(filteredPendingInvalidatedGlyphs, Texture()); - markGlyphsToRender(filteredPendingInvalidatedGlyphs); - } - } - - { - QList<GlyphPosition> glyphPositions; - - QHash<void *, TextureContent> textureContentForBuffer; - { - QHash<quint32, PendingGlyph>::iterator it = m_pendingReadyGlyphs.begin(); - while (it != m_pendingReadyGlyphs.end()) { - void *currentGlyphBuffer = m_bufferForGlyph.value(it.key(), 0); - if (currentGlyphBuffer != 0) { - if (!m_sharedGraphicsCache->dereferenceBuffer(currentGlyphBuffer)) { - Q_ASSERT(!textureContentForBuffer.contains(currentGlyphBuffer)); - } - } - - PendingGlyph &pendingGlyph = it.value(); - - // We don't ref or deref the buffer here, since it was already referenced when - // added to the pending ready glyphs - m_bufferForGlyph[it.key()] = pendingGlyph.buffer; - - textureContentForBuffer[pendingGlyph.buffer].size = pendingGlyph.bufferSize; - textureContentForBuffer[pendingGlyph.buffer].glyphs.append(it.key()); - - GlyphPosition glyphPosition; - glyphPosition.glyph = it.key(); - glyphPosition.position = pendingGlyph.position; - - glyphPositions.append(glyphPosition); - - ++it; - } - } - - setGlyphsPosition(glyphPositions); - - { - QHash<void *, TextureContent>::const_iterator it = textureContentForBuffer.constBegin(); - while (it != textureContentForBuffer.constEnd()) { - Texture texture; - texture.textureId = m_sharedGraphicsCache->textureIdForBuffer(it.key()); - texture.size = m_sharedGraphicsCache->sizeOfBuffer(it.key()); - - setGlyphsTexture(it.value().glyphs, texture); - - ++it; - } - } - } - - m_pendingMissingGlyphs.clear(); - m_pendingInvalidatedGlyphs.clear(); - m_pendingReadyGlyphs.clear(); - } -} - -void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &cacheId, - void *bufferId, - const QVector<quint32> &itemIds, - const QVector<QPoint> &positions) -{ - bool requestedItemsInList = false; - { - QMutexLocker locker(&m_pendingGlyphsMutex); - if (m_cacheId != cacheId) - return; - -#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) - qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs)", - cacheId.constData(), itemIds.size()); -#endif - - for (int i=0; i<itemIds.size(); ++i) { - if (m_requestedGlyphsThatHaveNotBeenReturned.contains(itemIds.at(i))) { - requestedItemsInList = true; - break; - } - } - } - - if (requestedItemsInList) - reportItemsUpdated(cacheId, bufferId,itemIds, positions); -} - -void QSGSharedDistanceFieldGlyphCache::reportItemsUpdated(const QByteArray &cacheId, - void *bufferId, - const QVector<quint32> &itemIds, - const QVector<QPoint> &positions) -{ - { - QMutexLocker locker(&m_pendingGlyphsMutex); - if (m_cacheId != cacheId) - return; - - Q_ASSERT(itemIds.size() == positions.size()); - -#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) - qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsUpdated() called for %s (%d glyphs)", - cacheId.constData(), itemIds.size()); -#endif - - for (int i=0; i<itemIds.size(); ++i) { - if (m_requestedGlyphs.contains(itemIds.at(i))) { - PendingGlyph &pendingGlyph = m_pendingReadyGlyphs[itemIds.at(i)]; - void *oldBuffer = pendingGlyph.buffer; - - pendingGlyph.buffer = bufferId; - pendingGlyph.position = positions.at(i); - - m_sharedGraphicsCache->referenceBuffer(bufferId); - if (oldBuffer != 0) - m_sharedGraphicsCache->dereferenceBuffer(oldBuffer); - - m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i)); - } - } - } - - m_pendingGlyphsCondition.wakeAll(); - emit glyphsPending(); -} - -void QSGSharedDistanceFieldGlyphCache::reportItemsInvalidated(const QByteArray &cacheId, - const QVector<quint32> &itemIds) -{ - { - QMutexLocker locker(&m_pendingGlyphsMutex); - if (m_cacheId != cacheId) - return; - - for (int i=0; i<itemIds.size(); ++i) { - if (m_requestedGlyphs.contains(itemIds.at(i))) - m_pendingInvalidatedGlyphs.insert(itemIds.at(i)); - } - } - - emit glyphsPending(); -} - - -void QSGSharedDistanceFieldGlyphCache::reportItemsMissing(const QByteArray &cacheId, - const QVector<quint32> &itemIds) -{ - { - QMutexLocker locker(&m_pendingGlyphsMutex); - if (m_cacheId != cacheId) - return; - -#if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) - qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsMissing() called for %s (%d glyphs)", - cacheId.constData(), itemIds.size()); -#endif - - for (int i=0; i<itemIds.size(); ++i) { - if (m_requestedGlyphsThatHaveNotBeenReturned.remove(itemIds.at(i))) - m_pendingMissingGlyphs.insert(itemIds.at(i)); - } - } - - m_pendingGlyphsCondition.wakeAll(); - emit glyphsPending(); -} - -void QSGSharedDistanceFieldGlyphCache::sceneGraphUpdateStarted() -{ - m_isInSceneGraphUpdate = true; - m_hasPostedEvents = false; -} - -void QSGSharedDistanceFieldGlyphCache::sceneGraphUpdateDone() -{ - m_isInSceneGraphUpdate = false; - - if (m_hasPostedEvents) { - QSGMainThreadInvoker *invoker = QSGMainThreadInvoker::instance(); - QCoreApplication::postEvent(invoker, new QSGEndRequestBatchEvent(m_sharedGraphicsCache)); - m_hasPostedEvents = false; - } -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h deleted file mode 100644 index aee77c49c6..0000000000 --- a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSGSHAREDDISTANCEFIELDGLYPHCACHE_H -#define QSGSHAREDDISTANCEFIELDGLYPHCACHE_H - -#include <QtCore/qwaitcondition.h> -#include <private/qsgadaptationlayer_p.h> - -QT_BEGIN_NAMESPACE - -class QPlatformSharedGraphicsCache; -class QSGSharedDistanceFieldGlyphCache : public QObject, public QSGDistanceFieldGlyphCache -{ - Q_OBJECT -public: - explicit QSGSharedDistanceFieldGlyphCache(const QByteArray &cacheId, - QPlatformSharedGraphicsCache *sharedGraphicsCache, - QSGDistanceFieldGlyphCacheManager *man, - QOpenGLContext *c, - const QRawFont &font); - ~QSGSharedDistanceFieldGlyphCache(); - - void registerOwnerElement(QQuickItem *ownerElement); - void unregisterOwnerElement(QQuickItem *ownerElement); - void processPendingGlyphs(); - - void requestGlyphs(const QSet<glyph_t> &glyphs); - void referenceGlyphs(const QSet<glyph_t> &glyphs); - void storeGlyphs(const QList<QDistanceField> &glyphs); - void releaseGlyphs(const QSet<glyph_t> &glyphs); - -Q_SIGNALS: - void glyphsPending(); - -private Q_SLOTS: - void reportItemsMissing(const QByteArray &cacheId, const QVector<quint32> &itemIds); - void reportItemsAvailable(const QByteArray &cacheId, - void *bufferId, - const QVector<quint32> &itemIds, - const QVector<QPoint> &positions); - void reportItemsUpdated(const QByteArray &cacheId, - void *bufferId, - const QVector<quint32> &itemIds, - const QVector<QPoint> &positions); - void reportItemsInvalidated(const QByteArray &cacheId, const QVector<quint32> &itemIds); - - void sceneGraphUpdateStarted(); - void sceneGraphUpdateDone(); - -private: - void waitForGlyphs(); - void saveTexture(GLuint textureId, int width, int height); - - QSet<quint32> m_requestedGlyphsThatHaveNotBeenReturned; - QSet<quint32> m_requestedGlyphs; - QWaitCondition m_pendingGlyphsCondition; - QByteArray m_cacheId; - QPlatformSharedGraphicsCache *m_sharedGraphicsCache; - QMutex m_pendingGlyphsMutex; - - QSet<glyph_t> m_pendingInvalidatedGlyphs; - QSet<glyph_t> m_pendingMissingGlyphs; - - struct PendingGlyph - { - PendingGlyph() : buffer(0) {} - - void *buffer; - QSize bufferSize; - QPoint position; - }; - - struct Owner - { - Owner() : ref(0) {} - Owner(const Owner &o) : item(o.item), ref(o.ref) {} - Owner &operator =(const Owner &o) { item = o.item; ref = o.ref; return *this; } - - QPointer<QQuickItem> item; - int ref; - }; - - QHash<quint32, PendingGlyph> m_pendingReadyGlyphs; - QHash<glyph_t, void *> m_bufferForGlyph; - QHash<QQuickItem *, Owner> m_registeredOwners; - - bool m_isInSceneGraphUpdate; - bool m_hasPostedEvents; -}; - -QT_END_NAMESPACE - -#endif // QSGSHAREDDISTANCEFIELDGLYPHCACHE_H diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 2cebbaf484..4b78fefa99 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -54,7 +54,8 @@ #include <private/qquickanimatorcontroller_p.h> #include <private/qquickprofiler_p.h> -#include <private/qqmldebugservice_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qqmldebugconnector_p.h> #include <private/qquickshadereffectnode_p.h> @@ -147,6 +148,9 @@ const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4); // called. const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5); +// Passed by the window when there is a render job to run +const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6); + template <typename T> T *windowFor(const QList<T> &list, QQuickWindow *window) { for (int i=0; i<list.size(); ++i) { @@ -200,6 +204,14 @@ public: QImage *image; }; +class WMJobEvent : public WMWindowEvent +{ +public: + WMJobEvent(QQuickWindow *c, QRunnable *postedJob) + : WMWindowEvent(c, WM_PostJob), job(postedJob) {} + ~WMJobEvent() { delete job; } + QRunnable *job; +}; class QSGRenderThreadEventQueue : public QQueue<QEvent *> { @@ -345,7 +357,6 @@ bool QSGRenderThread::event(QEvent *e) if (window) { QQuickWindowPrivate::get(window)->fireAboutToStop(); qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- window removed"; - gl->doneCurrent(); window = 0; } waitCondition.wakeOne(); @@ -396,20 +407,21 @@ bool QSGRenderThread::event(QEvent *e) case WM_Grab: { qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_Grab"; WMGrabEvent *ce = static_cast<WMGrabEvent *>(e); - Q_ASSERT(ce->window == window); + Q_ASSERT(ce->window); + Q_ASSERT(ce->window == window || !window); mutex.lock(); - if (window) { - gl->makeCurrent(window); + if (ce->window) { + gl->makeCurrent(ce->window); qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- sync scene graph"; - QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(ce->window); d->syncSceneGraph(); qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering scene graph"; - QQuickWindowPrivate::get(window)->renderSceneGraph(windowSize); + QQuickWindowPrivate::get(ce->window)->renderSceneGraph(ce->window->size()); qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- grabbing result"; - *ce->image = qt_gl_read_framebuffer(windowSize * window->effectiveDevicePixelRatio(), false, false); + *ce->image = qt_gl_read_framebuffer(windowSize * ce->window->effectiveDevicePixelRatio(), false, false); } qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- waking gui to handle result"; waitCondition.wakeOne(); @@ -417,6 +429,20 @@ bool QSGRenderThread::event(QEvent *e) return true; } + case WM_PostJob: { + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_PostJob"; + WMJobEvent *ce = static_cast<WMJobEvent *>(e); + Q_ASSERT(ce->window == window); + if (window) { + gl->makeCurrent(window); + ce->job->run(); + delete ce->job; + ce->job = 0; + qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- job done"; + } + return true; + } + case WM_RequestRepaint: qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_RequestPaint"; // When GUI posts this event, it is followed by a polishAndSync, so we mustn't @@ -666,7 +692,7 @@ void QSGRenderThread::run() qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "run()"; animatorDriver = sgrc->sceneGraphContext()->createAnimationDriver(0); animatorDriver->install(); - if (QQmlDebugService::isDebuggingEnabled()) + if (QQmlDebugConnector::service<QQmlProfilerService>()) QQuickProfiler::registerAnimationCallback(); while (active) { @@ -993,20 +1019,20 @@ void QSGThreadedRenderLoop::maybeUpdate(Window *w) if (!QCoreApplication::instance()) return; + if (!w || !w->thread->isRunning()) + return; + QThread *current = QThread::currentThread(); if (current != QCoreApplication::instance()->thread() && (current != w->thread || !m_lockedForSync)) { qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()"; return; } - if (!w || !w->thread->isRunning()) { - return; - } qCDebug(QSG_LOG_RENDERLOOP) << "update from item" << w->window; // Call this function from the Gui thread later as startTimer cannot be // called from the render thread. - if (QThread::currentThread() == w->thread) { + if (current == w->thread) { qCDebug(QSG_LOG_RENDERLOOP) << "- on render thread"; w->updateDuringSync = true; return; @@ -1242,6 +1268,18 @@ QImage QSGThreadedRenderLoop::grab(QQuickWindow *window) return result; } +/*! + * Posts a new job event to the render thread. + * Returns true if posting succeeded. + */ +void QSGThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job) +{ + Window *w = windowFor(m_windows, window); + if (w && w->thread && w->thread->window) + w->thread->postEvent(new WMJobEvent(window, job)); + else + delete job; +} #include "qsgthreadedrenderloop.moc" diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h index d5ffbf10a3..67df9dcd31 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h +++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h @@ -71,6 +71,7 @@ public: void releaseResources(QQuickWindow *window); bool event(QEvent *); + void postJob(QQuickWindow *window, QRunnable *job); bool interleaveIncubation() const; diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 480ac5e569..84cc2ba135 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -79,7 +79,6 @@ HEADERS += \ $$PWD/qsgdefaultglyphnode_p_p.h \ $$PWD/qsgdefaultimagenode_p.h \ $$PWD/qsgdefaultrectanglenode_p.h \ - $$PWD/qsgshareddistancefieldglyphcache_p.h \ $$PWD/qsgrenderloop_p.h \ $$PWD/qsgthreadedrenderloop_p.h \ $$PWD/qsgwindowsrenderloop_p.h \ @@ -96,7 +95,6 @@ SOURCES += \ $$PWD/qsgdistancefieldglyphnode_p.cpp \ $$PWD/qsgdefaultimagenode.cpp \ $$PWD/qsgdefaultrectanglenode.cpp \ - $$PWD/qsgshareddistancefieldglyphcache.cpp \ $$PWD/qsgrenderloop.cpp \ $$PWD/qsgthreadedrenderloop.cpp \ $$PWD/qsgwindowsrenderloop.cpp \ diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp index b862fa6a2b..8c649fb6bd 100644 --- a/src/quick/scenegraph/util/qsgatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgatlastexture.cpp @@ -112,13 +112,15 @@ void Manager::invalidate() } } -QSGTexture *Manager::create(const QImage &image) +QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel) { - QSGTexture *t = 0; + Texture *t = 0; if (image.width() < m_atlas_size_limit && image.height() < m_atlas_size_limit) { if (!m_atlas) m_atlas = new Atlas(m_atlas_size); t = m_atlas->create(image); + if (!hasAlphaChannel && t->hasAlphaChannel()) + t->setHasAlphaChannel(false); } return t; } diff --git a/src/quick/scenegraph/util/qsgatlastexture_p.h b/src/quick/scenegraph/util/qsgatlastexture_p.h index 399d5fd669..c0f6ab912d 100644 --- a/src/quick/scenegraph/util/qsgatlastexture_p.h +++ b/src/quick/scenegraph/util/qsgatlastexture_p.h @@ -58,7 +58,7 @@ public: Manager(); ~Manager(); - QSGTexture *create(const QImage &image); + QSGTexture *create(const QImage &image, bool hasAlphaChannel); void invalidate(); private: @@ -114,6 +114,7 @@ public: int textureId() const { return m_atlas->textureId(); } QSize textureSize() const { return atlasSubRectWithoutPadding().size(); } + void setHasAlphaChannel(bool alpha) { m_has_alpha = alpha; } bool hasAlphaChannel() const { return m_has_alpha; } bool hasMipmaps() const { return false; } bool isAtlasTexture() const { return true; } diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp index c0ddf25765..8622f8edc1 100644 --- a/src/quick/scenegraph/util/qsgengine.cpp +++ b/src/quick/scenegraph/util/qsgengine.cpp @@ -150,7 +150,7 @@ QSGAbstractRenderer *QSGEngine::createRenderer() const /*! Creates a texture using the data of \a image - Valid \a options are TextureCanUseAtlas + Valid \a options are TextureCanUseAtlas and TextureIsOpaque. The caller takes ownership of the texture and the texture should only be used with this engine. @@ -160,13 +160,12 @@ QSGAbstractRenderer *QSGEngine::createRenderer() const QSGTexture *QSGEngine::createTextureFromImage(const QImage &image, CreateTextureOptions options) const { Q_D(const QSGEngine); - if (!d->sgRenderContext->isValid()) - return 0; - - if (options & TextureCanUseAtlas) - return d->sgRenderContext->createTexture(image); - else - return d->sgRenderContext->createTextureNoAtlas(image); + if (!d->sgRenderContext->isValid()) + return 0; + uint flags = 0; + if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas; + if (!(options & TextureIsOpaque)) flags |= QSGRenderContext::CreateTexture_Alpha; + return d->sgRenderContext->createTexture(image, flags); } /*! diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h index 9a74a02aa1..325d3a9ca2 100644 --- a/src/quick/scenegraph/util/qsgengine.h +++ b/src/quick/scenegraph/util/qsgengine.h @@ -52,7 +52,8 @@ public: enum CreateTextureOption { TextureHasAlphaChannel = 0x0001, TextureOwnsGLTexture = 0x0004, - TextureCanUseAtlas = 0x0008 + TextureCanUseAtlas = 0x0008, + TextureIsOpaque = 0x0010 }; Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption) diff --git a/src/quick/util/qquickanimation_p.h b/src/quick/util/qquickanimation_p.h index 0f6224a831..c4d5cd20cd 100644 --- a/src/quick/util/qquickanimation_p.h +++ b/src/quick/util/qquickanimation_p.h @@ -57,7 +57,6 @@ class Q_QUICK_PRIVATE_EXPORT QQuickAbstractAnimation : public QObject, public QQ Q_INTERFACES(QQmlParserStatus) Q_INTERFACES(QQmlPropertyValueSource) - Q_ENUMS(Loops) Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) Q_PROPERTY(bool alwaysRunToEnd READ alwaysRunToEnd WRITE setAlwaysRunToEnd NOTIFY alwaysRunToEndChanged) @@ -75,6 +74,7 @@ public: virtual ~QQuickAbstractAnimation(); enum Loops { Infinite = -2 }; + Q_ENUM(Loops) bool isRunning() const; void setRunning(bool); @@ -367,7 +367,6 @@ class Q_QUICK_PRIVATE_EXPORT QQuickRotationAnimation : public QQuickPropertyAnim { Q_OBJECT Q_DECLARE_PRIVATE(QQuickRotationAnimation) - Q_ENUMS(RotationDirection) Q_PROPERTY(qreal from READ from WRITE setFrom) Q_PROPERTY(qreal to READ to WRITE setTo) @@ -384,6 +383,7 @@ public: void setTo(qreal); enum RotationDirection { Numerical, Shortest, Clockwise, Counterclockwise }; + Q_ENUM(RotationDirection) RotationDirection direction() const; void setDirection(RotationDirection direction); diff --git a/src/quick/util/qquickanimation_p_p.h b/src/quick/util/qquickanimation_p_p.h index 4224c6d9ed..b40e198cc1 100644 --- a/src/quick/util/qquickanimation_p_p.h +++ b/src/quick/util/qquickanimation_p_p.h @@ -164,7 +164,7 @@ private: T *m_instance; }; -class QQuickAbstractAnimationPrivate : public QObjectPrivate, public QAnimationJobChangeListener +class Q_QUICK_PRIVATE_EXPORT QQuickAbstractAnimationPrivate : public QObjectPrivate, public QAnimationJobChangeListener { Q_DECLARE_PUBLIC(QQuickAbstractAnimation) public: diff --git a/src/quick/util/qquickanimator_p.h b/src/quick/util/qquickanimator_p.h index 4d3a8e9e4f..7647e582b8 100644 --- a/src/quick/util/qquickanimator_p.h +++ b/src/quick/util/qquickanimator_p.h @@ -136,10 +136,9 @@ class Q_QUICK_PRIVATE_EXPORT QQuickRotationAnimator : public QQuickAnimator Q_DECLARE_PRIVATE(QQuickRotationAnimator) Q_PROPERTY(RotationDirection direction READ direction WRITE setDirection NOTIFY directionChanged) - Q_ENUMS(RotationDirection) - public: enum RotationDirection { Numerical, Shortest, Clockwise, Counterclockwise }; + Q_ENUM(RotationDirection) QQuickRotationAnimator(QObject *parent = 0); diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index 2a8e3c281c..0182f8abfb 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -126,9 +126,8 @@ void QQuickAnimatorProxyJob::updateState(QAbstractAnimationJob::State newState, m_controller->startJob(this, m_job); } else if (newState == Stopped) { syncBackCurrentValues(); - if (m_internalState == State_Starting) - m_internalState = State_Stopped; - else if (m_controller) { + m_internalState = State_Stopped; + if (m_controller) { m_controller->stopJob(this, m_job); } } diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h index f8f40b4705..ca70aecb8e 100644 --- a/src/quick/util/qquickanimatorjob_p.h +++ b/src/quick/util/qquickanimatorjob_p.h @@ -151,7 +151,6 @@ protected: int m_duration; - uint m_feedback : 1; uint m_isTransform : 1; uint m_isUniform : 1; uint m_hasBeenRunning : 1; diff --git a/src/quick/util/qquickfontloader_p.h b/src/quick/util/qquickfontloader_p.h index b6ca0b3c94..507d0210ee 100644 --- a/src/quick/util/qquickfontloader_p.h +++ b/src/quick/util/qquickfontloader_p.h @@ -46,7 +46,6 @@ class Q_AUTOTEST_EXPORT QQuickFontLoader : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QQuickFontLoader) - Q_ENUMS(Status) Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) @@ -54,6 +53,7 @@ class Q_AUTOTEST_EXPORT QQuickFontLoader : public QObject public: enum Status { Null = 0, Ready, Loading, Error }; + Q_ENUM(Status) QQuickFontLoader(QObject *parent = 0); ~QQuickFontLoader(); diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp index 6aa7bedc5b..0680a49d6d 100644 --- a/src/quick/util/qquickglobal.cpp +++ b/src/quick/util/qquickglobal.cpp @@ -285,6 +285,7 @@ public: QV4::ScopedValue vbold(scope, obj->get((s = v4->newString(QStringLiteral("bold"))))); QV4::ScopedValue vcap(scope, obj->get((s = v4->newString(QStringLiteral("capitalization"))))); QV4::ScopedValue vfam(scope, obj->get((s = v4->newString(QStringLiteral("family"))))); + QV4::ScopedValue vstyle(scope, obj->get((s = v4->newString(QStringLiteral("styleName"))))); QV4::ScopedValue vital(scope, obj->get((s = v4->newString(QStringLiteral("italic"))))); QV4::ScopedValue vlspac(scope, obj->get((s = v4->newString(QStringLiteral("letterSpacing"))))); QV4::ScopedValue vpixsz(scope, obj->get((s = v4->newString(QStringLiteral("pixelSize"))))); @@ -307,6 +308,10 @@ public: retn.setFamily(vfam->toQString()); if (ok) *ok = true; } + if (vstyle->isString()) { + retn.setStyleName(vstyle->toQString()); + if (ok) *ok = true; + } if (vital->isBoolean()) { retn.setItalic(vital->booleanValue()); if (ok) *ok = true; diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp index a231209cd0..89615a2079 100644 --- a/src/quick/util/qquickimageprovider.cpp +++ b/src/quick/util/qquickimageprovider.cpp @@ -33,6 +33,9 @@ #include "qquickimageprovider.h" +#include "qquickpixmapcache_p.h" +#include <QtQuick/private/qsgcontext_p.h> + QT_BEGIN_NAMESPACE class QQuickImageProviderPrivate @@ -95,6 +98,23 @@ QImage QQuickTextureFactory::image() const return QImage(); } +/*! + Returns a QQuickTextureFactory holding given the image. + + \since 5.6 + */ + +QQuickTextureFactory *QQuickTextureFactory::textureFactoryForImage(const QImage &image) +{ + if (image.isNull()) + return 0; + QQuickTextureFactory *texture = QSGContext::createTextureFactoryFromImage(image); + if (texture) + return texture; + return new QQuickDefaultTextureFactory(image); +} + + /*! \fn QSGTexture *QQuickTextureFactory::createTexture(QQuickWindow *window) const @@ -118,6 +138,68 @@ QImage QQuickTextureFactory::image() const /*! + \class QQuickImageResponse + \since 5.6 + \brief The QQuickImageResponse class provides an interface for asynchronous image loading in QQuickAsyncImageProvider. + \inmodule QtQuick + + The purpose of an image response is to provide a way for image provider jobs to be executed + in an asynchronous way. + + Responses are deleted via \l deleteLater once the finished() signal has been emitted. + If you are using QRunnable as base for your QQuickImageResponse + ensure automatic deletion is disabled. + + \sa QQuickImageProvider +*/ + +/*! + Constructs the image response +*/ +QQuickImageResponse::QQuickImageResponse() +{ +} + +/*! + Destructs the image response +*/ +QQuickImageResponse::~QQuickImageResponse() +{ +} + +/*! + Returns the error string for the job execution. An empty string means no error. +*/ +QString QQuickImageResponse::errorString() const +{ + return QString(); +} + +/*! + This method is used to communicate that the response is no longer required by the engine. + + It may be reimplemented to cancel a request in the provider side, however, it is not mandatory. +*/ +void QQuickImageResponse::cancel() +{ +} + +/*! + \fn void QQuickImageResponse::finished() + + Signals that the job execution has finished (be it successfully, because an error happened or because it was cancelled). + */ + +/*! + \fn QQuickTextureFactory *QQuickImageResponse::textureFactory() const + + Returns the texture factory for the job. You can use QQuickTextureFactory::textureFactoryForImage + if your provider works with QImage. This method is only called when the error string is not empty and the + engine takes ownership of the returned QQuickTextureFactory. + */ + + +/*! \class QQuickImageProvider \since 5.0 \inmodule QtQuick @@ -213,7 +295,7 @@ QImage QQuickTextureFactory::image() const To force asynchronous image loading, even for image sources that do not have the \c asynchronous property set to \c true, you may pass the - \c QQuickImageProvider::ForceAsynchronousImageLoading flag to the image + \c QQmlImageProviderBase::ForceAsynchronousImageLoading flag to the image provider constructor. This ensures that all image requests for the provider are handled in a separate thread. @@ -223,6 +305,12 @@ QImage QQuickTextureFactory::image() const if \l {Image::}{asynchronous} is set to \c true, the value is ignored and the image is loaded synchronously. + Asynchronous image loading for providers of type other than ImageResponse are + executed on a single thread per engine basis. That means that a slow image provider + will block the loading of any other request. To avoid that we suggest using QQuickAsyncImageProvider + and implement threading on the provider side via a \c QThreadPool or similar. + See the \l {imageresponseprovider}{Image Response Provider Example} for a complete implementation. + \section2 Image caching @@ -365,5 +453,41 @@ QQuickTextureFactory *QQuickImageProvider::requestTexture(const QString &id, QSi return 0; } +/*! + \class QQuickAsyncImageProvider + \since 5.6 + \inmodule QtQuick + \brief The QQuickAsyncImageProvider class provides an interface for for asynchronous control of QML image requests. + + \sa QQuickImageProvider +*/ +QQuickAsyncImageProvider::QQuickAsyncImageProvider() + : QQuickImageProvider(ImageResponse, ForceAsynchronousImageLoading) + , d(0) // just as a placeholder in case we need it for the future +{ + Q_UNUSED(d); +} + +QQuickAsyncImageProvider::~QQuickAsyncImageProvider() +{ +} + +/*! + \fn QQuickImageResponse *QQuickAsyncImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize) + + Implement this method to return the job that will provide the texture with \a id. + + The \a id is the requested image source, with the "image:" scheme and + provider identifier removed. For example, if the image \l{Image::}{source} + was "image://myprovider/icons/home", the given \a id would be "icons/home". + + The \a requestedSize corresponds to the \l {Image::sourceSize} requested by + an Image item. If \a requestedSize is a valid size, the image + returned should be of that size. + + \note this method may be called by multiple threads, so ensure the + implementation of this method is reentrant. +*/ + QT_END_NAMESPACE diff --git a/src/quick/util/qquickimageprovider.h b/src/quick/util/qquickimageprovider.h index a2b510f606..cc03eb0fa0 100644 --- a/src/quick/util/qquickimageprovider.h +++ b/src/quick/util/qquickimageprovider.h @@ -43,6 +43,7 @@ QT_BEGIN_NAMESPACE class QQuickImageProviderPrivate; +class QQuickAsyncImageProviderPrivate; class QSGTexture; class QQuickWindow; @@ -56,6 +57,25 @@ public: virtual QSize textureSize() const = 0; virtual int textureByteCount() const = 0; virtual QImage image() const; + + static QQuickTextureFactory *textureFactoryForImage(const QImage &image); +}; + +class Q_QUICK_EXPORT QQuickImageResponse : public QObject +{ +Q_OBJECT +public: + QQuickImageResponse(); + virtual ~QQuickImageResponse(); + + virtual QQuickTextureFactory *textureFactory() const = 0; + virtual QString errorString() const; + +public Q_SLOTS: + virtual void cancel(); + +Q_SIGNALS: + void finished(); }; class Q_QUICK_EXPORT QQuickImageProvider : public QQmlImageProviderBase @@ -81,6 +101,18 @@ private: QQuickImageProviderPrivate *d; }; +class Q_QUICK_EXPORT QQuickAsyncImageProvider : public QQuickImageProvider +{ +public: + QQuickAsyncImageProvider(); + virtual ~QQuickAsyncImageProvider(); + + virtual QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) = 0; + +private: + QQuickAsyncImageProviderPrivate *d; +}; + QT_END_NAMESPACE #endif // QQUICKIMAGEPROVIDER_H diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index eea313eeb1..5230a3cdea 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -265,7 +265,7 @@ public: : QQuickCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise) {} enum ArcDirection { Clockwise, Counterclockwise }; - Q_ENUMS(ArcDirection) + Q_ENUM(ArcDirection) qreal radiusX() const; void setRadiusX(qreal); diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index ddf2ae2393..6f6e91ec99 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -44,7 +44,6 @@ #include <qpa/qplatformintegration.h> #include <QtQuick/private/qsgtexture_p.h> -#include <QtQuick/private/qsgcontext_p.h> #include <QQuickWindow> #include <QCoreApplication> @@ -67,7 +66,7 @@ #include <private/qquickprofiler_p.h> -#define IMAGEREQUEST_MAX_REQUEST_COUNT 8 +#define IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT 8 #define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16 #define CACHE_EXPIRE_TIME 30 #define CACHE_REMOVAL_FRACTION 4 @@ -115,16 +114,6 @@ QSGTexture *QQuickDefaultTextureFactory::createTexture(QQuickWindow *window) con return t; } -static QQuickTextureFactory *textureFactoryForImage(const QImage &image) -{ - if (image.isNull()) - return 0; - QQuickTextureFactory *texture = QSGContext::createTextureFactoryFromImage(image); - if (texture) - return texture; - return new QQuickDefaultTextureFactory(image); -} - class QQuickPixmapReader; class QQuickPixmapData; class QQuickPixmapReply : public QObject @@ -181,6 +170,7 @@ public: virtual bool event(QEvent *e); private slots: void networkRequestDone(); + void asyncResponseFinished(); private: QQuickPixmapReader *reader; }; @@ -205,8 +195,9 @@ protected: private: friend class QQuickPixmapReaderThreadObject; void processJobs(); - void processJob(QQuickPixmapReply *, const QUrl &, const QSize &, AutoTransform); + void processJob(QQuickPixmapReply *, const QUrl &, const QString &, AutoTransform, QQuickImageProvider::ImageType, QQuickImageProvider *); void networkRequestDone(QNetworkReply *); + void asyncResponseFinished(QQuickImageResponse *); QList<QQuickPixmapReply*> jobs; QList<QQuickPixmapReply*> cancelled; @@ -220,7 +211,8 @@ private: QNetworkAccessManager *networkAccessManager(); QNetworkAccessManager *accessManager; - QHash<QNetworkReply*,QQuickPixmapReply*> replies; + QHash<QNetworkReply*,QQuickPixmapReply*> networkJobs; + QHash<QQuickImageResponse*,QQuickPixmapReply*> asyncResponses; static int replyDownloadProgress; static int replyFinished; @@ -439,8 +431,8 @@ QQuickPixmapReader::~QQuickPixmapReader() delete reply; } jobs.clear(); - QList<QQuickPixmapReply*> activeJobs = replies.values(); - foreach (QQuickPixmapReply *reply, activeJobs) { + QList<QQuickPixmapReply*> activeJobs = networkJobs.values() + asyncResponses.values(); + foreach (QQuickPixmapReply *reply, activeJobs ) { if (reply->loading) { cancelled.append(reply); reply->data = 0; @@ -455,7 +447,7 @@ QQuickPixmapReader::~QQuickPixmapReader() void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) { - QQuickPixmapReply *job = replies.take(reply); + QQuickPixmapReply *job = networkJobs.take(reply); if (job) { job->redirectCount++; @@ -472,7 +464,7 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress); QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); - replies.insert(reply, job); + networkJobs.insert(reply, job); return; } } @@ -494,7 +486,7 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) // send completion event to the QQuickPixmapReply mutex.lock(); if (!cancelled.contains(job)) - job->postReply(error, errorString, readSize, textureFactoryForImage(image)); + job->postReply(error, errorString, readSize, QQuickTextureFactory::textureFactoryForImage(image)); mutex.unlock(); } reply->deleteLater(); @@ -503,6 +495,34 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) threadObject->processJobs(); } +void QQuickPixmapReader::asyncResponseFinished(QQuickImageResponse *response) +{ + QQuickPixmapReply *job = asyncResponses.take(response); + + if (job) { + QQuickTextureFactory *t = 0; + QQuickPixmapReply::ReadError error = QQuickPixmapReply::NoError; + QString errorString; + QSize readSize; + if (!response->errorString().isEmpty()) { + error = QQuickPixmapReply::Loading; + errorString = response->errorString(); + } else { + t = response->textureFactory(); + } + mutex.lock(); + if (!cancelled.contains(job)) + job->postReply(error, errorString, t ? t->textureSize() : QSize(), t); + else + delete t; + mutex.unlock(); + } + response->deleteLater(); + + // kick off event loop again incase we have dropped below max request count + threadObject->processJobs(); +} + QQuickPixmapReaderThreadObject::QQuickPixmapReaderThreadObject(QQuickPixmapReader *i) : reader(i) { @@ -529,6 +549,12 @@ void QQuickPixmapReaderThreadObject::networkRequestDone() reader->networkRequestDone(reply); } +void QQuickPixmapReaderThreadObject::asyncResponseFinished() +{ + QQuickImageResponse *response = static_cast<QQuickImageResponse *>(sender()); + reader->asyncResponseFinished(response); +} + void QQuickPixmapReader::processJobs() { QMutexLocker locker(&mutex); @@ -538,16 +564,22 @@ void QQuickPixmapReader::processJobs() return; // Nothing else to do // Clean cancelled jobs - if (cancelled.count()) { + if (!cancelled.isEmpty()) { for (int i = 0; i < cancelled.count(); ++i) { QQuickPixmapReply *job = cancelled.at(i); - QNetworkReply *reply = replies.key(job, 0); + QNetworkReply *reply = networkJobs.key(job, 0); if (reply) { - replies.remove(reply); + networkJobs.remove(reply); if (reply->isRunning()) { // cancel any jobs already started reply->close(); } + } else { + QQuickImageResponse *asyncResponse = asyncResponses.key(job); + if (asyncResponse) { + asyncResponses.remove(asyncResponse); + asyncResponse->cancel(); + } } PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(job->url)); // deleteLater, since not owned by this thread @@ -560,27 +592,34 @@ void QQuickPixmapReader::processJobs() // Find a job we can use bool usableJob = false; for (int i = jobs.count() - 1; !usableJob && i >= 0; i--) { - QQuickPixmapReply *runningJob = jobs[i]; - const QUrl url = runningJob->url; + QQuickPixmapReply *job = jobs[i]; + const QUrl url = job->url; + QString localFile; + QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid; + QQuickImageProvider *provider = 0; if (url.scheme() == QLatin1String("image")) { + provider = static_cast<QQuickImageProvider *>(engine->imageProvider(imageProviderId(url))); + if (provider) + imageType = provider->imageType(); + usableJob = true; } else { - const QString localFile = QQmlFile::urlToLocalFileOrQrc(url); - usableJob = !localFile.isEmpty() || replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT; + localFile = QQmlFile::urlToLocalFileOrQrc(url); + usableJob = !localFile.isEmpty() || networkJobs.count() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT; } + if (usableJob) { jobs.removeAt(i); - runningJob->loading = true; + job->loading = true; PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url)); - QSize requestSize = runningJob->requestSize; - AutoTransform autoTransform = runningJob->autoTransform; + AutoTransform autoTransform = job->autoTransform; locker.unlock(); - processJob(runningJob, url, requestSize, autoTransform); + processJob(job, url, localFile, autoTransform, imageType, provider); locker.relock(); } } @@ -591,79 +630,97 @@ void QQuickPixmapReader::processJobs() } } -void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &url, - const QSize &requestSize, AutoTransform autoTransform) +void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &url, const QString &localFile, + AutoTransform autoTransform, QQuickImageProvider::ImageType imageType, QQuickImageProvider *provider) { // fetch if (url.scheme() == QLatin1String("image")) { // Use QQuickImageProvider QSize readSize; - QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid; - QQuickImageProvider *provider = static_cast<QQuickImageProvider *>(engine->imageProvider(imageProviderId(url))); - if (provider) - imageType = provider->imageType(); + switch (imageType) { + case QQuickImageProvider::Invalid: + { + QString errorStr = QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString()); + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(QQuickPixmapReply::Loading, errorStr, readSize, 0); + mutex.unlock(); + break; + } - if (imageType == QQuickImageProvider::Invalid) { - QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::Loading; - QString errorStr = QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString()); - QImage image; - mutex.lock(); - if (!cancelled.contains(runningJob)) - runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(image)); - mutex.unlock(); - } else if (imageType == QQuickImageProvider::Image) { - QImage image = provider->requestImage(imageId(url), &readSize, requestSize); - QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; - QString errorStr; - if (image.isNull()) { - errorCode = QQuickPixmapReply::Loading; - errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + case QQuickImageProvider::Image: + { + QImage image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize); + QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; + QString errorStr; + if (image.isNull()) { + errorCode = QQuickPixmapReply::Loading; + errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + } + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(image)); + mutex.unlock(); + break; } - mutex.lock(); - if (!cancelled.contains(runningJob)) - runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(image)); - mutex.unlock(); - } else if (imageType == QQuickImageProvider::Pixmap) { - const QPixmap pixmap = provider->requestPixmap(imageId(url), &readSize, requestSize); - QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; - QString errorStr; - if (pixmap.isNull()) { - errorCode = QQuickPixmapReply::Loading; - errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + + case QQuickImageProvider::Pixmap: + { + const QPixmap pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize); + QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; + QString errorStr; + if (pixmap.isNull()) { + errorCode = QQuickPixmapReply::Loading; + errorStr = QQuickPixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + } + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage())); + mutex.unlock(); + break; } - mutex.lock(); - if (!cancelled.contains(runningJob)) - runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(pixmap.toImage())); - mutex.unlock(); - } else { - QQuickTextureFactory *t = provider->requestTexture(imageId(url), &readSize, requestSize); - QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; - QString errorStr; - if (!t) { - errorCode = QQuickPixmapReply::Loading; - errorStr = QQuickPixmap::tr("Failed to get texture from provider: %1").arg(url.toString()); + + case QQuickImageProvider::Texture: + { + QQuickTextureFactory *t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize); + QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; + QString errorStr; + if (!t) { + errorCode = QQuickPixmapReply::Loading; + errorStr = QQuickPixmap::tr("Failed to get texture from provider: %1").arg(url.toString()); + } + mutex.lock(); + if (!cancelled.contains(runningJob)) + runningJob->postReply(errorCode, errorStr, readSize, t); + else + delete t; + mutex.unlock(); + break; } - mutex.lock(); - if (!cancelled.contains(runningJob)) - runningJob->postReply(errorCode, errorStr, readSize, t); - else - delete t; - mutex.unlock(); + case QQuickImageProvider::ImageResponse: + { + QQuickAsyncImageProvider *asyncProvider = static_cast<QQuickAsyncImageProvider*>(provider); + QQuickImageResponse *response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize); + + QObject::connect(response, SIGNAL(finished()), threadObject, SLOT(asyncResponseFinished())); + + asyncResponses.insert(response, runningJob); + break; + } } } else { - QString lf = QQmlFile::urlToLocalFileOrQrc(url); - if (!lf.isEmpty()) { + if (!localFile.isEmpty()) { // Image is local - load/decode immediately QImage image; QQuickPixmapReply::ReadError errorCode = QQuickPixmapReply::NoError; QString errorStr; - QFile f(lf); + QFile f(localFile); QSize readSize; if (f.open(QIODevice::ReadOnly)) { - if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize, autoTransform)) + if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize, autoTransform)) errorCode = QQuickPixmapReply::Loading; } else { errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString()); @@ -671,7 +728,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u } mutex.lock(); if (!cancelled.contains(runningJob)) - runningJob->postReply(errorCode, errorStr, readSize, textureFactoryForImage(image)); + runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(image)); mutex.unlock(); } else { // Network resource @@ -682,7 +739,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); - replies.insert(reply, runningJob); + networkJobs.insert(reply, runningJob); } } } @@ -775,8 +832,6 @@ inline uint qHash(const QQuickPixmapKey &key) return qHash(*key.url) ^ (key.size->width()*7) ^ (key.size->height()*17) ^ (key.autoTransform * 0x5c5c5c5c); } -class QSGContext; - class QQuickPixmapStore : public QObject { Q_OBJECT @@ -1084,7 +1139,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QImage image = provider->requestImage(imageId(url), &readSize, requestSize); if (!image.isNull()) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, textureFactoryForImage(image), readSize, requestSize, autoTransform, UsePluginDefault); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, autoTransform, UsePluginDefault); } } case QQuickImageProvider::Pixmap: @@ -1092,9 +1147,14 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QPixmap pixmap = provider->requestPixmap(imageId(url), &readSize, requestSize); if (!pixmap.isNull()) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, textureFactoryForImage(pixmap.toImage()), readSize, requestSize, autoTransform, UsePluginDefault); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()), readSize, requestSize, autoTransform, UsePluginDefault); } } + case QQuickImageProvider::ImageResponse: + { + // Fall through, ImageResponse providers never get here + Q_ASSERT(imageType != QQuickImageProvider::ImageResponse && "Sync call to ImageResponse provider"); + } } // provider has bad image type, or provider returned null image @@ -1115,7 +1175,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q AutoTransform appliedTransform = autoTransform; if (readImage(url, &f, &image, &errorString, &readSize, requestSize, appliedTransform)) { *ok = true; - return new QQuickPixmapData(declarativePixmap, url, textureFactoryForImage(image), readSize, requestSize, autoTransform, appliedTransform); + return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, autoTransform, appliedTransform); } errorString = QQuickPixmap::tr("Invalid image data: %1").arg(url.toString()); @@ -1252,7 +1312,7 @@ void QQuickPixmap::setImage(const QImage &p) clear(); if (!p.isNull()) - d = new QQuickPixmapData(this, textureFactoryForImage(p)); + d = new QQuickPixmapData(this, QQuickTextureFactory::textureFactoryForImage(p)); } void QQuickPixmap::setPixmap(const QQuickPixmap &other) diff --git a/src/quick/util/qquickprofiler.cpp b/src/quick/util/qquickprofiler.cpp index d9132a9cb2..77ffda474a 100644 --- a/src/quick/util/qquickprofiler.cpp +++ b/src/quick/util/qquickprofiler.cpp @@ -33,8 +33,7 @@ #include "qquickprofiler_p.h" #include <QCoreApplication> -#include <private/qqmldebugservice_p.h> -#include <private/qqmlprofilerservice_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> QT_BEGIN_NAMESPACE @@ -113,18 +112,22 @@ void QQuickProfilerData::toByteArrays(QList<QByteArray> &messages) const qint64 QQuickProfiler::sendMessages(qint64 until, QList<QByteArray> &messages) { QMutexLocker lock(&m_dataMutex); - while (next < m_data.size() && m_data[next].time <= until) { - m_data[next++].toByteArrays(messages); + while (next < m_data.size()) { + if (m_data[next].time <= until) + m_data[next++].toByteArrays(messages); + else + return m_data[next].time; } - return next < m_data.size() ? m_data[next].time : -1; + m_data.clear(); + next = 0; + return -1; } -void QQuickProfiler::initialize() +void QQuickProfiler::initialize(QQmlProfilerService *service) { Q_ASSERT(s_instance == 0); - QQmlProfilerService *service = QQmlProfilerService::instance(); s_instance = new QQuickProfiler(service); - QQmlProfilerService::instance()->addGlobalProfiler(s_instance); + service->addGlobalProfiler(s_instance); } void animationTimerCallback(qint64 delta) @@ -196,17 +199,12 @@ void QQuickProfiler::stopProfilingImpl() { QMutexLocker lock(&m_dataMutex); featuresEnabled = 0; - next = 0; } service->dataReady(this); } void QQuickProfiler::reportDataImpl() { - { - QMutexLocker lock(&m_dataMutex); - next = 0; - } service->dataReady(this); } diff --git a/src/quick/util/qquickprofiler_p.h b/src/quick/util/qquickprofiler_p.h index aaed4bd60e..6b6e7fa062 100644 --- a/src/quick/util/qquickprofiler_p.h +++ b/src/quick/util/qquickprofiler_p.h @@ -318,7 +318,7 @@ public: return featuresEnabled & (1 << QQuickProfiler::ProfileSceneGraph); } - static void initialize(); + static void initialize(QQmlProfilerService *service); virtual ~QQuickProfiler(); diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index 6c333c6b13..58d78a5d84 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -145,12 +145,12 @@ public: QQmlBoundSignalExpressionPointer reverseExpression; QQmlBoundSignalExpressionPointer rewindExpression; - virtual void execute(Reason) { + virtual void execute() { QQmlPropertyPrivate::setSignalExpression(property, expression); } virtual bool isReversable() { return true; } - virtual void reverse(Reason) { + virtual void reverse() { QQmlPropertyPrivate::setSignalExpression(property, reverseExpression); } @@ -464,10 +464,10 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions() // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString) // so that we can avoid creating then destroying the binding in this case. a.toValue = newBinding->evaluate(); - newBinding->destroy(); + delete newBinding; } else { newBinding->setTarget(prop); - a.toBinding = QQmlAbstractBinding::getPointer(newBinding); + a.toBinding = newBinding; a.deletableToBinding = true; } @@ -558,11 +558,7 @@ void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &val if (entry.name == name) { expressionIterator.remove(); if (state() && state()->isStateActive()) { - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(d->property(name)); - if (oldBinding) { - QQmlPropertyPrivate::setBinding(d->property(name), 0); - oldBinding->destroy(); - } + QQmlPropertyPrivate::removeBinding(d->property(name)); d->property(name).write(value); } @@ -624,15 +620,9 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString if (entry.name == name) { entry.expression = expression; if (state() && state()->isStateActive()) { - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(d->property(name)); - if (oldBinding) { - QQmlPropertyPrivate::setBinding(d->property(name), 0); - oldBinding->destroy(); - } - QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this)); newBinding->setTarget(d->property(name)); - QQmlPropertyPrivate::setBinding(d->property(name), newBinding, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); } return; } @@ -651,7 +641,7 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this)); newBinding->setTarget(d->property(name)); - QQmlPropertyPrivate::setBinding(d->property(name), newBinding, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); } else { QQuickStateAction action; action.restore = restoreEntryValues(); @@ -666,10 +656,10 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString) // so that we can avoid creating then destroying the binding in this case. action.toValue = newBinding->evaluate(); - newBinding->destroy(); + delete newBinding; } else { - newBinding->setTarget(d->property(name)); - action.toBinding = QQmlAbstractBinding::getPointer(newBinding); + newBinding->setTarget(action.property); + action.toBinding = newBinding; action.deletableToBinding = true; state()->addEntryToRevertList(action); @@ -677,7 +667,7 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString if (oldBinding) oldBinding->setEnabled(false, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); - QQmlPropertyPrivate::setBinding(action.property, newBinding, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); + QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor); } } } diff --git a/src/quick/util/qquicksmoothedanimation_p.h b/src/quick/util/qquicksmoothedanimation_p.h index 68479aa629..efac9217f5 100644 --- a/src/quick/util/qquicksmoothedanimation_p.h +++ b/src/quick/util/qquicksmoothedanimation_p.h @@ -47,7 +47,6 @@ class Q_AUTOTEST_EXPORT QQuickSmoothedAnimation : public QQuickNumberAnimation { Q_OBJECT Q_DECLARE_PRIVATE(QQuickSmoothedAnimation) - Q_ENUMS(ReversingMode) Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity NOTIFY velocityChanged) Q_PROPERTY(ReversingMode reversingMode READ reversingMode WRITE setReversingMode NOTIFY reversingModeChanged) @@ -55,6 +54,7 @@ class Q_AUTOTEST_EXPORT QQuickSmoothedAnimation : public QQuickNumberAnimation public: enum ReversingMode { Eased, Immediate, Sync }; + Q_ENUM(ReversingMode) QQuickSmoothedAnimation(QObject *parent = 0); ~QQuickSmoothedAnimation(); diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp index 98d7a76c7e..be676680a6 100644 --- a/src/quick/util/qquickstate.cpp +++ b/src/quick/util/qquickstate.cpp @@ -78,7 +78,7 @@ QQuickStateActionEvent::~QQuickStateActionEvent() { } -void QQuickStateActionEvent::execute(Reason) +void QQuickStateActionEvent::execute() { } @@ -87,7 +87,7 @@ bool QQuickStateActionEvent::isReversable() return false; } -void QQuickStateActionEvent::reverse(Reason) +void QQuickStateActionEvent::reverse() { } @@ -157,11 +157,6 @@ QQuickState::~QQuickState() Q_D(QQuickState); if (d->group) d->group->removeState(this); - - foreach (const QQuickSimpleAction &action, d->revertList) { - if (action.binding()) - action.binding()->destroy(); - } } /*! @@ -361,8 +356,7 @@ void QQuickState::cancel() void QQuickStateAction::deleteFromBinding() { if (fromBinding) { - QQmlPropertyPrivate::setBinding(property, 0); - fromBinding->destroy(); + QQmlPropertyPrivate::removeBinding(property); fromBinding = 0; } } @@ -413,9 +407,6 @@ bool QQuickState::changeBindingInRevertList(QObject *target, const QString &name while (revertListIterator.hasNext()) { QQuickSimpleAction &simpleAction = revertListIterator.next(); if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { - if (simpleAction.binding()) - simpleAction.binding()->destroy(); - simpleAction.setBinding(binding); return true; } @@ -435,15 +426,11 @@ bool QQuickState::removeEntryFromRevertList(QObject *target, const QString &name while (revertListIterator.hasNext()) { QQuickSimpleAction &simpleAction = revertListIterator.next(); if (simpleAction.property().object() == target && simpleAction.property().name() == name) { - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property()); - if (oldBinding) { - QQmlPropertyPrivate::setBinding(simpleAction.property(), 0); - oldBinding->destroy(); - } + QQmlPropertyPrivate::removeBinding(simpleAction.property()); simpleAction.property().write(simpleAction.value()); if (simpleAction.binding()) - QQmlPropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + QQmlPropertyPrivate::setBinding(simpleAction.binding()); revertListIterator.remove(); return true; @@ -473,15 +460,11 @@ void QQuickState::removeAllEntriesFromRevertList(QObject *target) while (revertListIterator.hasNext()) { QQuickSimpleAction &simpleAction = revertListIterator.next(); if (simpleAction.property().object() == target) { - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property()); - if (oldBinding) { - QQmlPropertyPrivate::setBinding(simpleAction.property(), 0); - oldBinding->destroy(); - } + QQmlPropertyPrivate::removeBinding(simpleAction.property()); simpleAction.property().write(simpleAction.value()); if (simpleAction.binding()) - QQmlPropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + QQmlPropertyPrivate::setBinding(simpleAction.binding()); revertListIterator.remove(); } @@ -494,18 +477,15 @@ void QQuickState::addEntriesToRevertList(const QList<QQuickStateAction> &actionL Q_D(QQuickState); if (isStateActive()) { QList<QQuickSimpleAction> simpleActionList; + simpleActionList.reserve(actionList.count()); QListIterator<QQuickStateAction> actionListIterator(actionList); while(actionListIterator.hasNext()) { const QQuickStateAction &action = actionListIterator.next(); QQuickSimpleAction simpleAction(action); action.property.write(action.toValue); - if (!action.toBinding.isNull()) { - QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property()); - if (oldBinding) - QQmlPropertyPrivate::setBinding(simpleAction.property(), 0); - QQmlPropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QQmlPropertyPrivate::DontRemoveBinding); - } + if (action.toBinding) + QQmlPropertyPrivate::setBinding(action.toBinding.data()); simpleActionList.append(simpleAction); } @@ -619,7 +599,7 @@ void QQuickState::apply(QQuickTransition *trans, QQuickState *revert) for (int jj = 0; jj < d->revertList.count(); ++jj) { if (d->revertList.at(jj).property() == action.property) { found = true; - if (d->revertList.at(jj).binding() != action.fromBinding) { + if (d->revertList.at(jj).binding() != action.fromBinding.data()) { action.deleteFromBinding(); } break; @@ -663,16 +643,13 @@ void QQuickState::apply(QQuickTransition *trans, QQuickState *revert) } if (!found) { QVariant cur = d->revertList.at(ii).property().read(); - QQmlAbstractBinding *delBinding = - QQmlPropertyPrivate::setBinding(d->revertList.at(ii).property(), 0); - if (delBinding) - delBinding->destroy(); + QQmlPropertyPrivate::removeBinding(d->revertList.at(ii).property()); QQuickStateAction a; a.property = d->revertList.at(ii).property(); a.fromValue = cur; a.toValue = d->revertList.at(ii).value(); - a.toBinding = QQmlAbstractBinding::getPointer(d->revertList.at(ii).binding()); + a.toBinding = d->revertList.at(ii).binding(); a.specifiedObject = d->revertList.at(ii).specifiedObject(); a.specifiedProperty = d->revertList.at(ii).specifiedProperty(); a.event = d->revertList.at(ii).event(); diff --git a/src/quick/util/qquickstate_p.h b/src/quick/util/qquickstate_p.h index 0c774635d8..1870b70626 100644 --- a/src/quick/util/qquickstate_p.h +++ b/src/quick/util/qquickstate_p.h @@ -39,13 +39,14 @@ #include <QtCore/qobject.h> #include <QtCore/qsharedpointer.h> #include <private/qtquickglobal_p.h> +#include <private/qqmlabstractbinding_p.h> QT_BEGIN_NAMESPACE class QQuickStateActionEvent; -class QQmlAbstractBinding; class QQmlBinding; class QQmlExpression; + class QQuickStateAction { public: @@ -63,8 +64,8 @@ public: QVariant fromValue; QVariant toValue; - QQmlAbstractBinding *fromBinding; - QWeakPointer<QQmlAbstractBinding> toBinding; + QQmlAbstractBinding::Ptr fromBinding; + QQmlAbstractBinding::Ptr toBinding; QQuickStateActionEvent *event; //strictly for matching @@ -80,13 +81,12 @@ public: virtual ~QQuickStateActionEvent(); enum EventType { Script, SignalHandler, ParentChange, AnchorChanges }; - enum Reason { ActualChange, FastForward }; virtual EventType type() const = 0; - virtual void execute(Reason reason = ActualChange); + virtual void execute(); virtual bool isReversable(); - virtual void reverse(Reason reason = ActualChange); + virtual void reverse(); virtual void saveOriginals() {} virtual bool needsCopy() { return false; } virtual void copyOriginals(QQuickStateActionEvent *) {} diff --git a/src/quick/util/qquickstate_p_p.h b/src/quick/util/qquickstate_p_p.h index fc589f0d2d..e6ecb424e5 100644 --- a/src/quick/util/qquickstate_p_p.h +++ b/src/quick/util/qquickstate_p_p.h @@ -71,7 +71,7 @@ public: if (state == StartState) { m_value = a.fromValue; if (QQmlPropertyPrivate::binding(m_property)) { - m_binding = QQmlAbstractBinding::getPointer(QQmlPropertyPrivate::binding(m_property)); + m_binding = QQmlPropertyPrivate::binding(m_property); } m_reverseEvent = true; } else { @@ -88,7 +88,7 @@ public: QQuickSimpleAction(const QQuickSimpleAction &other) : m_property(other.m_property), m_value(other.m_value), - m_binding(QQmlAbstractBinding::getPointer(other.binding())), + m_binding(other.binding()), m_specifiedObject(other.m_specifiedObject), m_specifiedProperty(other.m_specifiedProperty), m_event(other.m_event), @@ -100,7 +100,7 @@ public: { m_property = other.m_property; m_value = other.m_value; - m_binding = QQmlAbstractBinding::getPointer(other.binding()); + m_binding = other.binding(); m_specifiedObject = other.m_specifiedObject; m_specifiedProperty = other.m_specifiedProperty; m_event = other.m_event; @@ -131,7 +131,7 @@ public: void setBinding(QQmlAbstractBinding *binding) { - m_binding = QQmlAbstractBinding::getPointer(binding); + m_binding = binding; } QQmlAbstractBinding *binding() const @@ -162,7 +162,7 @@ public: private: QQmlProperty m_property; QVariant m_value; - QQmlAbstractBinding::Pointer m_binding; + QQmlAbstractBinding::Ptr m_binding; QObject *m_specifiedObject; QString m_specifiedProperty; QQuickStateActionEvent *m_event; diff --git a/src/quick/util/qquickstatechangescript.cpp b/src/quick/util/qquickstatechangescript.cpp index 6d25b9791d..c276183d62 100644 --- a/src/quick/util/qquickstatechangescript.cpp +++ b/src/quick/util/qquickstatechangescript.cpp @@ -118,7 +118,7 @@ void QQuickStateChangeScript::setName(const QString &n) d->name = n; } -void QQuickStateChangeScript::execute(Reason) +void QQuickStateChangeScript::execute() { Q_D(QQuickStateChangeScript); if (!d->script.isEmpty()) { diff --git a/src/quick/util/qquickstatechangescript_p.h b/src/quick/util/qquickstatechangescript_p.h index 4ff6f0db3e..6c019a43d2 100644 --- a/src/quick/util/qquickstatechangescript_p.h +++ b/src/quick/util/qquickstatechangescript_p.h @@ -62,7 +62,7 @@ public: QString name() const; void setName(const QString &); - virtual void execute(Reason reason = ActualChange); + virtual void execute(); }; diff --git a/src/quick/util/qquickstyledtext.cpp b/src/quick/util/qquickstyledtext.cpp index 6ed32c10e2..c411207121 100644 --- a/src/quick/util/qquickstyledtext.cpp +++ b/src/quick/util/qquickstyledtext.cpp @@ -182,7 +182,7 @@ void QQuickStyledText::parse(const QString &string, QTextLayout &layout, void QQuickStyledTextPrivate::parse() { - QList<QTextLayout::FormatRange> ranges; + QVector<QTextLayout::FormatRange> ranges; QStack<QTextCharFormat> formatStack; QString drawText; @@ -283,7 +283,7 @@ void QQuickStyledTextPrivate::parse() } layout.setText(drawText); - layout.setAdditionalFormats(ranges); + layout.setFormats(ranges); } void QQuickStyledTextPrivate::appendText(const QString &textIn, int start, int length, QString &textOut) diff --git a/src/quick/util/qquicksystempalette_p.h b/src/quick/util/qquicksystempalette_p.h index 143efa1c12..fb898eb1fa 100644 --- a/src/quick/util/qquicksystempalette_p.h +++ b/src/quick/util/qquicksystempalette_p.h @@ -45,7 +45,6 @@ class QQuickSystemPalettePrivate; class Q_AUTOTEST_EXPORT QQuickSystemPalette : public QObject { Q_OBJECT - Q_ENUMS(ColorGroup) Q_DECLARE_PRIVATE(QQuickSystemPalette) Q_PROPERTY(QQuickSystemPalette::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY paletteChanged) @@ -69,6 +68,7 @@ public: ~QQuickSystemPalette(); enum ColorGroup { Active = QPalette::Active, Inactive = QPalette::Inactive, Disabled = QPalette::Disabled }; + Q_ENUM(ColorGroup) QColor window() const; QColor windowText() const; diff --git a/src/quick/util/qquicktimeline.cpp b/src/quick/util/qquicktimeline.cpp index 74754a0bfb..88fc03bba8 100644 --- a/src/quick/util/qquicktimeline.cpp +++ b/src/quick/util/qquicktimeline.cpp @@ -659,7 +659,7 @@ void QQuickTimeLine::complete() */ void QQuickTimeLine::clear() { - for (QQuickTimeLinePrivate::Ops::const_iterator iter = d->ops.begin(), cend = d->ops.end(); iter != cend; ++iter) + for (QQuickTimeLinePrivate::Ops::const_iterator iter = d->ops.cbegin(), cend = d->ops.cend(); iter != cend; ++iter) iter.key()->_t = 0; d->ops.clear(); d->length = 0; diff --git a/src/quick/util/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp index 832596d9a2..3992df993c 100644 --- a/src/quick/util/qquicktransitionmanager.cpp +++ b/src/quick/util/qquicktransitionmanager.cpp @@ -101,8 +101,8 @@ void QQuickTransitionManager::complete() void QQuickTransitionManagerPrivate::applyBindings() { foreach(const QQuickStateAction &action, bindingsList) { - if (!action.toBinding.isNull()) { - QQmlPropertyPrivate::setBinding(action.property, action.toBinding.data()); + if (action.toBinding) { + QQmlPropertyPrivate::setBinding(action.toBinding.data()); } else if (action.event) { if (action.reverseEvent) action.event->reverse(); @@ -131,7 +131,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list, if (action.toBinding) d->bindingsList << action; if (action.fromBinding) - QQmlPropertyPrivate::setBinding(action.property, 0); // Disable current binding + QQmlPropertyPrivate::removeBinding(action.property); // Disable current binding if (action.event && action.event->changesBindings()) { //### assume isReversable()? d->bindingsList << action; action.event->clearBindings(); @@ -146,24 +146,21 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list, // // This doesn't catch everything, and it might be a little fragile in // some cases - but whatcha going to do? - // - // Note that we only fast forward if both a transition and bindings are - // present, as it is unnecessary (and potentially expensive) otherwise. if (transition && !d->bindingsList.isEmpty()) { // Apply all the property and binding changes for (int ii = 0; ii < applyList.size(); ++ii) { const QQuickStateAction &action = applyList.at(ii); - if (!action.toBinding.isNull()) { - QQmlPropertyPrivate::setBinding(action.property, action.toBinding.data(), QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); + if (action.toBinding) { + QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); } else if (!action.event) { QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); } else if (action.event->isReversable()) { if (action.reverseEvent) - action.event->reverse(QQuickStateActionEvent::FastForward); + action.event->reverse(); else - action.event->execute(QQuickStateActionEvent::FastForward); + action.event->execute(); } } @@ -175,7 +172,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list, continue; } const QQmlProperty &prop = action->property; - if (!action->toBinding.isNull() || !action->toValue.isValid()) { + if (action->toBinding || !action->toValue.isValid()) { action->toValue = prop.read(); } } @@ -192,7 +189,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list, } if (action.toBinding) - QQmlPropertyPrivate::setBinding(action.property, 0); // Make sure this is disabled during the transition + QQmlPropertyPrivate::removeBinding(action.property); // Make sure this is disabled during the transition QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); } @@ -269,10 +266,9 @@ void QQuickTransitionManager::cancel() for(int i = 0; i < d->bindingsList.count(); ++i) { QQuickStateAction action = d->bindingsList[i]; - if (!action.toBinding.isNull() && action.deletableToBinding) { - QQmlPropertyPrivate::setBinding(action.property, 0); - action.toBinding.data()->destroy(); - action.toBinding.clear(); + if (action.toBinding && action.deletableToBinding) { + QQmlPropertyPrivate::removeBinding(action.property); + action.toBinding = 0; action.deletableToBinding = false; } else if (action.event) { //### what do we do here? diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp index 4d156a2d9a..4f6e49fa7a 100644 --- a/src/quick/util/qquickutilmodule.cpp +++ b/src/quick/util/qquickutilmodule.cpp @@ -49,6 +49,7 @@ #include "qquicktransition_p.h" #include "qquickanimator_p.h" #include "qquickshortcut_p.h" +#include "qquickvalidator_p.h" #include <qqmlinfo.h> #include <private/qqmltypenotavailable_p.h> #include <private/qquickanimationcontroller_p.h> @@ -87,6 +88,13 @@ void QQuickUtilModule::defineModule() qmlRegisterType<QQuickTransition>("QtQuick",2,0,"Transition"); qmlRegisterType<QQuickVector3dAnimation>("QtQuick",2,0,"Vector3dAnimation"); +#ifndef QT_NO_VALIDATOR + qmlRegisterType<QValidator>(); + qmlRegisterType<QQuickIntValidator>("QtQuick",2,0,"IntValidator"); + qmlRegisterType<QQuickDoubleValidator>("QtQuick",2,0,"DoubleValidator"); + qmlRegisterType<QRegExpValidator>("QtQuick",2,0,"RegExpValidator"); +#endif + qmlRegisterUncreatableType<QQuickAnimator>("QtQuick", 2, 2, "Animator", QQuickAbstractAnimation::tr("Animator is an abstract class")); qmlRegisterType<QQuickXAnimator>("QtQuick", 2, 2, "XAnimator"); qmlRegisterType<QQuickYAnimator>("QtQuick", 2, 2, "YAnimator"); diff --git a/src/quick/util/qquickvalidator.cpp b/src/quick/util/qquickvalidator.cpp new file mode 100644 index 0000000000..3eebf5d77a --- /dev/null +++ b/src/quick/util/qquickvalidator.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickvalidator_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_VALIDATOR + +/*! + \qmltype IntValidator + \instantiates QIntValidator + \inqmlmodule QtQuick + \ingroup qtquick-text-utility + \brief Defines a validator for integer values + + The IntValidator type provides a validator for integer values. + + If no \l locale is set IntValidator uses the \l {QLocale::setDefault()}{default locale} to + interpret the number and will accept locale specific digits, group separators, and positive + and negative signs. In addition, IntValidator is always guaranteed to accept a number + formatted according to the "C" locale. +*/ + +QQuickIntValidator::QQuickIntValidator(QObject *parent) + : QIntValidator(parent) +{ +} + +/*! + \qmlproperty string QtQuick::IntValidator::locale + + This property holds the name of the locale used to interpret the number. + + \sa {QtQml::Qt::locale()}{Qt.locale()} +*/ + +QString QQuickIntValidator::localeName() const +{ + return locale().name(); +} + +void QQuickIntValidator::setLocaleName(const QString &name) +{ + if (locale().name() != name) { + setLocale(QLocale(name)); + emit localeNameChanged(); + } +} + +void QQuickIntValidator::resetLocaleName() +{ + QLocale defaultLocale; + if (locale() != defaultLocale) { + setLocale(defaultLocale); + emit localeNameChanged(); + } +} + +/*! + \qmlproperty int QtQuick::IntValidator::top + + This property holds the validator's highest acceptable value. + By default, this property's value is derived from the highest signed integer available (typically 2147483647). +*/ +/*! + \qmlproperty int QtQuick::IntValidator::bottom + + This property holds the validator's lowest acceptable value. + By default, this property's value is derived from the lowest signed integer available (typically -2147483647). +*/ + +/*! + \qmltype DoubleValidator + \instantiates QDoubleValidator + \inqmlmodule QtQuick + \ingroup qtquick-text-utility + \brief Defines a validator for non-integer numbers + + The DoubleValidator type provides a validator for non-integer numbers. + + Input is accepted if it contains a double that is within the valid range + and is in the correct format. + + Input is accepected but invalid if it contains a double that is outside + the range or is in the wrong format; e.g. with too many digits after the + decimal point or is empty. + + Input is rejected if it is not a double. + + Note: If the valid range consists of just positive doubles (e.g. 0.0 to + 100.0) and input is a negative double then it is rejected. If \l notation + is set to DoubleValidator.StandardNotation, and the input contains more + digits before the decimal point than a double in the valid range may have, + it is also rejected. If \l notation is DoubleValidator.ScientificNotation, + and the input is not in the valid range, it is accecpted but invalid. The + value may yet become valid by changing the exponent. +*/ + +QQuickDoubleValidator::QQuickDoubleValidator(QObject *parent) + : QDoubleValidator(parent) +{ +} + +/*! + \qmlproperty string QtQuick::DoubleValidator::locale + + This property holds the name of the locale used to interpret the number. + + \sa {QtQml::Qt::locale()}{Qt.locale()} +*/ + +QString QQuickDoubleValidator::localeName() const +{ + return locale().name(); +} + +void QQuickDoubleValidator::setLocaleName(const QString &name) +{ + if (locale().name() != name) { + setLocale(QLocale(name)); + emit localeNameChanged(); + } +} + +void QQuickDoubleValidator::resetLocaleName() +{ + QLocale defaultLocale; + if (locale() != defaultLocale) { + setLocale(defaultLocale); + emit localeNameChanged(); + } +} + +/*! + \qmlproperty real QtQuick::DoubleValidator::top + + This property holds the validator's maximum acceptable value. + By default, this property contains a value of infinity. +*/ +/*! + \qmlproperty real QtQuick::DoubleValidator::bottom + + This property holds the validator's minimum acceptable value. + By default, this property contains a value of -infinity. +*/ +/*! + \qmlproperty int QtQuick::DoubleValidator::decimals + + This property holds the validator's maximum number of digits after the decimal point. + By default, this property contains a value of 1000. +*/ +/*! + \qmlproperty enumeration QtQuick::DoubleValidator::notation + This property holds the notation of how a string can describe a number. + + The possible values for this property are: + + \list + \li DoubleValidator.StandardNotation + \li DoubleValidator.ScientificNotation (default) + \endlist + + If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2). +*/ + +/*! + \qmltype RegExpValidator + \instantiates QRegExpValidator + \inqmlmodule QtQuick + \ingroup qtquick-text-utility + \brief Provides a string validator + + The RegExpValidator type provides a validator, which counts as valid any string which + matches a specified regular expression. +*/ +/*! + \qmlproperty regExp QtQuick::RegExpValidator::regExp + + This property holds the regular expression used for validation. + + Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular expression + matching "a". + + By default, this property contains a regular expression with the pattern .* that matches any string. +*/ + +#endif // QT_NO_VALIDATOR + +QT_END_NAMESPACE + diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h b/src/quick/util/qquickvalidator_p.h index 942fb6e12c..59d7884afc 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.h +++ b/src/quick/util/qquickvalidator_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage @@ -31,47 +31,53 @@ ** ****************************************************************************/ -#ifndef QTCPSERVERCONNECTION_H -#define QTCPSERVERCONNECTION_H +#ifndef QQUICKVALIDATOR_P_H +#define QQUICKVALIDATOR_P_H -#include <private/qqmldebugserverconnection_p.h> +#include <QtGui/qvalidator.h> +#include <QtQml/qqml.h> QT_BEGIN_NAMESPACE -class QQmlDebugServer; -class QTcpServerConnectionPrivate; -class QTcpServerConnection : public QObject, public QQmlDebugServerConnection +#ifndef QT_NO_VALIDATOR +class Q_AUTOTEST_EXPORT QQuickIntValidator : public QIntValidator { Q_OBJECT - Q_DECLARE_PRIVATE(QTcpServerConnection) - Q_DISABLE_COPY(QTcpServerConnection) - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlDebugServerConnection") - Q_INTERFACES(QQmlDebugServerConnection) - + Q_PROPERTY(QString locale READ localeName WRITE setLocaleName RESET resetLocaleName NOTIFY localeNameChanged) public: - QTcpServerConnection(); - ~QTcpServerConnection(); + QQuickIntValidator(QObject *parent = 0); - void setServer(QQmlDebugServer *server); - bool setPortRange(int portFrom, int portTo, bool bock, const QString &hostaddress); + QString localeName() const; + void setLocaleName(const QString &name); + void resetLocaleName(); - bool isConnected() const; - void send(const QList<QByteArray> &messages); - void disconnect(); - bool waitForMessage(); +Q_SIGNALS: + void localeNameChanged(); +}; - bool listen(); - void waitForConnection(); +class Q_AUTOTEST_EXPORT QQuickDoubleValidator : public QDoubleValidator +{ + Q_OBJECT + Q_PROPERTY(QString locale READ localeName WRITE setLocaleName RESET resetLocaleName NOTIFY localeNameChanged) +public: + QQuickDoubleValidator(QObject *parent = 0); -private Q_SLOTS: - void readyRead(); - void newConnection(); - void invalidPacket(); + QString localeName() const; + void setLocaleName(const QString &name); + void resetLocaleName(); -private: - QTcpServerConnectionPrivate *d_ptr; +Q_SIGNALS: + void localeNameChanged(); }; +#endif QT_END_NAMESPACE -#endif // QTCPSERVERCONNECTION_H +#ifndef QT_NO_VALIDATOR +QML_DECLARE_TYPE(QValidator) +QML_DECLARE_TYPE(QQuickIntValidator) +QML_DECLARE_TYPE(QQuickDoubleValidator) +QML_DECLARE_TYPE(QRegExpValidator) +#endif + +#endif // QQUICKVALIDATOR_P_H diff --git a/src/quick/util/qquickvaluetypes.cpp b/src/quick/util/qquickvaluetypes.cpp index fef6dfd1d0..1f0d54e4e7 100644 --- a/src/quick/util/qquickvaluetypes.cpp +++ b/src/quick/util/qquickvaluetypes.cpp @@ -533,6 +533,16 @@ void QQuickFontValueType::setFamily(const QString &family) v.setFamily(family); } +QString QQuickFontValueType::styleName() const +{ + return v.styleName(); +} + +void QQuickFontValueType::setStyleName(const QString &style) +{ + v.setStyleName(style); +} + bool QQuickFontValueType::bold() const { return v.bold(); diff --git a/src/quick/util/qquickvaluetypes_p.h b/src/quick/util/qquickvaluetypes_p.h index f62306ed01..7a2e8888b7 100644 --- a/src/quick/util/qquickvaluetypes_p.h +++ b/src/quick/util/qquickvaluetypes_p.h @@ -266,10 +266,9 @@ class QQuickFontValueType { QFont v; Q_GADGET - Q_ENUMS(FontWeight) - Q_ENUMS(Capitalization) Q_PROPERTY(QString family READ family WRITE setFamily FINAL) + Q_PROPERTY(QString styleName READ styleName WRITE setStyleName FINAL) Q_PROPERTY(bool bold READ bold WRITE setBold FINAL) Q_PROPERTY(FontWeight weight READ weight WRITE setWeight FINAL) Q_PROPERTY(bool italic READ italic WRITE setItalic FINAL) @@ -292,17 +291,22 @@ public: Bold = QFont::Bold, ExtraBold = QFont::ExtraBold, Black = QFont::Black }; + Q_ENUM(FontWeight) enum Capitalization { MixedCase = QFont::MixedCase, AllUppercase = QFont::AllUppercase, AllLowercase = QFont::AllLowercase, SmallCaps = QFont::SmallCaps, Capitalize = QFont::Capitalize }; + Q_ENUM(Capitalization) Q_INVOKABLE QString toString() const; QString family() const; void setFamily(const QString &); + QString styleName() const; + void setStyleName(const QString &); + bool bold() const; void setBold(bool b); diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index 0e0df4e751..ffb31ae75e 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -29,7 +29,8 @@ SOURCES += \ $$PWD/qquickprofiler.cpp \ $$PWD/qquickfontmetrics.cpp \ $$PWD/qquicktextmetrics.cpp \ - $$PWD/qquickshortcut.cpp + $$PWD/qquickshortcut.cpp \ + $$PWD/qquickvalidator.cpp HEADERS += \ $$PWD/qquickapplication_p.h\ @@ -66,4 +67,5 @@ HEADERS += \ $$PWD/qquickprofiler_p.h \ $$PWD/qquickfontmetrics_p.h \ $$PWD/qquicktextmetrics_p.h \ - $$PWD/qquickshortcut_p.h + $$PWD/qquickshortcut_p.h \ + $$PWD/qquickvalidator_p.h diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index a848774ae9..5755271fe1 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -39,8 +39,9 @@ #include "private/qquickitemchangelistener_p.h" #include "private/qquickrendercontrol_p.h" +#include <private/qqmldebugconnector_p.h> #include <private/qquickprofiler_p.h> -#include <private/qqmlinspectorservice_p.h> +#include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmlmemoryprofiler_p.h> #include <QtQml/qqmlengine.h> @@ -95,8 +96,9 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) if (!engine.data()->incubationController()) engine.data()->setIncubationController(offscreenWindow->incubationController()); - if (QQmlDebugService::isDebuggingEnabled()) - QQmlInspectorService::instance()->addView(q); + QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>(); + if (service) + service->addView(q); #ifndef QT_NO_DRAGANDDROP q->setAcceptDrops(true); @@ -142,13 +144,15 @@ QQuickWidgetPrivate::QQuickWidgetPrivate() , eventPending(false) , updatePending(false) , fakeHidden(false) + , requestedSamples(0) { } QQuickWidgetPrivate::~QQuickWidgetPrivate() { - if (QQmlDebugService::isDebuggingEnabled()) - QQmlInspectorService::instance()->removeView(q_func()); + QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>(); + if (service) + service->removeView(q_func()); invalidateRenderControl(); @@ -538,6 +542,9 @@ QQuickWidget::Status QQuickWidget::status() const if (!d->component) return QQuickWidget::Null; + if (d->component->status() == QQmlComponent::Ready && !d->root) + return QQuickWidget::Error; + return QQuickWidget::Status(d->component->status()); } @@ -559,6 +566,10 @@ QList<QQmlError> QQuickWidget::errors() const QQmlError error; error.setDescription(QLatin1String("QQuickWidget: invalid qml engine.")); errs << error; + } else if (d->component->status() == QQmlComponent::Ready && !d->root) { + QQmlError error; + error.setDescription(QLatin1String("QQuickWidget: invalid root object.")); + errs << error; } return errs; @@ -764,7 +775,7 @@ void QQuickWidget::createFramebufferObject() context->makeCurrent(d->offscreenSurface); - int samples = d->offscreenWindow->requestedFormat().samples(); + int samples = d->requestedSamples; if (!QOpenGLExtensions(context).hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) samples = 0; @@ -878,14 +889,15 @@ void QQuickWidgetPrivate::setRootObject(QObject *obj) if (QQuickItem *sgItem = qobject_cast<QQuickItem *>(obj)) { root = sgItem; sgItem->setParentItem(offscreenWindow->contentItem()); + } else if (qobject_cast<QWindow *>(obj)) { + qWarning() << "QQuickWidget does not support using windows as a root item." << endl + << endl + << "If you wish to create your root window from QML, consider using QQmlApplicationEngine instead." << endl; } else { qWarning() << "QQuickWidget only supports loading of root objects that derive from QQuickItem." << endl << endl - << "If your example is using QML 2, (such as qmlscene) and the .qml file you" << endl - << "loaded has 'import QtQuick 1.0' or 'import Qt 4.7', this error will occur." << endl - << endl - << "To load files with 'import QtQuick 1.0' or 'import Qt 4.7', use the" << endl - << "QDeclarativeView class in the Qt Quick 1 module." << endl; + << "Ensure your QML code is written for QtQuick 2, and uses a root that is or" << endl + << "inherits from QtQuick's Item (not a Timer, QtObject, etc)." << endl; delete obj; root = 0; } @@ -1239,6 +1251,14 @@ void QQuickWidget::setFormat(const QSurfaceFormat &format) newFormat.setDepthBufferSize(qMax(newFormat.depthBufferSize(), currentFormat.depthBufferSize())); newFormat.setStencilBufferSize(qMax(newFormat.stencilBufferSize(), currentFormat.stencilBufferSize())); newFormat.setAlphaBufferSize(qMax(newFormat.alphaBufferSize(), currentFormat.alphaBufferSize())); + + // Do not include the sample count. Requesting a multisampled context is not necessary + // since we render into an FBO, never to an actual surface. What's more, attempting to + // create a pbuffer with a multisampled config crashes certain implementations. Just + // avoid the entire hassle, the result is the same. + d->requestedSamples = newFormat.samples(); + newFormat.setSamples(0); + d->offscreenWindow->setFormat(newFormat); } diff --git a/src/quickwidgets/qquickwidget.h b/src/quickwidgets/qquickwidget.h index a8bf03edfb..5bb6b49a49 100644 --- a/src/quickwidgets/qquickwidget.h +++ b/src/quickwidgets/qquickwidget.h @@ -56,7 +56,6 @@ class Q_QUICKWIDGETS_EXPORT QQuickWidget : public QWidget Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QUrl source READ source WRITE setSource DESIGNABLE true) - Q_ENUMS(ResizeMode Status) public: explicit QQuickWidget(QWidget *parent = 0); @@ -72,10 +71,12 @@ public: QQuickItem *rootObject() const; enum ResizeMode { SizeViewToRootObject, SizeRootObjectToView }; + Q_ENUM(ResizeMode) ResizeMode resizeMode() const; void setResizeMode(ResizeMode); enum Status { Null, Ready, Loading, Error }; + Q_ENUM(Status) Status status() const; QList<QQmlError> errors() const; diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h index e8d1f714f3..fa8bb3beb7 100644 --- a/src/quickwidgets/qquickwidget_p.h +++ b/src/quickwidgets/qquickwidget_p.h @@ -115,6 +115,8 @@ public: bool eventPending; bool updatePending; bool fakeHidden; + + int requestedSamples; }; QT_END_NAMESPACE |