From 6ea761dac6d95fd64fec35e0ccb6a6904597ca56 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 30 May 2016 18:10:27 +0200 Subject: [PATCH 001/199] Bug 1245627: Hoist store{Float32,Double} functions into the MacroAssembler; r=nbp MozReview-Commit-ID: GODz1ux3Cs0 --HG-- extra : rebase_source : 27cab500d3392278dade787099bc2efe2027d241 extra : histedit_source : 7d7d73cd6e23dc15a1e8824e3f30039b5b13f8a0 --- js/src/jit/MacroAssembler.h | 26 +++++- js/src/jit/arm/MacroAssembler-arm-inl.h | 37 +++++++++ js/src/jit/arm/MacroAssembler-arm.cpp | 59 +++++++------- js/src/jit/arm/MacroAssembler-arm.h | 21 ----- js/src/jit/arm64/MacroAssembler-arm64-inl.h | 35 ++++++++ js/src/jit/arm64/MacroAssembler-arm64.cpp | 41 ++++++++++ js/src/jit/arm64/MacroAssembler-arm64.h | 44 ----------- .../MacroAssembler-mips-shared-inl.h | 13 +++ js/src/jit/mips32/MacroAssembler-mips32-inl.h | 26 ++++++ js/src/jit/mips32/MacroAssembler-mips32.cpp | 59 +++++++------- js/src/jit/mips32/MacroAssembler-mips32.h | 17 ---- js/src/jit/mips64/MacroAssembler-mips64-inl.h | 26 ++++++ js/src/jit/mips64/MacroAssembler-mips64.cpp | 79 ++++++++++--------- js/src/jit/mips64/MacroAssembler-mips64.h | 21 ----- js/src/jit/x64/MacroAssembler-x64.cpp | 78 +++++++++--------- js/src/jit/x64/MacroAssembler-x64.h | 3 - .../MacroAssembler-x86-shared-inl.h | 73 +++++++++++++++++ .../x86-shared/MacroAssembler-x86-shared.h | 54 +------------ js/src/jit/x86/MacroAssembler-x86.cpp | 59 +++++++------- js/src/jit/x86/MacroAssembler-x86.h | 4 - 20 files changed, 445 insertions(+), 330 deletions(-) diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index f14f9b90a9cb..512eb63805b0 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -72,7 +72,7 @@ // DEFINED_ON is a macro which check if, for the current architecture, the // method is defined on the macro assembler or not. // -// For each architecutre, we have a macro named DEFINED_ON_arch. This macro is +// For each architecture, we have a macro named DEFINED_ON_arch. This macro is // empty if this is not the current architecture. Otherwise it must be either // set to "define" or "crash" (only use for the none target so-far). // @@ -94,7 +94,7 @@ // // crash // -// or to nothing, if the current architecture is not lsited in the list of +// or to nothing, if the current architecture is not listed in the list of // arguments of DEFINED_ON. Note, only one of the DEFINED_ON_arch macro // contributes to the non-empty result, which is the macro of the current // architecture if it is listed in the arguments of DEFINED_ON. @@ -1120,6 +1120,28 @@ class MacroAssembler : public MacroAssemblerSpecific inline void branchTestMagicImpl(Condition cond, const T& t, L label) DEFINED_ON(arm, arm64, x86_shared); + public: + // ======================================================================== + // Memory access primitives. + inline void storeDouble(FloatRegister src, const Address& dest) + DEFINED_ON(x86_shared, arm, arm64, mips32, mips64); + inline void storeDouble(FloatRegister src, const BaseIndex& dest) + DEFINED_ON(x86_shared, arm, arm64, mips32, mips64); + inline void storeDouble(FloatRegister src, const Operand& dest) DEFINED_ON(x86_shared); + + inline void storeFloat32(FloatRegister src, const Address& dest) + DEFINED_ON(x86_shared, arm, arm64, mips32, mips64); + inline void storeFloat32(FloatRegister src, const BaseIndex& dest) + DEFINED_ON(x86_shared, arm, arm64, mips32, mips64); + inline void storeFloat32(FloatRegister src, const Operand& dest) DEFINED_ON(x86_shared); + + inline void storeFloat32x3(FloatRegister src, const Address& dest) PER_SHARED_ARCH; + inline void storeFloat32x3(FloatRegister src, const BaseIndex& dest) PER_SHARED_ARCH; + + template + void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, + MIRType slotType) PER_ARCH; + //}}} check_macroassembler_style public: diff --git a/js/src/jit/arm/MacroAssembler-arm-inl.h b/js/src/jit/arm/MacroAssembler-arm-inl.h index 9f5ce360a258..e32a29da51c8 100644 --- a/js/src/jit/arm/MacroAssembler-arm-inl.h +++ b/js/src/jit/arm/MacroAssembler-arm-inl.h @@ -1203,6 +1203,43 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag branch32(cond, ToPayload(valaddr), Imm32(why), label); } +// ======================================================================== +// Memory access primitives. +void +MacroAssembler::storeDouble(FloatRegister src, const Address& addr) +{ + ma_vstr(src, addr); +} +void +MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& addr) +{ + uint32_t scale = Imm32::ShiftOf(addr.scale).value; + ma_vstr(src, addr.base, addr.index, scale, addr.offset); +} + +void +MacroAssembler::storeFloat32(FloatRegister src, const Address& addr) +{ + ma_vstr(src.asSingle(), addr); +} +void +MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr) +{ + uint32_t scale = Imm32::ShiftOf(addr.scale).value; + ma_vstr(src.asSingle(), addr.base, addr.index, scale, addr.offset); +} + +void +MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest) +{ + MOZ_CRASH("NYI"); +} +void +MacroAssembler::storeFloat32x3(FloatRegister src, const BaseIndex& dest) +{ + MOZ_CRASH("NYI"); +} + //}}} check_macroassembler_style // =============================================================== diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 81519aa365c0..12ac60977b69 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -3058,35 +3058,6 @@ MacroAssemblerARMCompat::extractTag(const BaseIndex& address, Register scratch) return extractTag(Address(scratch, address.offset), scratch); } -template -void -MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, - const T& dest, MIRType slotType) -{ - if (valueType == MIRType::Double) { - storeDouble(value.reg().typedReg().fpu(), dest); - return; - } - - // Store the type tag if needed. - if (valueType != slotType) - storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest); - - // Store the payload. - if (value.constant()) - storePayload(value.value(), dest); - else - storePayload(value.reg().typedReg().gpr(), dest); -} - -template void -MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, - const Address& dest, MIRType slotType); - -template void -MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, - const BaseIndex& dest, MIRType slotType); - void MacroAssemblerARMCompat::moveValue(const Value& val, Register type, Register data) { @@ -5081,4 +5052,34 @@ MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, ma_b(label, cond); } +// ======================================================================== +// Memory access primitives. +template +void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, + MIRType slotType) +{ + if (valueType == MIRType::Double) { + storeDouble(value.reg().typedReg().fpu(), dest); + return; + } + + // Store the type tag if needed. + if (valueType != slotType) + storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest); + + // Store the payload. + if (value.constant()) + storePayload(value.value(), dest); + else + storePayload(value.reg().typedReg().gpr(), dest); +} + +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, + const Address& dest, MIRType slotType); +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, + const BaseIndex& dest, MIRType slotType); + //}}} check_macroassembler_style diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index d0c1873473b9..5c05d5f58cf5 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -769,10 +769,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM load32(address, dest.gpr()); } - template - void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, - MIRType slotType); - template void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) { switch (nbytes) { @@ -948,8 +944,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void loadFloat32x3(const Address& src, FloatRegister dest) { MOZ_CRASH("NYI"); } void loadFloat32x3(const BaseIndex& src, FloatRegister dest) { MOZ_CRASH("NYI"); } - void storeFloat32x3(FloatRegister src, const Address& dest) { MOZ_CRASH("NYI"); } - void storeFloat32x3(FloatRegister src, const BaseIndex& dest) { MOZ_CRASH("NYI"); } void loadAlignedSimd128Float(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); } void storeAlignedSimd128Float(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); } void loadUnalignedSimd128Float(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); } @@ -994,25 +988,10 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM void storePtr(Register src, const Address& address); void storePtr(Register src, const BaseIndex& address); void storePtr(Register src, AbsoluteAddress dest); - void storeDouble(FloatRegister src, Address addr) { - ma_vstr(src, addr); - } - void storeDouble(FloatRegister src, BaseIndex addr) { - uint32_t scale = Imm32::ShiftOf(addr.scale).value; - ma_vstr(src, addr.base, addr.index, scale, addr.offset); - } void moveDouble(FloatRegister src, FloatRegister dest, Condition cc = Always) { ma_vmov(src, dest, cc); } - void storeFloat32(FloatRegister src, const Address& addr) { - ma_vstr(VFPRegister(src).singleOverlay(), addr); - } - void storeFloat32(FloatRegister src, const BaseIndex& addr) { - uint32_t scale = Imm32::ShiftOf(addr.scale).value; - ma_vstr(VFPRegister(src).singleOverlay(), addr.base, addr.index, scale, addr.offset); - } - private: template Register computePointer(const T& src, Register r); diff --git a/js/src/jit/arm64/MacroAssembler-arm64-inl.h b/js/src/jit/arm64/MacroAssembler-arm64-inl.h index 133c419a9b90..a5e7a95a4a9a 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h +++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h @@ -1298,6 +1298,41 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag B(label, cond); } +// ======================================================================== +// Memory access primitives. +void +MacroAssembler::storeDouble(FloatRegister src, const Address& dest) +{ + Str(ARMFPRegister(src, 64), MemOperand(ARMRegister(dest.base, 64), dest.offset)); +} +void +MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& dest) +{ + doBaseIndex(ARMFPRegister(src, 64), dest, vixl::STR_d); +} + +void +MacroAssembler::storeFloat32(FloatRegister src, const Address& addr) +{ + Str(ARMFPRegister(src, 32), MemOperand(ARMRegister(addr.base, 64), addr.offset)); +} +void +MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr) +{ + doBaseIndex(ARMFPRegister(src, 32), addr, vixl::STR_s); +} + +void +MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest) +{ + MOZ_CRASH("NYI"); +} +void +MacroAssembler::storeFloat32x3(FloatRegister src, const BaseIndex& dest) +{ + MOZ_CRASH("NYI"); +} + //}}} check_macroassembler_style // =============================================================== diff --git a/js/src/jit/arm64/MacroAssembler-arm64.cpp b/js/src/jit/arm64/MacroAssembler-arm64.cpp index 5fd6e7ca018e..f3e5e0fc4212 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.cpp +++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp @@ -784,6 +784,47 @@ MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, B(label, cond); } +// ======================================================================== +// Memory access primitives. +template +void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, + MIRType slotType) +{ + if (valueType == MIRType::Double) { + storeDouble(value.reg().typedReg().fpu(), dest); + return; + } + + // For known integers and booleans, we can just store the unboxed value if + // the slot has the same type. + if ((valueType == MIRType::Int32 || valueType == MIRType::Boolean) && slotType == valueType) { + if (value.constant()) { + Value val = value.value(); + if (valueType == MIRType::Int32) + store32(Imm32(val.toInt32()), dest); + else + store32(Imm32(val.toBoolean() ? 1 : 0), dest); + } else { + store32(value.reg().typedReg().gpr(), dest); + } + return; + } + + if (value.constant()) + storeValue(value.value(), dest); + else + storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), dest); + +} + +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, + const Address& dest, MIRType slotType); +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, + const BaseIndex& dest, MIRType slotType); + //}}} check_macroassembler_style } // namespace jit diff --git a/js/src/jit/arm64/MacroAssembler-arm64.h b/js/src/jit/arm64/MacroAssembler-arm64.h index f1f05da02bce..21b6bb5d071f 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.h +++ b/js/src/jit/arm64/MacroAssembler-arm64.h @@ -284,34 +284,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler storePtr(temp, dest); } - template - void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, MIRType slotType) { - if (valueType == MIRType::Double) { - storeDouble(value.reg().typedReg().fpu(), dest); - return; - } - - // For known integers and booleans, we can just store the unboxed value if - // the slot has the same type. - if ((valueType == MIRType::Int32 || valueType == MIRType::Boolean) && slotType == valueType) { - if (value.constant()) { - Value val = value.value(); - if (valueType == MIRType::Int32) - store32(Imm32(val.toInt32()), dest); - else - store32(Imm32(val.toBoolean() ? 1 : 0), dest); - } else { - store32(value.reg().typedReg().gpr(), dest); - } - return; - } - - if (value.constant()) - storeValue(value.value(), dest); - else - storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), dest); - - } void loadValue(Address src, Register val) { Ldr(ARMRegister(val, 64), MemOperand(src)); } @@ -973,8 +945,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler void loadFloat32x3(const Address& src, FloatRegister dest) { MOZ_CRASH("NYI"); } void loadFloat32x3(const BaseIndex& src, FloatRegister dest) { MOZ_CRASH("NYI"); } - void storeFloat32x3(FloatRegister src, const Address& dest) { MOZ_CRASH("NYI"); } - void storeFloat32x3(FloatRegister src, const BaseIndex& dest) { MOZ_CRASH("NYI"); } void loadAlignedSimd128Float(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); } void loadAlignedSimd128Float(const BaseIndex& addr, FloatRegister dest) { MOZ_CRASH("NYI"); } void storeAlignedSimd128Float(FloatRegister src, const Address& addr) { MOZ_CRASH("NYI"); } @@ -1163,20 +1133,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler } } - void storeDouble(FloatRegister src, const Address& dest) { - Str(ARMFPRegister(src, 64), MemOperand(ARMRegister(dest.base, 64), dest.offset)); - } - void storeDouble(FloatRegister src, const BaseIndex& dest) { - doBaseIndex(ARMFPRegister(src, 64), dest, vixl::STR_d); - } - - void storeFloat32(FloatRegister src, Address addr) { - Str(ARMFPRegister(src, 32), MemOperand(ARMRegister(addr.base, 64), addr.offset)); - } - void storeFloat32(FloatRegister src, BaseIndex addr) { - doBaseIndex(ARMFPRegister(src, 32), addr, vixl::STR_s); - } - void moveDouble(FloatRegister src, FloatRegister dest) { fmov(ARMFPRegister(dest, 64), ARMFPRegister(src, 64)); } diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h index 7c0d9e408ee7..f97b3c5e0331 100644 --- a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h +++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h @@ -749,6 +749,19 @@ MacroAssembler::branchTestMagic(Condition cond, const BaseIndex& address, Label* branchTestMagic(cond, scratch2, label); } +// ======================================================================== +// Memory access primitives. +void +MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest) +{ + MOZ_CRASH("NYI"); +} +void +MacroAssembler::storeFloat32x3(FloatRegister src, const BaseIndex& dest) +{ + MOZ_CRASH("NYI"); +} + //}}} check_macroassembler_style // =============================================================== diff --git a/js/src/jit/mips32/MacroAssembler-mips32-inl.h b/js/src/jit/mips32/MacroAssembler-mips32-inl.h index 18c4b18bb97d..c344b640659b 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h +++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h @@ -420,6 +420,32 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag branch32(cond, ToPayload(valaddr), Imm32(why), label); } +// ======================================================================== +// Memory access primitives. +void +MacroAssembler::storeDouble(FloatRegister src, const Address& addr) +{ + ma_sd(src, addr); +} +void +MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& addr) +{ + MOZ_ASSERT(addr.offset == 0); + ma_sd(src, addr); +} + +void +MacroAssembler::storeFloat32(FloatRegister src, const Address& addr) +{ + ma_ss(src, addr); +} +void +MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr) +{ + MOZ_ASSERT(addr.offset == 0); + ma_ss(src, addr); +} + //}}} check_macroassembler_style // =============================================================== diff --git a/js/src/jit/mips32/MacroAssembler-mips32.cpp b/js/src/jit/mips32/MacroAssembler-mips32.cpp index 9b3602b6b271..c3b116ade9f2 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.cpp +++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -1415,35 +1415,6 @@ MacroAssemblerMIPSCompat::getType(const Value& val) return jv.s.tag; } -template -void -MacroAssemblerMIPSCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, - MIRType slotType) -{ - if (valueType == MIRType::Double) { - storeDouble(value.reg().typedReg().fpu(), dest); - return; - } - - // Store the type tag if needed. - if (valueType != slotType) - storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest); - - // Store the payload. - if (value.constant()) - storePayload(value.value(), dest); - else - storePayload(value.reg().typedReg().gpr(), dest); -} - -template void -MacroAssemblerMIPSCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest, - MIRType slotType); - -template void -MacroAssemblerMIPSCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest, - MIRType slotType); - void MacroAssemblerMIPSCompat::moveData(const Value& val, Register data) { @@ -2254,4 +2225,34 @@ MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, } } +// ======================================================================== +// Memory access primitives. +template +void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, + MIRType slotType) +{ + if (valueType == MIRType::Double) { + storeDouble(value.reg().typedReg().fpu(), dest); + return; + } + + // Store the type tag if needed. + if (valueType != slotType) + storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest); + + // Store the payload. + if (value.constant()) + storePayload(value.value(), dest); + else + storePayload(value.reg().typedReg().gpr(), dest); +} + +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest, + MIRType slotType); +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest, + MIRType slotType); + //}}} check_macroassembler_style diff --git a/js/src/jit/mips32/MacroAssembler-mips32.h b/js/src/jit/mips32/MacroAssembler-mips32.h index b782914b962e..c77cba685e56 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.h +++ b/js/src/jit/mips32/MacroAssembler-mips32.h @@ -880,8 +880,6 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS void loadFloat32x3(const Address& src, FloatRegister dest) { MOZ_CRASH("NYI"); } void loadFloat32x3(const BaseIndex& src, FloatRegister dest) { MOZ_CRASH("NYI"); } - void storeFloat32x3(FloatRegister src, const Address& dest) { MOZ_CRASH("NYI"); } - void storeFloat32x3(FloatRegister src, const BaseIndex& dest) { MOZ_CRASH("NYI"); } void loadAlignedSimd128Float(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); } void storeAlignedSimd128Float(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); } void loadUnalignedSimd128Float(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); } @@ -932,25 +930,10 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS void storePtr(Register src, const Address& address); void storePtr(Register src, const BaseIndex& address); void storePtr(Register src, AbsoluteAddress dest); - void storeDouble(FloatRegister src, Address addr) { - ma_sd(src, addr); - } - void storeDouble(FloatRegister src, BaseIndex addr) { - MOZ_ASSERT(addr.offset == 0); - ma_sd(src, addr); - } void moveDouble(FloatRegister src, FloatRegister dest) { as_movd(dest, src); } - void storeFloat32(FloatRegister src, Address addr) { - ma_ss(src, addr); - } - void storeFloat32(FloatRegister src, BaseIndex addr) { - MOZ_ASSERT(addr.offset == 0); - ma_ss(src, addr); - } - void zeroDouble(FloatRegister reg) { moveToDoubleLo(zero, reg); moveToDoubleHi(zero, reg); diff --git a/js/src/jit/mips64/MacroAssembler-mips64-inl.h b/js/src/jit/mips64/MacroAssembler-mips64-inl.h index 14ee80f9f9b4..7f052d0b7eb5 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h +++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h @@ -377,6 +377,32 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag ma_b(scratch, ImmWord(magic), label, cond); } +// ======================================================================== +// Memory access primitives. +void +MacroAssembler::storeDouble(FloatRegister src, const Address& addr) +{ + ma_sd(src, addr); +} +void +MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& addr) +{ + MOZ_ASSERT(addr.offset == 0); + ma_sd(src, addr); +} + +void +MacroAssembler::storeFloat32(FloatRegister src, const Address& addr) +{ + ma_ss(src, addr); +} +void +MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr) +{ + MOZ_ASSERT(addr.offset == 0); + ma_ss(src, addr); +} + //}}} check_macroassembler_style // =============================================================== diff --git a/js/src/jit/mips64/MacroAssembler-mips64.cpp b/js/src/jit/mips64/MacroAssembler-mips64.cpp index 55f2e5e9aca1..ab1ad7a49c48 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64.cpp +++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp @@ -1594,45 +1594,6 @@ MacroAssemblerMIPS64Compat::extractTag(const BaseIndex& address, Register scratc return extractTag(Address(scratch, address.offset), scratch); } -template -void -MacroAssemblerMIPS64Compat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, - MIRType slotType) -{ - if (valueType == MIRType::Double) { - storeDouble(value.reg().typedReg().fpu(), dest); - return; - } - - // For known integers and booleans, we can just store the unboxed value if - // the slot has the same type. - if ((valueType == MIRType::Int32 || valueType == MIRType::Boolean) && slotType == valueType) { - if (value.constant()) { - Value val = value.value(); - if (valueType == MIRType::Int32) - store32(Imm32(val.toInt32()), dest); - else - store32(Imm32(val.toBoolean() ? 1 : 0), dest); - } else { - store32(value.reg().typedReg().gpr(), dest); - } - return; - } - - if (value.constant()) - storeValue(value.value(), dest); - else - storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), dest); -} - -template void -MacroAssemblerMIPS64Compat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest, - MIRType slotType); - -template void -MacroAssemblerMIPS64Compat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest, - MIRType slotType); - void MacroAssemblerMIPS64Compat::moveValue(const Value& val, Register dest) { @@ -2359,4 +2320,44 @@ MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, ma_b(lhs.valueReg(), scratch, label, cond); } +// ======================================================================== +// Memory access primitives. +template +void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, + MIRType slotType) +{ + if (valueType == MIRType::Double) { + storeDouble(value.reg().typedReg().fpu(), dest); + return; + } + + // For known integers and booleans, we can just store the unboxed value if + // the slot has the same type. + if ((valueType == MIRType::Int32 || valueType == MIRType::Boolean) && slotType == valueType) { + if (value.constant()) { + Value val = value.value(); + if (valueType == MIRType::Int32) + store32(Imm32(val.toInt32()), dest); + else + store32(Imm32(val.toBoolean() ? 1 : 0), dest); + } else { + store32(value.reg().typedReg().gpr(), dest); + } + return; + } + + if (value.constant()) + storeValue(value.value(), dest); + else + storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), dest); +} + +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest, + MIRType slotType); +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest, + MIRType slotType); + //}}} check_macroassembler_style diff --git a/js/src/jit/mips64/MacroAssembler-mips64.h b/js/src/jit/mips64/MacroAssembler-mips64.h index 04adefe9cbb5..883cae7f0e11 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64.h +++ b/js/src/jit/mips64/MacroAssembler-mips64.h @@ -434,10 +434,6 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64 unboxNonDouble(address, dest.gpr()); } - template - void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, - MIRType slotType); - template void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) { switch (nbytes) { @@ -891,8 +887,6 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64 void loadFloat32x3(const Address& src, FloatRegister dest) { MOZ_CRASH("NYI"); } void loadFloat32x3(const BaseIndex& src, FloatRegister dest) { MOZ_CRASH("NYI"); } - void storeFloat32x3(FloatRegister src, const Address& dest) { MOZ_CRASH("NYI"); } - void storeFloat32x3(FloatRegister src, const BaseIndex& dest) { MOZ_CRASH("NYI"); } void loadAlignedSimd128Float(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); } void storeAlignedSimd128Float(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); } void loadUnalignedSimd128Float(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); } @@ -942,25 +936,10 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64 void storePtr(Register src, const Address& address); void storePtr(Register src, const BaseIndex& address); void storePtr(Register src, AbsoluteAddress dest); - void storeDouble(FloatRegister src, Address addr) { - ma_sd(src, addr); - } - void storeDouble(FloatRegister src, BaseIndex addr) { - MOZ_ASSERT(addr.offset == 0); - ma_sd(src, addr); - } void moveDouble(FloatRegister src, FloatRegister dest) { as_movd(dest, src); } - void storeFloat32(FloatRegister src, Address addr) { - ma_ss(src, addr); - } - void storeFloat32(FloatRegister src, BaseIndex addr) { - MOZ_ASSERT(addr.offset == 0); - ma_ss(src, addr); - } - void zeroDouble(FloatRegister reg) { moveToDouble(zero, reg); } diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 542ce19e805b..0363e8c98868 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -217,45 +217,6 @@ MacroAssemblerX64::handleFailureWithHandlerTail(void* handler) jmp(Operand(rsp, offsetof(ResumeFromException, target))); } -template -void -MacroAssemblerX64::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, - MIRType slotType) -{ - if (valueType == MIRType::Double) { - storeDouble(value.reg().typedReg().fpu(), dest); - return; - } - - // For known integers and booleans, we can just store the unboxed value if - // the slot has the same type. - if ((valueType == MIRType::Int32 || valueType == MIRType::Boolean) && slotType == valueType) { - if (value.constant()) { - Value val = value.value(); - if (valueType == MIRType::Int32) - store32(Imm32(val.toInt32()), dest); - else - store32(Imm32(val.toBoolean() ? 1 : 0), dest); - } else { - store32(value.reg().typedReg().gpr(), dest); - } - return; - } - - if (value.constant()) - storeValue(value.value(), dest); - else - storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), dest); -} - -template void -MacroAssemblerX64::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest, - MIRType slotType); - -template void -MacroAssemblerX64::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest, - MIRType slotType); - void MacroAssemblerX64::profilerEnterFrame(Register framePtr, Register scratch) { @@ -486,4 +447,43 @@ MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, j(cond, label); } +// ======================================================================== +// Memory access primitives. +template +void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, + MIRType slotType) +{ + if (valueType == MIRType::Double) { + storeDouble(value.reg().typedReg().fpu(), dest); + return; + } + + // For known integers and booleans, we can just store the unboxed value if + // the slot has the same type. + if ((valueType == MIRType::Int32 || valueType == MIRType::Boolean) && slotType == valueType) { + if (value.constant()) { + Value val = value.value(); + if (valueType == MIRType::Int32) + store32(Imm32(val.toInt32()), dest); + else + store32(Imm32(val.toBoolean() ? 1 : 0), dest); + } else { + store32(value.reg().typedReg().gpr(), dest); + } + return; + } + + if (value.constant()) + storeValue(value.value(), dest); + else + storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), dest); +} + +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest, + MIRType slotType); +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest, + MIRType slotType); //}}} check_macroassembler_style diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index 927681193ec5..a869371b13fa 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -888,9 +888,6 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared unboxNonDouble(Operand(src), dest.gpr()); } - template - void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, MIRType slotType); - template void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) { switch (nbytes) { diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h index 78ffb37a4207..91b29ed78322 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h @@ -910,6 +910,79 @@ MacroAssembler::branchTestMagicImpl(Condition cond, const T& t, L label) j(cond, label); } +// ======================================================================== +// Memory access primitives. +void +MacroAssembler::storeDouble(FloatRegister src, const Address& dest) +{ + vmovsd(src, dest); +} +void +MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& dest) +{ + vmovsd(src, dest); +} +void +MacroAssembler::storeDouble(FloatRegister src, const Operand& dest) +{ + switch (dest.kind()) { + case Operand::MEM_REG_DISP: + storeDouble(src, dest.toAddress()); + break; + case Operand::MEM_SCALE: + storeDouble(src, dest.toBaseIndex()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } +} + +void +MacroAssembler::storeFloat32(FloatRegister src, const Address& dest) +{ + vmovss(src, dest); +} +void +MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& dest) +{ + vmovss(src, dest); +} +void +MacroAssembler::storeFloat32(FloatRegister src, const Operand& dest) +{ + switch (dest.kind()) { + case Operand::MEM_REG_DISP: + storeFloat32(src, dest.toAddress()); + break; + case Operand::MEM_SCALE: + storeFloat32(src, dest.toBaseIndex()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } +} + +void +MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest) +{ + Address destZ(dest); + destZ.offset += 2 * sizeof(int32_t); + storeDouble(src, dest); + ScratchSimd128Scope scratch(*this); + vmovhlps(src, scratch, scratch); + storeFloat32(scratch, destZ); +} +void +MacroAssembler::storeFloat32x3(FloatRegister src, const BaseIndex& dest) +{ + BaseIndex destZ(dest); + destZ.offset += 2 * sizeof(int32_t); + storeDouble(src, dest); + ScratchSimd128Scope scratch(*this); + vmovhlps(src, scratch, scratch); + storeFloat32(scratch, destZ); +} + //}}} check_macroassembler_style // =============================================================== diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h index 9aeb940d2896..673e10f83e57 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h @@ -776,24 +776,6 @@ class MacroAssemblerX86Shared : public Assembler MOZ_CRASH("unexpected operand kind"); } } - void storeDouble(FloatRegister src, const Address& dest) { - vmovsd(src, dest); - } - void storeDouble(FloatRegister src, const BaseIndex& dest) { - vmovsd(src, dest); - } - void storeDouble(FloatRegister src, const Operand& dest) { - switch (dest.kind()) { - case Operand::MEM_REG_DISP: - storeDouble(src, dest.toAddress()); - break; - case Operand::MEM_SCALE: - storeDouble(src, dest.toBaseIndex()); - break; - default: - MOZ_CRASH("unexpected operand kind"); - } - } void moveDouble(FloatRegister src, FloatRegister dest) { // Use vmovapd instead of vmovsd to avoid dependencies. vmovapd(src, dest); @@ -1020,22 +1002,6 @@ class MacroAssemblerX86Shared : public Assembler vmovaps(src, dest); } - void storeFloat32x3(FloatRegister src, const Address& dest) { - Address destZ(dest); - destZ.offset += 2 * sizeof(int32_t); - storeDouble(src, dest); - ScratchSimd128Scope scratch(asMasm()); - vmovhlps(src, scratch, scratch); - storeFloat32(scratch, destZ); - } - void storeFloat32x3(FloatRegister src, const BaseIndex& dest) { - BaseIndex destZ(dest); - destZ.offset += 2 * sizeof(int32_t); - storeDouble(src, dest); - ScratchSimd128Scope scratch(asMasm()); - vmovhlps(src, scratch, scratch); - storeFloat32(scratch, destZ); - } void storeAlignedSimd128Float(FloatRegister src, const Address& dest) { vmovaps(src, Operand(dest)); } @@ -1153,24 +1119,6 @@ class MacroAssemblerX86Shared : public Assembler MOZ_CRASH("unexpected operand kind"); } } - void storeFloat32(FloatRegister src, const Address& dest) { - vmovss(src, dest); - } - void storeFloat32(FloatRegister src, const BaseIndex& dest) { - vmovss(src, dest); - } - void storeFloat32(FloatRegister src, const Operand& dest) { - switch (dest.kind()) { - case Operand::MEM_REG_DISP: - storeFloat32(src, dest.toAddress()); - break; - case Operand::MEM_SCALE: - storeFloat32(src, dest.toBaseIndex()); - break; - default: - MOZ_CRASH("unexpected operand kind"); - } - } void moveFloat32(FloatRegister src, FloatRegister dest) { // Use vmovaps instead of vmovss to avoid dependencies. vmovaps(src, dest); @@ -1386,7 +1334,7 @@ MacroAssemblerX86Shared::storeScalar(Register src, const Address& dest) } template <> inline void MacroAssemblerX86Shared::storeScalar(FloatRegister src, const Address& dest) { - storeFloat32(src, dest); + vmovss(src, dest); } } // namespace jit diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index bb6980e0aae6..27c8e973fb44 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -265,35 +265,6 @@ MacroAssemblerX86::handleFailureWithHandlerTail(void* handler) jmp(Operand(esp, offsetof(ResumeFromException, target))); } -template -void -MacroAssemblerX86::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, - MIRType slotType) -{ - if (valueType == MIRType::Double) { - storeDouble(value.reg().typedReg().fpu(), dest); - return; - } - - // Store the type tag if needed. - if (valueType != slotType) - storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), Operand(dest)); - - // Store the payload. - if (value.constant()) - storePayload(value.value(), Operand(dest)); - else - storePayload(value.reg().typedReg().gpr(), Operand(dest)); -} - -template void -MacroAssemblerX86::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest, - MIRType slotType); - -template void -MacroAssemblerX86::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest, - MIRType slotType); - void MacroAssemblerX86::profilerEnterFrame(Register framePtr, Register scratch) { @@ -523,4 +494,34 @@ MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, } } +// ======================================================================== +// Memory access primitives. +template +void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, + MIRType slotType) +{ + if (valueType == MIRType::Double) { + storeDouble(value.reg().typedReg().fpu(), dest); + return; + } + + // Store the type tag if needed. + if (valueType != slotType) + storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), Operand(dest)); + + // Store the payload. + if (value.constant()) + storePayload(value.value(), Operand(dest)); + else + storePayload(value.reg().typedReg().gpr(), Operand(dest)); +} + +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest, + MIRType slotType); +template void +MacroAssembler::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest, + MIRType slotType); + //}}} check_macroassembler_style diff --git a/js/src/jit/x86/MacroAssembler-x86.h b/js/src/jit/x86/MacroAssembler-x86.h index 407e1182bd45..1b4fec08f7c6 100644 --- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -804,10 +804,6 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared template inline void loadUnboxedValue(const T& src, MIRType type, AnyRegister dest); - template - void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest, - MIRType slotType); - template void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) { switch (nbytes) { From 220626ddcbb4c71eae3f45664aa1a55a40e34b72 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 23 May 2016 22:49:56 +0200 Subject: [PATCH 002/199] Bug 1245627: Canonicalize before storing a floating point value in deterministic mode; r=nbp Patch written by :Waldo and :bbouvier. MozReview-Commit-ID: 6vjiqfk0jvj --HG-- extra : rebase_source : 9a9b1bc3dae5fe417cf49f11c65b5ab36993d6eb extra : amend_source : 7f29a0d6278cf9130c8fd0c87bf44616ffb0ac4d extra : histedit_source : d7659b43c41c1531f147366e143c0233b7599c92 --- .../tests/asm.js/testSIMD-load-store.js | 5 ++ js/src/jit/MacroAssembler-inl.h | 78 ++++++++++++++----- js/src/jit/MacroAssembler.cpp | 4 - js/src/jit/MacroAssembler.h | 44 +++++++---- js/src/jit/arm/CodeGenerator-arm.cpp | 9 ++- js/src/jit/arm/MacroAssembler-arm-inl.h | 8 +- js/src/jit/arm64/MacroAssembler-arm64-inl.h | 8 +- js/src/jit/mips32/MacroAssembler-mips32-inl.h | 8 +- js/src/jit/mips64/MacroAssembler-mips64-inl.h | 8 +- js/src/jit/x64/CodeGenerator-x64.cpp | 33 +++++--- js/src/jit/x86-shared/Assembler-x86-shared.h | 3 + .../x86-shared/CodeGenerator-x86-shared.cpp | 32 ++++++++ .../jit/x86-shared/CodeGenerator-x86-shared.h | 2 + .../MacroAssembler-x86-shared-inl.h | 47 ++++++++--- js/src/jit/x86/CodeGenerator-x86.cpp | 40 +++++++--- js/src/vm/TypedArrayObject.cpp | 5 -- 16 files changed, 244 insertions(+), 90 deletions(-) diff --git a/js/src/jit-test/tests/asm.js/testSIMD-load-store.js b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js index 598d66b230c3..1abb9599145b 100644 --- a/js/src/jit-test/tests/asm.js/testSIMD-load-store.js +++ b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js @@ -171,6 +171,7 @@ function f32s(n, v) { return m.f32s((n|0) << 2 | 0, v); }; var vec = SIMD.Float32x4(5,6,7,8); var vec2 = SIMD.Float32x4(0,1,2,3); +var vecWithNaN = SIMD.Float32x4(NaN, 2, NaN, 4); reset(); f32s(0, vec); @@ -184,6 +185,10 @@ reset(); f32s(4, vec); assertEqX4(vec, slice(F32, 4, 4)); +reset(); +f32s(4, vecWithNaN); +assertEqX4(vecWithNaN, slice(F32, 4, 4)); + reset(); m.f32scst(vec2); assertEqX4(vec2, slice(F32, CONSTANT_INDEX, 4)); diff --git a/js/src/jit/MacroAssembler-inl.h b/js/src/jit/MacroAssembler-inl.h index 4c082b0626b2..a9ecfa6d4891 100644 --- a/js/src/jit/MacroAssembler-inl.h +++ b/js/src/jit/MacroAssembler-inl.h @@ -551,6 +551,66 @@ MacroAssembler::branchTestMagicValue(Condition cond, const ValueOperand& val, JS branchTestValue(cond, val, MagicValue(why), label); } +// ======================================================================== +// Canonicalization primitives. +void +MacroAssembler::canonicalizeFloat(FloatRegister reg) +{ + Label notNaN; + branchFloat(DoubleOrdered, reg, reg, ¬NaN); + loadConstantFloat32(float(JS::GenericNaN()), reg); + bind(¬NaN); +} + +void +MacroAssembler::canonicalizeFloatIfDeterministic(FloatRegister reg) +{ +#ifdef JS_MORE_DETERMINISTIC + // See the comment in TypedArrayObjectTemplate::getIndexValue. + canonicalizeFloat(reg); +#endif // JS_MORE_DETERMINISTIC +} + +void +MacroAssembler::canonicalizeDouble(FloatRegister reg) +{ + Label notNaN; + branchDouble(DoubleOrdered, reg, reg, ¬NaN); + loadConstantDouble(JS::GenericNaN(), reg); + bind(¬NaN); +} + +void +MacroAssembler::canonicalizeDoubleIfDeterministic(FloatRegister reg) +{ +#ifdef JS_MORE_DETERMINISTIC + // See the comment in TypedArrayObjectTemplate::getIndexValue. + canonicalizeDouble(reg); +#endif // JS_MORE_DETERMINISTIC +} + +// ======================================================================== +// Memory access primitives. +template void +MacroAssembler::storeDouble(FloatRegister src, const T& dest) +{ + canonicalizeDoubleIfDeterministic(src); + storeUncanonicalizedDouble(src, dest); +} + +template void MacroAssembler::storeDouble(FloatRegister src, const Address& dest); +template void MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& dest); + +template void +MacroAssembler::storeFloat32(FloatRegister src, const T& dest) +{ + canonicalizeFloatIfDeterministic(src); + storeUncanonicalizedFloat32(src, dest); +} + +template void MacroAssembler::storeFloat32(FloatRegister src, const Address& dest); +template void MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& dest); + //}}} check_macroassembler_style // =============================================================== @@ -591,24 +651,6 @@ MacroAssembler::addStackPtrTo(T t) #endif // !JS_CODEGEN_ARM64 -void -MacroAssembler::canonicalizeFloat(FloatRegister reg) -{ - Label notNaN; - branchFloat(DoubleOrdered, reg, reg, ¬NaN); - loadConstantFloat32(float(JS::GenericNaN()), reg); - bind(¬NaN); -} - -void -MacroAssembler::canonicalizeDouble(FloatRegister reg) -{ - Label notNaN; - branchDouble(DoubleOrdered, reg, reg, ¬NaN); - loadConstantDouble(JS::GenericNaN(), reg); - bind(¬NaN); -} - template void MacroAssembler::storeObjectOrNull(Register src, const T& dest) diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 0e6317cc3098..e9ddc6cbd009 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -238,10 +238,6 @@ StoreToTypedFloatArray(MacroAssembler& masm, int arrayType, const S& value, cons masm.storeFloat32(value, dest); break; case Scalar::Float64: -#ifdef JS_MORE_DETERMINISTIC - // See the comment in TypedArrayObjectTemplate::doubleToNative. - masm.canonicalizeDouble(value); -#endif masm.storeDouble(value, dest); break; case Scalar::Float32x4: diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 512eb63805b0..51f44484dbca 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -1122,18 +1122,38 @@ class MacroAssembler : public MacroAssemblerSpecific public: // ======================================================================== - // Memory access primitives. - inline void storeDouble(FloatRegister src, const Address& dest) - DEFINED_ON(x86_shared, arm, arm64, mips32, mips64); - inline void storeDouble(FloatRegister src, const BaseIndex& dest) - DEFINED_ON(x86_shared, arm, arm64, mips32, mips64); - inline void storeDouble(FloatRegister src, const Operand& dest) DEFINED_ON(x86_shared); + // Canonicalization primitives. + inline void canonicalizeDouble(FloatRegister reg); + inline void canonicalizeDoubleIfDeterministic(FloatRegister reg); - inline void storeFloat32(FloatRegister src, const Address& dest) + inline void canonicalizeFloat(FloatRegister reg); + inline void canonicalizeFloatIfDeterministic(FloatRegister reg); + + inline void canonicalizeFloat32x4(FloatRegister reg, FloatRegister scratch) + DEFINED_ON(x86_shared); + + public: + // ======================================================================== + // Memory access primitives. + inline void storeUncanonicalizedDouble(FloatRegister src, const Address& dest) DEFINED_ON(x86_shared, arm, arm64, mips32, mips64); - inline void storeFloat32(FloatRegister src, const BaseIndex& dest) + inline void storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& dest) DEFINED_ON(x86_shared, arm, arm64, mips32, mips64); - inline void storeFloat32(FloatRegister src, const Operand& dest) DEFINED_ON(x86_shared); + inline void storeUncanonicalizedDouble(FloatRegister src, const Operand& dest) + DEFINED_ON(x86_shared); + + template + inline void storeDouble(FloatRegister src, const T& dest); + + inline void storeUncanonicalizedFloat32(FloatRegister src, const Address& dest) + DEFINED_ON(x86_shared, arm, arm64, mips32, mips64); + inline void storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& dest) + DEFINED_ON(x86_shared, arm, arm64, mips32, mips64); + inline void storeUncanonicalizedFloat32(FloatRegister src, const Operand& dest) + DEFINED_ON(x86_shared); + + template + inline void storeFloat32(FloatRegister src, const T& dest); inline void storeFloat32x3(FloatRegister src, const Address& dest) PER_SHARED_ARCH; inline void storeFloat32x3(FloatRegister src, const BaseIndex& dest) PER_SHARED_ARCH; @@ -1332,10 +1352,6 @@ class MacroAssembler : public MacroAssemblerSpecific bind(&done); } - inline void canonicalizeDouble(FloatRegister reg); - - inline void canonicalizeFloat(FloatRegister reg); - template void loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegister dest, Register temp, Label* fail, bool canonicalizeDoubles = true, unsigned numElems = 0); @@ -1594,7 +1610,7 @@ class MacroAssembler : public MacroAssemblerSpecific #define DISPATCH_FLOATING_POINT_OP(method, type, arg1d, arg1f, arg2) \ MOZ_ASSERT(IsFloatingPointType(type)); \ - if (type == MIRType::Double) \ + if (type == MIRType::Double) \ method##Double(arg1d, arg2); \ else \ method##Float32(arg1f, arg2); \ diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index 656f4ce8a79b..24856dac77d0 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -2330,9 +2330,9 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) VFPRegister vd(ToFloatRegister(ins->value())); Address addr(HeapReg, ptrImm); if (size == 32) - masm.ma_vstr(vd.singleOverlay(), addr, Assembler::Always); + masm.storeFloat32(vd, addr); else - masm.ma_vstr(vd, addr, Assembler::Always); + masm.storeDouble(vd, addr); } else { masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm), ToRegister(ins->value()), Offset, Assembler::Always); @@ -2347,10 +2347,11 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) Register ptrReg = ToRegister(ptr); if (isFloat) { VFPRegister vd(ToFloatRegister(ins->value())); + BaseIndex addr(HeapReg, ptrReg, TimesOne, 0); if (size == 32) - masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, 0, Assembler::Always); + masm.storeFloat32(vd, addr); else - masm.ma_vstr(vd, HeapReg, ptrReg, 0, 0, Assembler::Always); + masm.storeDouble(vd, addr); } else { masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, ToRegister(ins->value()), Offset, Assembler::Always); diff --git a/js/src/jit/arm/MacroAssembler-arm-inl.h b/js/src/jit/arm/MacroAssembler-arm-inl.h index e32a29da51c8..b54e242032e1 100644 --- a/js/src/jit/arm/MacroAssembler-arm-inl.h +++ b/js/src/jit/arm/MacroAssembler-arm-inl.h @@ -1206,24 +1206,24 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag // ======================================================================== // Memory access primitives. void -MacroAssembler::storeDouble(FloatRegister src, const Address& addr) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr) { ma_vstr(src, addr); } void -MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& addr) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr) { uint32_t scale = Imm32::ShiftOf(addr.scale).value; ma_vstr(src, addr.base, addr.index, scale, addr.offset); } void -MacroAssembler::storeFloat32(FloatRegister src, const Address& addr) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr) { ma_vstr(src.asSingle(), addr); } void -MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr) { uint32_t scale = Imm32::ShiftOf(addr.scale).value; ma_vstr(src.asSingle(), addr.base, addr.index, scale, addr.offset); diff --git a/js/src/jit/arm64/MacroAssembler-arm64-inl.h b/js/src/jit/arm64/MacroAssembler-arm64-inl.h index a5e7a95a4a9a..9b1a70d9baf5 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h +++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h @@ -1301,23 +1301,23 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag // ======================================================================== // Memory access primitives. void -MacroAssembler::storeDouble(FloatRegister src, const Address& dest) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& dest) { Str(ARMFPRegister(src, 64), MemOperand(ARMRegister(dest.base, 64), dest.offset)); } void -MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& dest) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& dest) { doBaseIndex(ARMFPRegister(src, 64), dest, vixl::STR_d); } void -MacroAssembler::storeFloat32(FloatRegister src, const Address& addr) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr) { Str(ARMFPRegister(src, 32), MemOperand(ARMRegister(addr.base, 64), addr.offset)); } void -MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr) { doBaseIndex(ARMFPRegister(src, 32), addr, vixl::STR_s); } diff --git a/js/src/jit/mips32/MacroAssembler-mips32-inl.h b/js/src/jit/mips32/MacroAssembler-mips32-inl.h index c344b640659b..7baf0bf3dd92 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h +++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h @@ -423,24 +423,24 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag // ======================================================================== // Memory access primitives. void -MacroAssembler::storeDouble(FloatRegister src, const Address& addr) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr) { ma_sd(src, addr); } void -MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& addr) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr) { MOZ_ASSERT(addr.offset == 0); ma_sd(src, addr); } void -MacroAssembler::storeFloat32(FloatRegister src, const Address& addr) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr) { ma_ss(src, addr); } void -MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr) { MOZ_ASSERT(addr.offset == 0); ma_ss(src, addr); diff --git a/js/src/jit/mips64/MacroAssembler-mips64-inl.h b/js/src/jit/mips64/MacroAssembler-mips64-inl.h index 7f052d0b7eb5..3ae352d26f3b 100644 --- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h +++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h @@ -380,24 +380,24 @@ MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, JSWhyMag // ======================================================================== // Memory access primitives. void -MacroAssembler::storeDouble(FloatRegister src, const Address& addr) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr) { ma_sd(src, addr); } void -MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& addr) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr) { MOZ_ASSERT(addr.offset == 0); ma_sd(src, addr); } void -MacroAssembler::storeFloat32(FloatRegister src, const Address& addr) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr) { ma_ss(src, addr); } void -MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr) { MOZ_ASSERT(addr.offset == 0); ma_ss(src, addr); diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 26ea9c934e9d..c687e4857547 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -725,9 +725,9 @@ CodeGeneratorX64::storeSimd(Scalar::Type type, unsigned numElems, FloatRegister case Scalar::Float32x4: { switch (numElems) { // In memory-to-register mode, movss zeroes out the high lanes. - case 1: masm.storeFloat32(in, dstAddr); break; + case 1: masm.storeUncanonicalizedFloat32(in, dstAddr); break; // See comment above, which also applies to movsd. - case 2: masm.storeDouble(in, dstAddr); break; + case 2: masm.storeUncanonicalizedDouble(in, dstAddr); break; case 4: masm.storeUnalignedSimd128Float(in, dstAddr); break; default: MOZ_CRASH("unexpected size for partial load"); } @@ -816,11 +816,13 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) { const MAsmJSStoreHeap* mir = ins->mir(); Scalar::Type accessType = mir->accessType(); + const LAllocation* value = ins->value(); + + canonicalizeIfDeterministic(accessType, value); if (Scalar::isSimdType(accessType)) return emitSimdStore(ins); - const LAllocation* value = ins->value(); const LAllocation* ptr = ins->ptr(); Operand dstAddr = ptr->isBogus() ? Operand(HeapReg, mir->offset()) @@ -851,18 +853,29 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) } else { switch (accessType) { case Scalar::Int8: - case Scalar::Uint8: masm.movb(ToRegister(value), dstAddr); break; + case Scalar::Uint8: + masm.movb(ToRegister(value), dstAddr); + break; case Scalar::Int16: - case Scalar::Uint16: masm.movw(ToRegister(value), dstAddr); break; + case Scalar::Uint16: + masm.movw(ToRegister(value), dstAddr); + break; case Scalar::Int32: - case Scalar::Uint32: masm.movl(ToRegister(value), dstAddr); break; - case Scalar::Float32: masm.storeFloat32(ToFloatRegister(value), dstAddr); break; - case Scalar::Float64: masm.storeDouble(ToFloatRegister(value), dstAddr); break; + case Scalar::Uint32: + masm.movl(ToRegister(value), dstAddr); + break; + case Scalar::Float32: + masm.storeUncanonicalizedFloat32(ToFloatRegister(value), dstAddr); + break; + case Scalar::Float64: + masm.storeUncanonicalizedDouble(ToFloatRegister(value), dstAddr); + break; case Scalar::Float32x4: - case Scalar::Int32x4: MOZ_CRASH("SIMD stores must be handled in emitSimdStore"); + case Scalar::Int32x4: + MOZ_CRASH("SIMD stores must be handled in emitSimdStore"); case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: - MOZ_CRASH("unexpected array type"); + MOZ_CRASH("unexpected array type"); } } uint32_t after = masm.size(); diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index fb54aba4f6bb..451f1a0a20a6 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -2332,6 +2332,9 @@ class AssemblerX86Shared : public AssemblerShared void vcmpneqps(const Operand& src1, FloatRegister src0, FloatRegister dest) { vcmpps(X86Encoding::ConditionCmp_NEQ, src1, src0, dest); } + void vcmpordps(const Operand& src1, FloatRegister src0, FloatRegister dest) { + vcmpps(X86Encoding::ConditionCmp_ORD, src1, src0, dest); + } void vrcpps(const Operand& src, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); switch (src.kind()) { diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 90d06db0e35a..f7a9d54c9438 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -4067,5 +4067,37 @@ CodeGeneratorX86Shared::emitWasmSignedTruncateToInt32(OutOfLineWasmTruncateCheck masm.j(Assembler::Overflow, ool->entry()); } +void +CodeGeneratorX86Shared::canonicalizeIfDeterministic(Scalar::Type type, const LAllocation* value) +{ +#ifdef JS_MORE_DETERMINISTIC + switch (type) { + case Scalar::Float32: { + FloatRegister in = ToFloatRegister(value); + masm.canonicalizeFloatIfDeterministic(in); + break; + } + case Scalar::Float64: { + FloatRegister in = ToFloatRegister(value); + masm.canonicalizeDoubleIfDeterministic(in); + break; + } + case Scalar::Float32x4: { + FloatRegister in = ToFloatRegister(value); + MOZ_ASSERT(in.isSimd128()); + FloatRegister scratch = in != xmm0.asSimd128() ? xmm0 : xmm1; + masm.push(scratch); + masm.canonicalizeFloat32x4(in, scratch); + masm.pop(scratch); + break; + } + default: { + // Other types don't need canonicalization. + break; + } + } +#endif // JS_MORE_DETERMINISTIC +} + } // namespace jit } // namespace js diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 807acf125a38..132ae5a2f224 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -339,6 +339,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value, const T& mem); void setReturnDoubleRegs(LiveRegisterSet* regs); + + void canonicalizeIfDeterministic(Scalar::Type type, const LAllocation* value); }; // An out-of-line bailout thunk. diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h index 91b29ed78322..8fb70e0769e7 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h @@ -910,58 +910,85 @@ MacroAssembler::branchTestMagicImpl(Condition cond, const T& t, L label) j(cond, label); } +// ======================================================================== +// Canonicalization primitives. +void +MacroAssembler::canonicalizeFloat32x4(FloatRegister reg, FloatRegister scratch) +{ + ScratchSimd128Scope scratch2(*this); + + MOZ_ASSERT(scratch.asSimd128() != scratch2.asSimd128()); + MOZ_ASSERT(reg.asSimd128() != scratch2.asSimd128()); + MOZ_ASSERT(reg.asSimd128() != scratch.asSimd128()); + + FloatRegister mask = scratch; + vcmpordps(Operand(reg), reg, mask); + + FloatRegister ifFalse = scratch2; + float nanf = float(JS::GenericNaN()); + loadConstantSimd128Float(SimdConstant::SplatX4(nanf), ifFalse); + + bitwiseAndX4(Operand(mask), reg); + bitwiseAndNotX4(Operand(ifFalse), mask); + bitwiseOrX4(Operand(mask), reg); +} + // ======================================================================== // Memory access primitives. void -MacroAssembler::storeDouble(FloatRegister src, const Address& dest) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& dest) { vmovsd(src, dest); } void -MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& dest) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& dest) { vmovsd(src, dest); } void -MacroAssembler::storeDouble(FloatRegister src, const Operand& dest) +MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Operand& dest) { switch (dest.kind()) { case Operand::MEM_REG_DISP: - storeDouble(src, dest.toAddress()); + storeUncanonicalizedDouble(src, dest.toAddress()); break; case Operand::MEM_SCALE: - storeDouble(src, dest.toBaseIndex()); + storeUncanonicalizedDouble(src, dest.toBaseIndex()); break; default: MOZ_CRASH("unexpected operand kind"); } } +template void MacroAssembler::storeDouble(FloatRegister src, const Operand& dest); + void -MacroAssembler::storeFloat32(FloatRegister src, const Address& dest) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& dest) { vmovss(src, dest); } void -MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& dest) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& dest) { vmovss(src, dest); } void -MacroAssembler::storeFloat32(FloatRegister src, const Operand& dest) +MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Operand& dest) { switch (dest.kind()) { case Operand::MEM_REG_DISP: - storeFloat32(src, dest.toAddress()); + storeUncanonicalizedFloat32(src, dest.toAddress()); break; case Operand::MEM_SCALE: - storeFloat32(src, dest.toBaseIndex()); + storeUncanonicalizedFloat32(src, dest.toBaseIndex()); break; default: MOZ_CRASH("unexpected operand kind"); } } +template void MacroAssembler::storeFloat32(FloatRegister src, const Operand& dest); + void MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest) { diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 082ef788cc20..08bec2367e96 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -473,16 +473,34 @@ CodeGeneratorX86::store(Scalar::Type accessType, const LAllocation* value, const switch (accessType) { case Scalar::Int8: case Scalar::Uint8Clamped: - case Scalar::Uint8: masm.movbWithPatch(ToRegister(value), dstAddr); break; + case Scalar::Uint8: + masm.movbWithPatch(ToRegister(value), dstAddr); + break; + case Scalar::Int16: - case Scalar::Uint16: masm.movwWithPatch(ToRegister(value), dstAddr); break; + case Scalar::Uint16: + masm.movwWithPatch(ToRegister(value), dstAddr); + break; + case Scalar::Int32: - case Scalar::Uint32: masm.movlWithPatch(ToRegister(value), dstAddr); break; - case Scalar::Float32: masm.vmovssWithPatch(ToFloatRegister(value), dstAddr); break; - case Scalar::Float64: masm.vmovsdWithPatch(ToFloatRegister(value), dstAddr); break; + case Scalar::Uint32: + masm.movlWithPatch(ToRegister(value), dstAddr); + break; + + case Scalar::Float32: + masm.vmovssWithPatch(ToFloatRegister(value), dstAddr); + break; + + case Scalar::Float64: + masm.vmovsdWithPatch(ToFloatRegister(value), dstAddr); + break; + case Scalar::Float32x4: - case Scalar::Int32x4: MOZ_CRASH("SIMD stores should be handled in emitSimdStore"); - case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected type"); + case Scalar::Int32x4: + MOZ_CRASH("SIMD stores should be handled in emitSimdStore"); + + case Scalar::MaxTypedArrayViewType: + MOZ_CRASH("unexpected type"); } } @@ -493,8 +511,10 @@ CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStati Scalar::Type accessType = mir->accessType(); Register ptr = ToRegister(ins->ptr()); const LAllocation* value = ins->value(); - uint32_t offset = mir->offset(); + canonicalizeIfDeterministic(accessType, value); + + uint32_t offset = mir->offset(); if (!mir->needsBoundsCheck()) { Operand dstAddr(ptr, int32_t(mir->base().asValue()) + int32_t(offset)); store(accessType, value, dstAddr); @@ -605,11 +625,13 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) { const MAsmJSStoreHeap* mir = ins->mir(); Scalar::Type accessType = mir->accessType(); + const LAllocation* value = ins->value(); + + canonicalizeIfDeterministic(accessType, value); if (Scalar::isSimdType(accessType)) return emitSimdStore(ins); - const LAllocation* value = ins->value(); const LAllocation* ptr = ins->ptr(); Operand dstAddr = ptr->isBogus() ? Operand(PatchedAbsoluteAddress(mir->offset())) diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 27757f7c3eca..de03e8248b45 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -49,12 +49,7 @@ using namespace js; using namespace js::gc; -using mozilla::IsNaN; -using mozilla::NegativeInfinity; -using mozilla::PodCopy; -using mozilla::PositiveInfinity; using JS::CanonicalizeNaN; -using JS::GenericNaN; using JS::ToInt32; using JS::ToUint32; From 29923a5e9720fdd20ffe587d614d9a8025ccb7a0 Mon Sep 17 00:00:00 2001 From: Ethan Lin Date: Tue, 31 May 2016 16:07:38 +0800 Subject: [PATCH 003/199] Bug 1275478 - Workaround for svg mask on windows. r=mstange MozReview-Commit-ID: GilZz7AJTQX --- layout/svg/nsSVGIntegrationUtils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 0d580743db15..d487ba5456dd 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -464,7 +464,8 @@ GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams, // when mask-mode is not add(source over). Switch to skia when CG backend // detected. RefPtr maskDT = - (ctx.GetDrawTarget()->GetBackendType() == BackendType::COREGRAPHICS) + (ctx.GetDrawTarget()->GetBackendType() == BackendType::COREGRAPHICS || + ctx.GetDrawTarget()->GetBackendType() == BackendType::DIRECT2D1_1) ? Factory::CreateDrawTarget(BackendType::SKIA, maskSurfaceRect.Size(), SurfaceFormat::A8) : ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(), From 378ce6e9b847b046211785d7e6312150e2bf4dd4 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Mon, 30 May 2016 21:12:35 +0800 Subject: [PATCH 004/199] Bug 1276838 - improve coding style of calling cubeb functions. r=kinetik. MozReview-Commit-ID: 1KjjF6StM0a --HG-- extra : rebase_source : 5174bedd6400c88b01cb0018f9ff64f1eadb2d4a --- dom/media/AudioStream.cpp | 40 +++++++++++++++------------------------ dom/media/AudioStream.h | 3 +++ 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/dom/media/AudioStream.cpp b/dom/media/AudioStream.cpp index c17ff04baa23..8ed269a7c2ab 100644 --- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -315,6 +315,13 @@ struct ToCubebFormat { static const cubeb_sample_format value = CUBEB_SAMPLE_S16NE; }; +template +int AudioStream::InvokeCubeb(Function aFunction, Args&&... aArgs) +{ + MonitorAutoUnlock mon(mMonitor); + return aFunction(mCubebStream.get(), Forward(aArgs)...); +} + nsresult AudioStream::Init(uint32_t aNumChannels, uint32_t aRate, const dom::AudioChannel aAudioChannel) @@ -414,16 +421,11 @@ AudioStream::Start() { MonitorAutoLock mon(mMonitor); if (mState == INITIALIZED) { + // DataCallback might be called before InvokeCubeb returns + // if cubeb_stream_start() succeeds. mState must be set to STARTED + // beforehand. mState = STARTED; - int r; - { - MonitorAutoUnlock mon(mMonitor); - r = cubeb_stream_start(mCubebStream.get()); - // DataCallback might be called before we exit this scope - // if cubeb_stream_start() succeeds. mState must be set to STARTED - // beforehand. - } - if (r != CUBEB_OK) { + if (InvokeCubeb(cubeb_stream_start) != CUBEB_OK) { mState = ERRORED; } LOG("started, state %s", mState == STARTED ? "STARTED" : "ERRORED"); @@ -444,11 +446,7 @@ AudioStream::Pause() return; } - int r; - { - MonitorAutoUnlock mon(mMonitor); - r = cubeb_stream_stop(mCubebStream.get()); - } + int r = InvokeCubeb(cubeb_stream_stop); if (mState != ERRORED && r == CUBEB_OK) { mState = STOPPED; } @@ -462,11 +460,7 @@ AudioStream::Resume() return; } - int r; - { - MonitorAutoUnlock mon(mMonitor); - r = cubeb_stream_start(mCubebStream.get()); - } + int r = InvokeCubeb(cubeb_stream_start); if (mState != ERRORED && r == CUBEB_OK) { mState = STARTED; } @@ -514,13 +508,9 @@ AudioStream::GetPositionInFramesUnlocked() } uint64_t position = 0; - { - MonitorAutoUnlock mon(mMonitor); - if (cubeb_stream_get_position(mCubebStream.get(), &position) != CUBEB_OK) { - return -1; - } + if (InvokeCubeb(cubeb_stream_get_position, &position) != CUBEB_OK) { + return -1; } - return std::min(position, INT64_MAX); } diff --git a/dom/media/AudioStream.h b/dom/media/AudioStream.h index ce2c6f1c08a3..f7e1c08e790b 100644 --- a/dom/media/AudioStream.h +++ b/dom/media/AudioStream.h @@ -340,6 +340,9 @@ private: void GetUnprocessed(AudioBufferWriter& aWriter); void GetTimeStretched(AudioBufferWriter& aWriter); + template + int InvokeCubeb(Function aFunction, Args&&... aArgs); + // The monitor is held to protect all access to member variables. Monitor mMonitor; From 1ad8c294dd3e57e2395568ccbca8893612b8a7af Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 31 May 2016 15:45:42 +0800 Subject: [PATCH 005/199] Bug 1275450 - Part 1. Generate a transparent black mask layer when there is no resolvable mask source or image r=mstange MozReview-Commit-ID: I2QlZnz07TL --HG-- extra : rebase_source : accf30f8fe945d59d4fab84c2f9eb42df23c267c --- layout/svg/nsSVGIntegrationUtils.cpp | 75 ++++++++++++++-------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index d487ba5456dd..5d670fc47e3a 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -409,48 +409,28 @@ private: nsPoint mOffset; }; -static bool -HasMaskToDraw(const nsStyleSVGReset* aSVGReset, - nsSVGEffects::EffectProperties& aEffectProperties) -{ - nsTArray svgMaskFrames = aEffectProperties.GetMaskFrames(); - for (int i = svgMaskFrames.Length() - 1; i >= 0 ; i--) { - nsSVGMaskFrame *maskFrame = svgMaskFrames[i]; - - // We found a SVG mask or an image mask. - if (maskFrame || !aSVGReset->mMask.mLayers[i].mImage.IsEmpty()) { - return true; - } - } - - return false; -} - static void GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams, float aOpacity, nsStyleContext* aSC, - nsSVGEffects::EffectProperties& aEffectProperties, + const nsTArray& aMaskFrames, const gfxPoint& aOffest, Matrix& aOutMaskTransform, RefPtr& aOutMaskSurface) { const nsStyleSVGReset *svgReset = aSC->StyleSVGReset(); - MOZ_ASSERT(HasMaskToDraw(svgReset, aEffectProperties)); - - nsTArray svgMaskFrames = aEffectProperties.GetMaskFrames(); - MOZ_ASSERT(svgMaskFrames.Length() == svgReset->mMask.mImageCount); + MOZ_ASSERT(aMaskFrames.Length() > 0); gfxMatrix cssPxToDevPxMatrix = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame); gfxContext& ctx = aParams.ctx; - // There is only one mask. And that mask is a SVG mask. - if ((svgMaskFrames.Length() == 1) && svgMaskFrames[0]) { + // There is only one SVG mask. + if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) { aOutMaskSurface = - svgMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame, - cssPxToDevPxMatrix, aOpacity, - &aOutMaskTransform, - svgReset->mMask.mLayers[0].mMaskMode); + aMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame, + cssPxToDevPxMatrix, aOpacity, + &aOutMaskTransform, + svgReset->mMask.mLayers[0].mMaskMode); return; } @@ -460,6 +440,10 @@ GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams, IntRect maskSurfaceRect = RoundedOut(ToRect(clipExtents)); ctx.Restore(); + if (maskSurfaceRect.IsEmpty()) { + return; + } + // Mask composition result on CoreGraphic::A8 surface is not correct // when mask-mode is not add(source over). Switch to skia when CG backend // detected. @@ -470,6 +454,7 @@ GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams, SurfaceFormat::A8) : ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(), SurfaceFormat::A8); + RefPtr maskContext = gfxContext::ForDrawTarget(maskDT); // Set ctx's matrix on maskContext, offset by the maskSurfaceRect's position. @@ -480,10 +465,10 @@ GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams, // Multiple SVG masks interleave with image mask. Paint each layer onto maskDT // one at a time. - for (int i = svgMaskFrames.Length() - 1; i >= 0 ; i--) { - nsSVGMaskFrame *maskFrame = svgMaskFrames[i]; + for (int i = aMaskFrames.Length() - 1; i >= 0 ; i--) { + nsSVGMaskFrame *maskFrame = aMaskFrames[i]; - CompositionOp compositionOp = (i == int(svgMaskFrames.Length() - 1)) + CompositionOp compositionOp = (i == int(aMaskFrames.Length() - 1)) ? CompositionOp::OP_OVER : nsCSSRendering::GetGFXCompositeMode(svgReset->mMask.mLayers[i].mComposite); @@ -630,7 +615,19 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame); const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset(); - bool hasMaskToDraw = HasMaskToDraw(svgReset, effectProperties); + nsTArray maskFrames = effectProperties.GetMaskFrames(); + // For a HTML doc: + // According to css-masking spec, always create a mask surface when we + // have any item in maskFrame even if all of those items are + // non-resolvable or , we still need to create a + // transparent black mask layer under this condition. + // For a SVG doc: + // SVG 1.1 say that if we fail to resolve a mask, we should draw the + // object unmasked. + nsIDocument* currentDoc = frame->PresContext()->Document(); + bool shouldGenerateMaskLayer = currentDoc->IsSVGDocument() + ? maskFrames.Length() == 1 && maskFrames[0] + : maskFrames.Length() > 0; // These are used if we require a temporary surface for a custom blend mode. RefPtr target = &aParams.ctx; @@ -641,7 +638,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) * rendering, which necessitates rendering into another surface. */ if (opacity != 1.0f || (clipPathFrame && !isTrivialClip) || frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL - || hasMaskToDraw) { + || shouldGenerateMaskLayer) { complexEffects = true; context.Save(); @@ -653,13 +650,13 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) Matrix maskTransform; RefPtr maskSurface; - if (hasMaskToDraw) { + if (shouldGenerateMaskLayer) { GenerateMaskSurface(aParams, opacity, firstFrame->StyleContext(), - effectProperties, devPixelOffsetToUserSpace, + maskFrames, devPixelOffsetToUserSpace, maskTransform, maskSurface); } - if (hasMaskToDraw && !maskSurface) { + if (shouldGenerateMaskLayer && !maskSurface) { // Entire surface is clipped out. context.Restore(); return; @@ -700,7 +697,8 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) } } - if (opacity != 1.0f || hasMaskToDraw || (clipPathFrame && !isTrivialClip)) { + if (opacity != 1.0f || shouldGenerateMaskLayer || + (clipPathFrame && !isTrivialClip)) { target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform); } } @@ -745,7 +743,8 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) return; } - if (opacity != 1.0f || hasMaskToDraw || (clipPathFrame && !isTrivialClip)) { + if (opacity != 1.0f || shouldGenerateMaskLayer || + (clipPathFrame && !isTrivialClip)) { target->PopGroupAndBlend(); } From 4c1e46c8ca8220d6169c2951398c2340a6ff56ca Mon Sep 17 00:00:00 2001 From: cku Date: Mon, 30 May 2016 15:57:43 +0800 Subject: [PATCH 006/199] Bug 1275450 - Part 2. Reftest for unresolvable mask-reference r=mstange MozReview-Commit-ID: Je2mJvwBiF7 --HG-- extra : rebase_source : ab20eada13e14363138dc5b234d5bc68a7d4b10e --- .../w3c-css/submitted/masking/blank.html | 9 ++++++ .../submitted/masking/mask-image-4a.html | 26 ++++++++++++++++ .../submitted/masking/mask-image-4b.html | 31 +++++++++++++++++++ .../w3c-css/submitted/masking/reftest.list | 2 ++ layout/svg/nsSVGIntegrationUtils.cpp | 1 + 5 files changed, 69 insertions(+) create mode 100644 layout/reftests/w3c-css/submitted/masking/blank.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-image-4a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/mask-image-4b.html diff --git a/layout/reftests/w3c-css/submitted/masking/blank.html b/layout/reftests/w3c-css/submitted/masking/blank.html new file mode 100644 index 000000000000..abb1b5472843 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/blank.html @@ -0,0 +1,9 @@ + + + + Blank document + + + + + \ No newline at end of file diff --git a/layout/reftests/w3c-css/submitted/masking/mask-image-4a.html b/layout/reftests/w3c-css/submitted/masking/mask-image-4a.html new file mode 100644 index 000000000000..ae3ad76a20cd --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-image-4a.html @@ -0,0 +1,26 @@ + + + + + CSS Masking: mask-image: unresovlable mask url + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/mask-image-4b.html b/layout/reftests/w3c-css/submitted/masking/mask-image-4b.html new file mode 100644 index 000000000000..ed0022a4c90b --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/mask-image-4b.html @@ -0,0 +1,31 @@ + + + + + CSS Masking: mask-image: unresovlable mask url + + + + + + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/reftest.list b/layout/reftests/w3c-css/submitted/masking/reftest.list index 7525a97b3547..1484b587841f 100644 --- a/layout/reftests/w3c-css/submitted/masking/reftest.list +++ b/layout/reftests/w3c-css/submitted/masking/reftest.list @@ -25,6 +25,8 @@ fails == mask-image-3b.html mask-image-3-ref.html fails == mask-image-3c.html mask-image-3-ref.html fails == mask-image-3d.html mask-image-3-ref.html fails == mask-image-3e.html mask-image-3-ref.html +fails == mask-image-4a.html blank.html +fails == mask-image-4b.html blank.html # mask-clip test cases fails == mask-clip-1.html mask-clip-1-ref.html diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 5d670fc47e3a..bec8f8474f83 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -444,6 +444,7 @@ GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams, return; } + // Mask composition result on CoreGraphic::A8 surface is not correct // when mask-mode is not add(source over). Switch to skia when CG backend // detected. From e8cc0375203c8ad089afc4d471a58a49d1a279f4 Mon Sep 17 00:00:00 2001 From: aleth Date: Thu, 26 May 2016 20:53:12 +0200 Subject: [PATCH 007/199] Bug 1272322 - Ensure PlacesRemoteTabsAutocompleteProvider doesn't fail when sync isn't built. r=mak --- .../PlacesRemoteTabsAutocompleteProvider.jsm | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/toolkit/components/places/PlacesRemoteTabsAutocompleteProvider.jsm b/toolkit/components/places/PlacesRemoteTabsAutocompleteProvider.jsm index b3ca38ba8f83..b4edb9891349 100644 --- a/toolkit/components/places/PlacesRemoteTabsAutocompleteProvider.jsm +++ b/toolkit/components/places/PlacesRemoteTabsAutocompleteProvider.jsm @@ -15,7 +15,6 @@ const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://services-sync/main.js"); XPCOMUtils.defineLazyGetter(this, "weaveXPCService", function() { return Cc["@mozilla.org/weave/service;1"] @@ -23,6 +22,16 @@ XPCOMUtils.defineLazyGetter(this, "weaveXPCService", function() { .wrappedJSObject; }); +XPCOMUtils.defineLazyGetter(this, "Weave", () => { + try { + let {Weave} = Cu.import("resource://services-sync/main.js", {}); + return Weave; + } catch (ex) { + // The app didn't build Sync. + } + return null; +}); + // from MDN... function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); @@ -111,7 +120,8 @@ this.PlacesRemoteTabsAutocompleteProvider = { // a promise that resolves with an array of matching remote tabs. getMatches(searchString) { // If Sync isn't configured we bail early. - if (!Services.prefs.prefHasUserValue("services.sync.username")) { + if (Weave === null || + !Services.prefs.prefHasUserValue("services.sync.username")) { return Promise.resolve([]); } From 90af12edd2b57173a720bf14304a38672cd86f65 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Tue, 31 May 2016 12:34:17 +0200 Subject: [PATCH 008/199] Bug 1276068 - Correct the path flatness estimation computation. r=Bas --- gfx/2d/Path.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/2d/Path.cpp b/gfx/2d/Path.cpp index b4ed64372329..17181e07baec 100644 --- a/gfx/2d/Path.cpp +++ b/gfx/2d/Path.cpp @@ -259,7 +259,7 @@ FlattenBezierCurveSegment(const BezierControlPoints &aControlPoints, double t = 0; while (t < 1.0) { - PointD cp21 = currentCP.mCP2 - currentCP.mCP3; + PointD cp21 = currentCP.mCP2 - currentCP.mCP1; PointD cp31 = currentCP.mCP3 - currentCP.mCP1; /* To remove divisions and check for divide-by-zero, this is optimized from: From d3cf11f109d50a63786e92b4e166f1f13d9b5288 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 31 May 2016 12:54:24 +0200 Subject: [PATCH 009/199] Bug 1261693 - Implement HTMLInputElement::GetFiles, r=smaug --- dom/filesystem/tests/test_basic.html | 50 ++++ dom/html/HTMLInputElement.cpp | 397 +++++++++++++++++++++++++++ dom/html/HTMLInputElement.h | 8 + dom/webidl/HTMLInputElement.webidl | 3 + 4 files changed, 458 insertions(+) diff --git a/dom/filesystem/tests/test_basic.html b/dom/filesystem/tests/test_basic.html index ba8cabc118d3..4cc3bff13bd7 100644 --- a/dom/filesystem/tests/test_basic.html +++ b/dom/filesystem/tests/test_basic.html @@ -77,6 +77,55 @@ function test_duplicateGetFilesAndDirectories() { script.sendAsyncMessage("dir.open", { path: 'test' }); } +function test_inputGetFiles() { + var url = SimpleTest.getTestFileURL("script_fileList.js"); + var script = SpecialPowers.loadChromeScript(url); + + function onOpened(message) { + var fileList = document.getElementById('fileList'); + SpecialPowers.wrap(fileList).mozSetDirectory(message.dir); + + fileList.getFilesAndDirectories() + .then(function(result) { + is(result.length, 1, "getFilesAndDirectories should return 1 element"); + ok(result[0] instanceof Directory, "getFilesAndDirectories should return 1 directory"); + + return fileList.getFiles(false); + }) + .then(function(result) { + is(result.length, 1, "getFiles should return 1 element"); + ok(result[0] instanceof File, "getFile should return 1 file"); + is(result[0].name, 'foo.txt', "getFiles()[0].name should be 'foo.txt'"); + is(result[0].webkitRelativePath, '/foo.txt', "getFiles()[0].webkitRelativePath should be '/foo.txt'"); + + return fileList.getFiles(true); + }) + .then(function(result) { + is(result.length, 2, "getFiles should return 2 elements"); + + function checkFile(file) { + ok(file instanceof File, "getFile[x] should return a file"); + if (file.name == 'foo.txt') { + is(file.webkitRelativePath, '/foo.txt', "getFiles()[x].webkitRelativePath should be '/foo.txt'"); + } else { + is(file.name, 'bar.txt', "getFiles()[x].name should be 'bar.txt'"); + is(file.webkitRelativePath, '/subdir/bar.txt', "getFiles()[x].webkitRelativePath should be '/subdir/bar.txt'"); + } + } + + checkFile(result[0]); + checkFile(result[1]); + }) + .then(function() { + script.destroy(); + next(); + }); + } + + script.addMessageListener("dir.opened", onOpened); + script.sendAsyncMessage("dir.open", { path: 'test' }); +} + var tests = [ function() { setup_tests(next); }, @@ -95,6 +144,7 @@ var tests = [ function() { test_getFiles(directory, false, next); }, test_duplicateGetFilesAndDirectories, + test_inputGetFiles, test_simpleFilePicker ]; diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 101cb626ae32..0021b1e1e10d 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -218,6 +218,321 @@ const Decimal HTMLInputElement::kStepAny = Decimal(0); #define PROGRESS_STR "progress" static const uint32_t kProgressEventInterval = 50; // ms +// Retrieving the list of files can be very time/IO consuming. We use this +// helper class to do it just once. +class GetFilesHelper final : public Runnable +{ +public: + static already_AddRefed + Create(nsIGlobalObject* aGlobal, + const nsTArray& aFilesOrDirectory, + bool aRecursiveFlag, ErrorResult& aRv) + { + MOZ_ASSERT(aGlobal); + + RefPtr helper = new GetFilesHelper(aGlobal, aRecursiveFlag); + + nsAutoString directoryPath; + + for (uint32_t i = 0; i < aFilesOrDirectory.Length(); ++i) { + const OwningFileOrDirectory& data = aFilesOrDirectory[i]; + if (data.IsFile()) { + if (!helper->mFiles.AppendElement(data.GetAsFile(), fallible)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + } else { + MOZ_ASSERT(data.IsDirectory()); + + // We support the upload of only 1 top-level directory from our + // directory picker. This means that we cannot have more than 1 + // Directory object in aFilesOrDirectory array. + MOZ_ASSERT(directoryPath.IsEmpty()); + + RefPtr directory = data.GetAsDirectory(); + MOZ_ASSERT(directory); + + aRv = directory->GetFullRealPath(directoryPath); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + } + } + + // No directories to explore. + if (directoryPath.IsEmpty()) { + helper->mListingCompleted = true; + return helper.forget(); + } + + MOZ_ASSERT(helper->mFiles.IsEmpty()); + helper->SetDirectoryPath(directoryPath); + + nsCOMPtr target = + do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + MOZ_ASSERT(target); + + aRv = target->Dispatch(helper, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return helper.forget(); + } + + void + AddPromise(Promise* aPromise) + { + MOZ_ASSERT(aPromise); + + // Still working. + if (!mListingCompleted) { + mPromises.AppendElement(aPromise); + return; + } + + MOZ_ASSERT(mPromises.IsEmpty()); + ResolveOrRejectPromise(aPromise); + } + + // CC methods + void Unlink() + { + mGlobal = nullptr; + mFiles.Clear(); + mPromises.Clear(); + } + + void Traverse(nsCycleCollectionTraversalCallback &cb) + { + GetFilesHelper* tmp = this; + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromises); + } + +private: + GetFilesHelper(nsIGlobalObject* aGlobal, bool aRecursiveFlag) + : mGlobal(aGlobal) + , mRecursiveFlag(aRecursiveFlag) + , mListingCompleted(false) + , mErrorResult(NS_OK) + { + MOZ_ASSERT(aGlobal); + } + + void + SetDirectoryPath(const nsAString& aDirectoryPath) + { + mDirectoryPath = aDirectoryPath; + } + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(!mDirectoryPath.IsEmpty()); + MOZ_ASSERT(!mListingCompleted); + + // First step is to retrieve the list of file paths. + // This happens in the I/O thread. + if (!NS_IsMainThread()) { + RunIO(); + return NS_DispatchToMainThread(this); + } + + RunMainThread(); + + // We mark the operation as completed here. + mListingCompleted = true; + + // Let's process the pending promises. + nsTArray> promises; + promises.SwapElements(mPromises); + + for (uint32_t i = 0; i < promises.Length(); ++i) { + ResolveOrRejectPromise(promises[i]); + } + + return NS_OK; + } + + void + RunIO() + { + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!mDirectoryPath.IsEmpty()); + MOZ_ASSERT(!mListingCompleted); + + nsCOMPtr file; + mErrorResult = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(mDirectoryPath), true, + getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(mErrorResult))) { + return; + } + + nsAutoString path; + path.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); + + mErrorResult = ExploreDirectory(path, file); + } + + void + RunMainThread() + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mDirectoryPath.IsEmpty()); + MOZ_ASSERT(!mListingCompleted); + + // If there is an error, do nothing. + if (NS_FAILED(mErrorResult)) { + return; + } + + // Create the sequence of Files. + for (uint32_t i = 0; i < mTargetPathArray.Length(); ++i) { + nsCOMPtr file; + mErrorResult = + NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(mTargetPathArray[i].mRealPath), + true, getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(mErrorResult))) { + mFiles.Clear(); + return; + } + + RefPtr domFile = + File::CreateFromFile(mGlobal, file); + MOZ_ASSERT(domFile); + + domFile->SetPath(mTargetPathArray[i].mDomPath); + + if (!mFiles.AppendElement(domFile, fallible)) { + mErrorResult = NS_ERROR_OUT_OF_MEMORY; + mFiles.Clear(); + return; + } + } + } + + nsresult + ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile) + { + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aFile); + + nsCOMPtr entries; + nsresult rv = aFile->GetDirectoryEntries(getter_AddRefs(entries)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + for (;;) { + bool hasMore = false; + if (NS_WARN_IF(NS_FAILED(entries->HasMoreElements(&hasMore))) || !hasMore) { + break; + } + + nsCOMPtr supp; + if (NS_WARN_IF(NS_FAILED(entries->GetNext(getter_AddRefs(supp))))) { + break; + } + + nsCOMPtr currFile = do_QueryInterface(supp); + MOZ_ASSERT(currFile); + + bool isLink, isSpecial, isFile, isDir; + if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) || + NS_FAILED(currFile->IsSpecial(&isSpecial))) || + isLink || isSpecial) { + continue; + } + + if (NS_WARN_IF(NS_FAILED(currFile->IsFile(&isFile)) || + NS_FAILED(currFile->IsDirectory(&isDir))) || + !(isFile || isDir)) { + continue; + } + + // The new domPath + nsAutoString domPath; + domPath.Assign(aDOMPath); + if (!aDOMPath.EqualsLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL)) { + domPath.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); + } + + nsAutoString leafName; + if (NS_WARN_IF(NS_FAILED(currFile->GetLeafName(leafName)))) { + continue; + } + domPath.Append(leafName); + + if (isFile) { + FileData* data = mTargetPathArray.AppendElement(fallible); + if (!data) { + return NS_ERROR_OUT_OF_MEMORY; + } + + if (NS_WARN_IF(NS_FAILED(currFile->GetPath(data->mRealPath)))) { + continue; + } + + data->mDomPath = domPath; + continue; + } + + MOZ_ASSERT(isDir); + if (!mRecursiveFlag) { + continue; + } + + // Recursive. + rv = ExploreDirectory(domPath, currFile); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + return NS_OK; + } + + void + ResolveOrRejectPromise(Promise* aPromise) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mListingCompleted); + MOZ_ASSERT(aPromise); + + // Error propagation. + if (NS_FAILED(mErrorResult)) { + aPromise->MaybeReject(mErrorResult); + return; + } + + aPromise->MaybeResolve(mFiles); + } + + nsCOMPtr mGlobal; + + bool mRecursiveFlag; + bool mListingCompleted; + nsString mDirectoryPath; + + // We populate this array in the I/O thread with the paths of the Files that + // we want to send as result to the promise objects. + struct FileData { + nsString mDomPath; + nsString mRealPath; + }; + FallibleTArray mTargetPathArray; + + // This is the real File sequence that we expose via Promises. + Sequence> mFiles; + + // Error code to propagate. + nsresult mErrorResult; + + nsTArray> mPromises; +}; + class HTMLInputElementState final : public nsISupports { public: @@ -1052,6 +1367,15 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement, tmp->mInputData.mState->Traverse(cb); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories) + + if (tmp->mGetFilesRecursiveHelper) { + tmp->mGetFilesRecursiveHelper->Traverse(cb); + } + + if (tmp->mGetFilesNonRecursiveHelper) { + tmp->mGetFilesNonRecursiveHelper->Traverse(cb); + } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -1064,6 +1388,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement, if (tmp->IsSingleLineTextControl(false)) { tmp->mInputData.mState->Unlink(); } + + tmp->ClearGetFilesHelpers(); + //XXX should unlink more? NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -1117,6 +1444,7 @@ HTMLInputElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) co // we can just grab the pretty string and use it as wallpaper GetDisplayFileName(it->mStaticDocFileList); } else { + it->ClearGetFilesHelpers(); it->mFilesOrDirectories.Clear(); it->mFilesOrDirectories.AppendElements(mFilesOrDirectories); } @@ -2562,6 +2890,8 @@ void HTMLInputElement::SetFilesOrDirectories(const nsTArray& aFilesOrDirectories, bool aSetValueChanged) { + ClearGetFilesHelpers(); + mFilesOrDirectories.Clear(); mFilesOrDirectories.AppendElements(aFilesOrDirectories); @@ -2574,6 +2904,7 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles, { RefPtr files = static_cast(aFiles); mFilesOrDirectories.Clear(); + ClearGetFilesHelpers(); if (aFiles) { uint32_t listLength; @@ -5081,6 +5412,58 @@ HTMLInputElement::GetFilesAndDirectories(ErrorResult& aRv) return p.forget(); } +already_AddRefed +HTMLInputElement::GetFiles(bool aRecursiveFlag, ErrorResult& aRv) +{ + if (mType != NS_FORM_INPUT_FILE) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + + nsCOMPtr global = OwnerDoc()->GetScopeObject(); + MOZ_ASSERT(global); + if (!global) { + return nullptr; + } + + RefPtr helper; + if (aRecursiveFlag) { + if (!mGetFilesRecursiveHelper) { + mGetFilesRecursiveHelper = + GetFilesHelper::Create(global, + GetFilesOrDirectoriesInternal(), + aRecursiveFlag, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + } + + helper = mGetFilesRecursiveHelper; + } else { + if (!mGetFilesNonRecursiveHelper) { + mGetFilesNonRecursiveHelper = + GetFilesHelper::Create(global, + GetFilesOrDirectoriesInternal(), + aRecursiveFlag, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + } + + helper = mGetFilesNonRecursiveHelper; + } + + MOZ_ASSERT(helper); + + RefPtr p = Promise::Create(global, aRv); + if (aRv.Failed()) { + return nullptr; + } + + helper->AddPromise(p); + return p.forget(); +} + // Controllers Methods @@ -7578,6 +7961,20 @@ HTMLInputElement::WrapNode(JSContext* aCx, JS::Handle aGivenProto) return HTMLInputElementBinding::Wrap(aCx, this, aGivenProto); } +void +HTMLInputElement::ClearGetFilesHelpers() +{ + if (mGetFilesRecursiveHelper) { + mGetFilesRecursiveHelper->Unlink(); + mGetFilesRecursiveHelper = nullptr; + } + + if (mGetFilesNonRecursiveHelper) { + mGetFilesNonRecursiveHelper->Unlink(); + mGetFilesNonRecursiveHelper = nullptr; + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index d138cea6a239..a37d4c678413 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -40,6 +40,7 @@ namespace dom { class Date; class File; class FileList; +class GetFilesHelper; /** * A class we use to create a singleton object that is used to keep track of @@ -703,6 +704,8 @@ public: already_AddRefed GetFilesAndDirectories(ErrorResult& aRv); + already_AddRefed GetFiles(bool aRecursiveFlag, ErrorResult& aRv); + void ChooseDirectory(ErrorResult& aRv); // XPCOM GetAlign() is OK @@ -1253,6 +1256,8 @@ protected: */ bool IsPopupBlocked() const; + void ClearGetFilesHelpers(); + nsCOMPtr mControllers; /* @@ -1286,6 +1291,9 @@ protected: */ nsTArray mFilesOrDirectories; + RefPtr mGetFilesRecursiveHelper; + RefPtr mGetFilesNonRecursiveHelper; + #ifndef MOZ_CHILD_PERMISSIONS /** * Hack for bug 1086684: Stash the .value when we're a file picker. diff --git a/dom/webidl/HTMLInputElement.webidl b/dom/webidl/HTMLInputElement.webidl index 6712b7c5d4f1..5c874353f826 100644 --- a/dom/webidl/HTMLInputElement.webidl +++ b/dom/webidl/HTMLInputElement.webidl @@ -204,6 +204,9 @@ partial interface HTMLInputElement { [Throws, Pref="dom.input.dirpicker"] Promise> getFilesAndDirectories(); + [Throws, Pref="dom.input.dirpicker"] + Promise> getFiles(optional boolean recursiveFlag = false); + [Throws, Pref="dom.input.dirpicker"] void chooseDirectory(); }; From 2c98e6008594109585a6423ead23fcb9835043da Mon Sep 17 00:00:00 2001 From: Eddy Bruel Date: Tue, 31 May 2016 13:42:33 +0200 Subject: [PATCH 010/199] Bug 1271653 - Use EvalOptions for eval options instead of HandleObject;r=jimb --- js/src/vm/Debugger.cpp | 136 +++++++++++++++++++++++++++++------------ 1 file changed, 97 insertions(+), 39 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 06c94a858c00..a97e9ef89a89 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -406,6 +406,75 @@ NukeDebuggerWrapper(NativeObject *wrapper) wrapper->setPrivate(nullptr); } +class MOZ_RAII EvalOptions { + const char* filename_; + unsigned lineno_; + + public: + EvalOptions() : filename_(nullptr), lineno_(1) {} + ~EvalOptions(); + const char* filename() const { return filename_; } + unsigned lineno() const { return lineno_; } + bool setFilename(JSContext* cx, const char* filename); + void setLineno(unsigned lineno) { lineno_ = lineno; } +}; + +EvalOptions::~EvalOptions() +{ + js_free(const_cast(filename_)); +} + +bool +EvalOptions::setFilename(JSContext* cx, const char* filename) +{ + char* copy = nullptr; + if (filename) { + copy = JS_strdup(cx, filename); + if (!copy) + return false; + } + + // EvalOptions always owns filename_, so this cast is okay. + js_free(const_cast(filename_)); + + filename_ = copy; + return true; +} + +static bool +ParseEvalOptions(JSContext* cx, HandleValue value, EvalOptions& options) +{ + if (!value.isObject()) + return true; + + RootedObject opts(cx, &value.toObject()); + + RootedValue v(cx); + if (!JS_GetProperty(cx, opts, "url", &v)) + return false; + if (!v.isUndefined()) { + RootedString url_str(cx, ToString(cx, v)); + if (!url_str) + return false; + JSAutoByteString url_bytes(cx, url_str); + if (!url_bytes) + return false; + if (!options.setFilename(cx, url_bytes.ptr())) + return false; + } + + if (!JS_GetProperty(cx, opts, "lineNumber", &v)) + return false; + if (!v.isUndefined()) { + uint32_t lineno; + if (!ToUint32(cx, v, &lineno)) + return false; + options.setLineno(lineno); + } + + return true; +} + /*** Breakpoints *********************************************************************************/ @@ -7487,9 +7556,9 @@ enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false static bool DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code, - EvalBindings evalWithBindings, HandleValue bindings, HandleValue options, - MutableHandleValue vp, Debugger* dbg, HandleObject scope, - ScriptFrameIter* iter) + EvalBindings evalWithBindings, HandleValue bindings, + const EvalOptions& options, MutableHandleValue vp, Debugger* dbg, + HandleObject scope, ScriptFrameIter* iter) { /* Either we're specifying the frame, or a global. */ MOZ_ASSERT_IF(iter, !scope); @@ -7530,36 +7599,6 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code } } - /* Set options from object if provided. */ - JSAutoByteString url_bytes; - char* url = nullptr; - unsigned lineNumber = 1; - - if (options.isObject()) { - RootedObject opts(cx, &options.toObject()); - RootedValue v(cx); - - if (!JS_GetProperty(cx, opts, "url", &v)) - return false; - if (!v.isUndefined()) { - RootedString url_str(cx, ToString(cx, v)); - if (!url_str) - return false; - url = url_bytes.encodeLatin1(cx, url_str); - if (!url) - return false; - } - - if (!JS_GetProperty(cx, opts, "lineNumber", &v)) - return false; - if (!v.isUndefined()) { - uint32_t lineno; - if (!ToUint32(cx, v, &lineno)) - return false; - lineNumber = lineno; - } - } - Maybe ac; if (iter) ac.emplace(cx, iter->scopeChain(cx)); @@ -7612,8 +7651,9 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code return false; mozilla::Range chars = stableChars.twoByteRange(); - bool ok = EvaluateInEnv(cx, env, frame, pc, chars, url ? url : "debugger eval code", - lineNumber, &rval); + bool ok = EvaluateInEnv(cx, env, frame, pc, chars, + options.filename() ? options.filename() : "debugger eval code", + options.lineno(), &rval); return dbg->receiveCompletionValue(ac, ok, rval, vp); } @@ -7625,9 +7665,14 @@ DebuggerFrame_eval(JSContext* cx, unsigned argc, Value* vp) return false; Debugger* dbg = Debugger::fromChildJSObject(thisobj); UpdateFrameIterPc(iter); + + EvalOptions options; + if (!ParseEvalOptions(cx, args.get(1), options)) + return false; + return DebuggerGenericEval(cx, "Debugger.Frame.prototype.eval", args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue, - args.get(1), args.rval(), dbg, nullptr, &iter); + options, args.rval(), dbg, nullptr, &iter); } static bool @@ -7638,8 +7683,13 @@ DebuggerFrame_evalWithBindings(JSContext* cx, unsigned argc, Value* vp) return false; Debugger* dbg = Debugger::fromChildJSObject(thisobj); UpdateFrameIterPc(iter); + + EvalOptions options; + if (!ParseEvalOptions(cx, args.get(2), options)) + return false; + return DebuggerGenericEval(cx, "Debugger.Frame.prototype.evalWithBindings", - args[0], EvalHasExtraBindings, args[1], args.get(2), + args[0], EvalHasExtraBindings, args[1], options, args.rval(), dbg, nullptr, &iter); } @@ -8650,10 +8700,14 @@ DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) if (!RequireGlobalObject(cx, args.thisv(), referent)) return false; + EvalOptions options; + if (!ParseEvalOptions(cx, args.get(1), options)) + return false; + RootedObject globalLexical(cx, &referent->as().lexicalScope()); return DebuggerGenericEval(cx, "Debugger.Object.prototype.executeInGlobal", args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue, - args.get(1), args.rval(), dbg, globalLexical, nullptr); + options, args.rval(), dbg, globalLexical, nullptr); } static bool @@ -8666,9 +8720,13 @@ DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* if (!RequireGlobalObject(cx, args.thisv(), referent)) return false; + EvalOptions options; + if (!ParseEvalOptions(cx, args.get(2), options)) + return false; + RootedObject globalLexical(cx, &referent->as().lexicalScope()); return DebuggerGenericEval(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", - args[0], EvalHasExtraBindings, args[1], args.get(2), + args[0], EvalHasExtraBindings, args[1], options, args.rval(), dbg, globalLexical, nullptr); } From 78277d1b1dd920997362e08d06f4da5c4f05d280 Mon Sep 17 00:00:00 2001 From: Eddy Bruel Date: Tue, 31 May 2016 13:55:37 +0200 Subject: [PATCH 011/199] Bug 1271653 - Use mozilla::range for eval code instead of HandleValue;r=jimb --- js/src/vm/Debugger.cpp | 74 +++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index a97e9ef89a89..2f79d6313512 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -7552,10 +7552,27 @@ EvaluateInEnv(JSContext* cx, Handle env, AbstractFramePtr frame, return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address()); } +static bool +ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, + AutoStableStringChars& stableChars) +{ + if (!value.isString()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, + fnname, "string"); + return false; + } + RootedLinearString linear(cx, value.toString()->ensureLinear(cx)); + if (!linear) + return false; + if (!stableChars.initTwoByte(cx, linear)) + return false; + return true; +} + enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false }; static bool -DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code, +DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, EvalBindings evalWithBindings, HandleValue bindings, const EvalOptions& options, MutableHandleValue vp, Debugger* dbg, HandleObject scope, ScriptFrameIter* iter) @@ -7564,16 +7581,6 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code MOZ_ASSERT_IF(iter, !scope); MOZ_ASSERT_IF(!iter, scope && IsGlobalLexicalScope(scope)); - /* Check the first argument, the eval code string. */ - if (!code.isString()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, - fullMethodName, "string", InformalValueTypeName(code)); - return false; - } - RootedLinearString linear(cx, code.toString()->ensureLinear(cx)); - if (!linear) - return false; - /* * Gather keys and values of bindings, if any. This must be done in the * debugger compartment, since that is where any exceptions must be @@ -7646,11 +7653,7 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code RootedValue rval(cx); AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr(); jsbytecode* pc = iter ? iter->pc() : nullptr; - AutoStableStringChars stableChars(cx); - if (!stableChars.initTwoByte(cx, linear)) - return false; - mozilla::Range chars = stableChars.twoByteRange(); bool ok = EvaluateInEnv(cx, env, frame, pc, chars, options.filename() ? options.filename() : "debugger eval code", options.lineno(), &rval); @@ -7666,12 +7669,16 @@ DebuggerFrame_eval(JSContext* cx, unsigned argc, Value* vp) Debugger* dbg = Debugger::fromChildJSObject(thisobj); UpdateFrameIterPc(iter); + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Frame.prototype.eval", args[0], stableChars)) + return false; + mozilla::Range chars = stableChars.twoByteRange(); + EvalOptions options; if (!ParseEvalOptions(cx, args.get(1), options)) return false; - return DebuggerGenericEval(cx, "Debugger.Frame.prototype.eval", - args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue, + return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, options, args.rval(), dbg, nullptr, &iter); } @@ -7684,12 +7691,19 @@ DebuggerFrame_evalWithBindings(JSContext* cx, unsigned argc, Value* vp) Debugger* dbg = Debugger::fromChildJSObject(thisobj); UpdateFrameIterPc(iter); + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Frame.prototype.evalWithBindings", args[0], + stableChars)) + { + return false; + } + mozilla::Range chars = stableChars.twoByteRange(); + EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; - return DebuggerGenericEval(cx, "Debugger.Frame.prototype.evalWithBindings", - args[0], EvalHasExtraBindings, args[1], options, + return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, args.rval(), dbg, nullptr, &iter); } @@ -8700,13 +8714,20 @@ DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) if (!RequireGlobalObject(cx, args.thisv(), referent)) return false; + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal", args[0], + stableChars)) + { + return false; + } + mozilla::Range chars = stableChars.twoByteRange(); + EvalOptions options; if (!ParseEvalOptions(cx, args.get(1), options)) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, "Debugger.Object.prototype.executeInGlobal", - args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue, + return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, options, args.rval(), dbg, globalLexical, nullptr); } @@ -8720,13 +8741,20 @@ DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* if (!RequireGlobalObject(cx, args.thisv(), referent)) return false; + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0], + stableChars)) + { + return false; + } + mozilla::Range chars = stableChars.twoByteRange(); + EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", - args[0], EvalHasExtraBindings, args[1], options, + return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, args.rval(), dbg, globalLexical, nullptr); } From ffcb943b14f04102173d4acb059c2742a64e1e05 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Tue, 31 May 2016 14:38:04 +0300 Subject: [PATCH 012/199] Bug 1276888 - Optimize out some refcounting in TreeMatchContext::TreeMatchContext, r=baku --HG-- extra : rebase_source : 96f46729eb009a0b927986f9e9317c2925fa1448 --- layout/style/nsRuleProcessorData.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/layout/style/nsRuleProcessorData.h b/layout/style/nsRuleProcessorData.h index 3373cb9f1d2d..f6548ee33960 100644 --- a/layout/style/nsRuleProcessorData.h +++ b/layout/style/nsRuleProcessorData.h @@ -404,13 +404,9 @@ struct MOZ_STACK_CLASS TreeMatchContext { , mCurrentStyleScope(nullptr) { if (aMatchVisited != eNeverMatchVisited) { - nsCOMPtr container = mDocument->GetContainer(); - if (container) { - nsCOMPtr loadContext = do_QueryInterface(container); - NS_ASSERTION(loadContext, "Couldn't get loadContext from container; assuming no private browsing."); - if (loadContext) { - mUsingPrivateBrowsing = loadContext->UsePrivateBrowsing(); - } + nsILoadContext* loadContext = mDocument->GetLoadContext(); + if (loadContext) { + mUsingPrivateBrowsing = loadContext->UsePrivateBrowsing(); } } } From c734bbe8aa8b47018e48a08502eb55b6d6ec5244 Mon Sep 17 00:00:00 2001 From: Eddy Bruel Date: Tue, 31 May 2016 14:07:37 +0200 Subject: [PATCH 013/199] Bug 1271653 - Use HandleObject for eval bindings instead of HandleValue;r=jimb --- js/src/vm/Debugger.cpp | 73 +++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 2f79d6313512..9970c7aa4784 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -406,6 +406,23 @@ NukeDebuggerWrapper(NativeObject *wrapper) wrapper->setPrivate(nullptr); } +static bool +ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, + AutoStableStringChars& stableChars) +{ + if (!value.isString()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, + fnname, "string"); + return false; + } + RootedLinearString linear(cx, value.toString()->ensureLinear(cx)); + if (!linear) + return false; + if (!stableChars.initTwoByte(cx, linear)) + return false; + return true; +} + class MOZ_RAII EvalOptions { const char* filename_; unsigned lineno_; @@ -7552,30 +7569,10 @@ EvaluateInEnv(JSContext* cx, Handle env, AbstractFramePtr frame, return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address()); } -static bool -ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, - AutoStableStringChars& stableChars) -{ - if (!value.isString()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, - fnname, "string"); - return false; - } - RootedLinearString linear(cx, value.toString()->ensureLinear(cx)); - if (!linear) - return false; - if (!stableChars.initTwoByte(cx, linear)) - return false; - return true; -} - -enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false }; - static bool DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, - EvalBindings evalWithBindings, HandleValue bindings, - const EvalOptions& options, MutableHandleValue vp, Debugger* dbg, - HandleObject scope, ScriptFrameIter* iter) + HandleObject bindings, const EvalOptions& options, MutableHandleValue vp, + Debugger* dbg, HandleObject scope, ScriptFrameIter* iter) { /* Either we're specifying the frame, or a global. */ MOZ_ASSERT_IF(iter, !scope); @@ -7588,17 +7585,15 @@ DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, */ AutoIdVector keys(cx); AutoValueVector values(cx); - if (evalWithBindings) { - RootedObject bindingsobj(cx, NonNullObject(cx, bindings)); - if (!bindingsobj || - !GetPropertyKeys(cx, bindingsobj, JSITER_OWNONLY, &keys) || + if (bindings) { + if (!GetPropertyKeys(cx, bindings, JSITER_OWNONLY, &keys) || !values.growBy(keys.length())) { return false; } for (size_t i = 0; i < keys.length(); i++) { MutableHandleValue valp = values[i]; - if (!GetProperty(cx, bindingsobj, bindingsobj, keys[i], valp) || + if (!GetProperty(cx, bindings, bindings, keys[i], valp) || !dbg->unwrapDebuggeeValue(cx, valp)) { return false; @@ -7622,7 +7617,7 @@ DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, } /* If evalWithBindings, create the inner environment. */ - if (evalWithBindings) { + if (bindings) { RootedPlainObject nenv(cx, NewObjectWithGivenProto(cx, nullptr)); if (!nenv) return false; @@ -7678,8 +7673,7 @@ DebuggerFrame_eval(JSContext* cx, unsigned argc, Value* vp) if (!ParseEvalOptions(cx, args.get(1), options)) return false; - return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, - options, args.rval(), dbg, nullptr, &iter); + return DebuggerGenericEval(cx, chars, nullptr, options, args.rval(), dbg, nullptr, &iter); } static bool @@ -7699,12 +7693,15 @@ DebuggerFrame_evalWithBindings(JSContext* cx, unsigned argc, Value* vp) } mozilla::Range chars = stableChars.twoByteRange(); + RootedObject bindings(cx, NonNullObject(cx, args[1])); + if (!bindings) + return false; + EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; - return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, - args.rval(), dbg, nullptr, &iter); + return DebuggerGenericEval(cx, chars, bindings, options, args.rval(), dbg, nullptr, &iter); } static bool @@ -8727,8 +8724,8 @@ DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, - options, args.rval(), dbg, globalLexical, nullptr); + return DebuggerGenericEval(cx, chars, nullptr, options, args.rval(), dbg, globalLexical, + nullptr); } static bool @@ -8749,13 +8746,17 @@ DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* } mozilla::Range chars = stableChars.twoByteRange(); + RootedObject bindings(cx, NonNullObject(cx, args[1])); + if (!bindings) + return false; + EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, - args.rval(), dbg, globalLexical, nullptr); + return DebuggerGenericEval(cx, chars, bindings, options, args.rval(), dbg, globalLexical, + nullptr); } static bool From 9bfaa764102eb9c5b662b180faf3cdba16e69b89 Mon Sep 17 00:00:00 2001 From: Eddy Bruel Date: Tue, 31 May 2016 14:14:31 +0200 Subject: [PATCH 014/199] Bug 1271653 - Implement a C++ interface for executeInGlobal(WithBindings);r=jimb --- js/src/vm/Debugger.cpp | 209 ++++++++++++++++++++--------------------- js/src/vm/Debugger.h | 16 ++++ 2 files changed, 118 insertions(+), 107 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 9970c7aa4784..367665f90e9c 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -423,19 +423,6 @@ ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, return true; } -class MOZ_RAII EvalOptions { - const char* filename_; - unsigned lineno_; - - public: - EvalOptions() : filename_(nullptr), lineno_(1) {} - ~EvalOptions(); - const char* filename() const { return filename_; } - unsigned lineno() const { return lineno_; } - bool setFilename(JSContext* cx, const char* filename); - void setLineno(unsigned lineno) { lineno_ = lineno; } -}; - EvalOptions::~EvalOptions() { js_free(const_cast(filename_)); @@ -492,6 +479,42 @@ ParseEvalOptions(JSContext* cx, HandleValue value, EvalOptions& options) return true; } +static bool +RequireGlobalObject(JSContext* cx, HandleValue dbgobj, HandleObject referent) +{ + RootedObject obj(cx, referent); + + if (!obj->is()) { + const char* isWrapper = ""; + const char* isWindowProxy = ""; + + /* Help the poor programmer by pointing out wrappers around globals... */ + if (obj->is()) { + obj = js::UncheckedUnwrap(obj); + isWrapper = "a wrapper around "; + } + + /* ... and WindowProxies around Windows. */ + if (IsWindowProxy(obj)) { + obj = ToWindowIfWindowProxy(obj); + isWindowProxy = "a WindowProxy referring to "; + } + + if (obj->is()) { + ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_WRAPPER_IN_WAY, + JSDVG_SEARCH_STACK, dbgobj, nullptr, + isWrapper, isWindowProxy); + } else { + ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT, + JSDVG_SEARCH_STACK, dbgobj, nullptr, + "a global object", nullptr); + } + return false; + } + + return true; +} + /*** Breakpoints *********************************************************************************/ @@ -8594,6 +8617,54 @@ DebuggerObject_apply(JSContext* cx, unsigned argc, Value* vp) return object->call(cx, object, thisv, args, callArgs.rval()); } +static bool +DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) +{ + THIS_DEBUGOBJECT(cx, argc, vp, "executeInGlobal", args, object); + if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobal", 1)) + return false; + + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal", args[0], + stableChars)) + { + return false; + } + mozilla::Range chars = stableChars.twoByteRange(); + + EvalOptions options; + if (!ParseEvalOptions(cx, args.get(1), options)) + return false; + + return DebuggerObject::executeInGlobal(cx, object, chars, nullptr, options, args.rval()); +} + +static bool +DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* vp) +{ + THIS_DEBUGOBJECT(cx, argc, vp, "executeInGlobalWithBindings", args, object); + if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", 2)) + return false; + + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0], + stableChars)) + { + return false; + } + mozilla::Range chars = stableChars.twoByteRange(); + + RootedObject bindings(cx, NonNullObject(cx, args[1])); + if (!bindings) + return false; + + EvalOptions options; + if (!ParseEvalOptions(cx, args.get(2), options)) + return false; + + return DebuggerObject::executeInGlobal(cx, object, chars, bindings, options, args.rval()); +} + static bool DebuggerObject_makeDebuggeeValue(JSContext* cx, unsigned argc, Value* vp) { @@ -8623,42 +8694,6 @@ DebuggerObject_makeDebuggeeValue(JSContext* cx, unsigned argc, Value* vp) return true; } -static bool -RequireGlobalObject(JSContext* cx, HandleValue dbgobj, HandleObject referent) -{ - RootedObject obj(cx, referent); - - if (!obj->is()) { - const char* isWrapper = ""; - const char* isWindowProxy = ""; - - /* Help the poor programmer by pointing out wrappers around globals... */ - if (obj->is()) { - obj = js::UncheckedUnwrap(obj); - isWrapper = "a wrapper around "; - } - - /* ... and WindowProxies around Windows. */ - if (IsWindowProxy(obj)) { - obj = ToWindowIfWindowProxy(obj); - isWindowProxy = "a WindowProxy referring to "; - } - - if (obj->is()) { - ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_WRAPPER_IN_WAY, - JSDVG_SEARCH_STACK, dbgobj, nullptr, - isWrapper, isWindowProxy); - } else { - ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT, - JSDVG_SEARCH_STACK, dbgobj, nullptr, - "a global object", nullptr); - } - return false; - } - - return true; -} - // Lookup a binding on the referent's global scope and change it to undefined // if it is an uninitialized lexical, otherwise do nothing. The method's return // value is true _only_ when an uninitialized lexical has been altered, otherwise @@ -8702,63 +8737,6 @@ DebuggerObject_forceLexicalInitializationByName(JSContext *cx, unsigned argc, Va return true; } -static bool -DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) -{ - THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "executeInGlobal", args, dbg, referent); - if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobal", 1)) - return false; - if (!RequireGlobalObject(cx, args.thisv(), referent)) - return false; - - AutoStableStringChars stableChars(cx); - if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal", args[0], - stableChars)) - { - return false; - } - mozilla::Range chars = stableChars.twoByteRange(); - - EvalOptions options; - if (!ParseEvalOptions(cx, args.get(1), options)) - return false; - - RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, chars, nullptr, options, args.rval(), dbg, globalLexical, - nullptr); -} - -static bool -DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* vp) -{ - THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "executeInGlobalWithBindings", args, dbg, - referent); - if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", 2)) - return false; - if (!RequireGlobalObject(cx, args.thisv(), referent)) - return false; - - AutoStableStringChars stableChars(cx); - if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0], - stableChars)) - { - return false; - } - mozilla::Range chars = stableChars.twoByteRange(); - - RootedObject bindings(cx, NonNullObject(cx, args[1])); - if (!bindings) - return false; - - EvalOptions options; - if (!ParseEvalOptions(cx, args.get(2), options)) - return false; - - RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, chars, bindings, options, args.rval(), dbg, globalLexical, - nullptr); -} - static bool DebuggerObject_asEnvironment(JSContext* cx, unsigned argc, Value* vp) { @@ -8867,9 +8845,9 @@ const JSFunctionSpec DebuggerObject::methods_[] = { JS_FN("deleteProperty", DebuggerObject_deleteProperty, 1, 0), JS_FN("call", DebuggerObject_call, 0, 0), JS_FN("apply", DebuggerObject_apply, 0, 0), - JS_FN("makeDebuggeeValue", DebuggerObject_makeDebuggeeValue, 1, 0), JS_FN("executeInGlobal", DebuggerObject_executeInGlobal, 1, 0), JS_FN("executeInGlobalWithBindings", DebuggerObject_executeInGlobalWithBindings, 2, 0), + JS_FN("makeDebuggeeValue", DebuggerObject_makeDebuggeeValue, 1, 0), JS_FN("asEnvironment", DebuggerObject_asEnvironment, 0, 0), JS_FN("unwrap", DebuggerObject_unwrap, 0, 0), JS_FN("unsafeDereference", DebuggerObject_unsafeDereference, 0, 0), @@ -9202,6 +9180,23 @@ DebuggerObject::call(JSContext* cx, Handle object, HandleValue return dbg->receiveCompletionValue(ac, ok, result, result); } +/* static */ bool +DebuggerObject::executeInGlobal(JSContext* cx, Handle object, + mozilla::Range chars, HandleObject bindings, + const EvalOptions& options, MutableHandleValue result) +{ + RootedObject referent(cx, object->referent()); + Debugger* dbg = object->owner(); + + RootedValue dbgobj(cx, ObjectValue(*object)); + if (!RequireGlobalObject(cx, dbgobj, referent)) + return false; + RootedObject globalLexical(cx, &referent->as().lexicalScope()); + + return DebuggerGenericEval(cx, chars, bindings, options, result, dbg, globalLexical, + nullptr); +} + /*** Debugger.Environment ************************************************************************/ diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index f7d6cc954d33..197b9717f422 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -209,6 +209,19 @@ class AutoSuppressDebuggeeNoExecuteChecks } }; +class MOZ_RAII EvalOptions { + const char* filename_; + unsigned lineno_; + + public: + EvalOptions() : filename_(nullptr), lineno_(1) {} + ~EvalOptions(); + const char* filename() const { return filename_; } + unsigned lineno() const { return lineno_; } + bool setFilename(JSContext* cx, const char* filename); + void setLineno(unsigned lineno) { lineno_ = lineno; } +}; + /* * Env is the type of what ES5 calls "lexical environments" (runtime activations * of lexical scopes). This is currently just JSObject, and is implemented by @@ -1058,6 +1071,9 @@ class DebuggerObject : public NativeObject ObjectOpResult& result); static bool call(JSContext* cx, Handle object, HandleValue thisv, Handle args, MutableHandleValue result); + static bool executeInGlobal(JSContext* cx, Handle object, + mozilla::Range chars, HandleObject bindings, + const EvalOptions& options, MutableHandleValue result); private: enum { From a63c7e4ee31d5edc2c2d830e98c5c787f406eeac Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 31 May 2016 15:34:49 +0200 Subject: [PATCH 015/199] Bug 1276904 - container-icon visibility should follow the selectedBrowser, r=gijs --- browser/base/content/browser.js | 4 ++-- browser/base/content/tabbrowser.xml | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index ca7a26eb4120..6a09dce6191f 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -4069,11 +4069,11 @@ function updateUserContextUIVisibility() /** * Updates the User Context UI indicators if the browser is in a non-default context */ -function updateUserContextUIIndicator(browser) +function updateUserContextUIIndicator() { let hbox = document.getElementById("userContext-icons"); - let userContextId = browser.getAttribute("usercontextid"); + let userContextId = gBrowser.selectedBrowser.getAttribute("usercontextid"); if (!userContextId) { hbox.hidden = true; return; diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index cefb52213ad1..9ea754e1be73 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1217,7 +1217,7 @@ this._adjustFocusAfterTabSwitch(this.mCurrentTab); } - updateUserContextUIIndicator(gBrowser.selectedBrowser); + updateUserContextUIIndicator(); this.tabContainer._setPositionalAttributes(); @@ -4328,7 +4328,12 @@ tab.setUserContextId(data.userContextId); } - updateUserContextUIIndicator(browser); + // We don't want to update the container icon and identifier if + // this is not the selected browser. + if (browser == gBrowser.selectedBrowser) { + updateUserContextUIIndicator(); + } + break; } case "Findbar:Keypress": { From 37788537c1f387756f699549c8bb24f2296da61d Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Tue, 31 May 2016 15:36:55 +0200 Subject: [PATCH 016/199] Backed out changeset 11c4daf94661 (bug 1271653) --HG-- extra : rebase_source : 513f3ab45f5000b7aa6053260632064e80ef3f41 --- js/src/vm/Debugger.cpp | 209 +++++++++++++++++++++-------------------- js/src/vm/Debugger.h | 16 ---- 2 files changed, 107 insertions(+), 118 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 367665f90e9c..9970c7aa4784 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -423,6 +423,19 @@ ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, return true; } +class MOZ_RAII EvalOptions { + const char* filename_; + unsigned lineno_; + + public: + EvalOptions() : filename_(nullptr), lineno_(1) {} + ~EvalOptions(); + const char* filename() const { return filename_; } + unsigned lineno() const { return lineno_; } + bool setFilename(JSContext* cx, const char* filename); + void setLineno(unsigned lineno) { lineno_ = lineno; } +}; + EvalOptions::~EvalOptions() { js_free(const_cast(filename_)); @@ -479,42 +492,6 @@ ParseEvalOptions(JSContext* cx, HandleValue value, EvalOptions& options) return true; } -static bool -RequireGlobalObject(JSContext* cx, HandleValue dbgobj, HandleObject referent) -{ - RootedObject obj(cx, referent); - - if (!obj->is()) { - const char* isWrapper = ""; - const char* isWindowProxy = ""; - - /* Help the poor programmer by pointing out wrappers around globals... */ - if (obj->is()) { - obj = js::UncheckedUnwrap(obj); - isWrapper = "a wrapper around "; - } - - /* ... and WindowProxies around Windows. */ - if (IsWindowProxy(obj)) { - obj = ToWindowIfWindowProxy(obj); - isWindowProxy = "a WindowProxy referring to "; - } - - if (obj->is()) { - ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_WRAPPER_IN_WAY, - JSDVG_SEARCH_STACK, dbgobj, nullptr, - isWrapper, isWindowProxy); - } else { - ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT, - JSDVG_SEARCH_STACK, dbgobj, nullptr, - "a global object", nullptr); - } - return false; - } - - return true; -} - /*** Breakpoints *********************************************************************************/ @@ -8617,54 +8594,6 @@ DebuggerObject_apply(JSContext* cx, unsigned argc, Value* vp) return object->call(cx, object, thisv, args, callArgs.rval()); } -static bool -DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) -{ - THIS_DEBUGOBJECT(cx, argc, vp, "executeInGlobal", args, object); - if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobal", 1)) - return false; - - AutoStableStringChars stableChars(cx); - if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal", args[0], - stableChars)) - { - return false; - } - mozilla::Range chars = stableChars.twoByteRange(); - - EvalOptions options; - if (!ParseEvalOptions(cx, args.get(1), options)) - return false; - - return DebuggerObject::executeInGlobal(cx, object, chars, nullptr, options, args.rval()); -} - -static bool -DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* vp) -{ - THIS_DEBUGOBJECT(cx, argc, vp, "executeInGlobalWithBindings", args, object); - if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", 2)) - return false; - - AutoStableStringChars stableChars(cx); - if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0], - stableChars)) - { - return false; - } - mozilla::Range chars = stableChars.twoByteRange(); - - RootedObject bindings(cx, NonNullObject(cx, args[1])); - if (!bindings) - return false; - - EvalOptions options; - if (!ParseEvalOptions(cx, args.get(2), options)) - return false; - - return DebuggerObject::executeInGlobal(cx, object, chars, bindings, options, args.rval()); -} - static bool DebuggerObject_makeDebuggeeValue(JSContext* cx, unsigned argc, Value* vp) { @@ -8694,6 +8623,42 @@ DebuggerObject_makeDebuggeeValue(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +RequireGlobalObject(JSContext* cx, HandleValue dbgobj, HandleObject referent) +{ + RootedObject obj(cx, referent); + + if (!obj->is()) { + const char* isWrapper = ""; + const char* isWindowProxy = ""; + + /* Help the poor programmer by pointing out wrappers around globals... */ + if (obj->is()) { + obj = js::UncheckedUnwrap(obj); + isWrapper = "a wrapper around "; + } + + /* ... and WindowProxies around Windows. */ + if (IsWindowProxy(obj)) { + obj = ToWindowIfWindowProxy(obj); + isWindowProxy = "a WindowProxy referring to "; + } + + if (obj->is()) { + ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_WRAPPER_IN_WAY, + JSDVG_SEARCH_STACK, dbgobj, nullptr, + isWrapper, isWindowProxy); + } else { + ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT, + JSDVG_SEARCH_STACK, dbgobj, nullptr, + "a global object", nullptr); + } + return false; + } + + return true; +} + // Lookup a binding on the referent's global scope and change it to undefined // if it is an uninitialized lexical, otherwise do nothing. The method's return // value is true _only_ when an uninitialized lexical has been altered, otherwise @@ -8737,6 +8702,63 @@ DebuggerObject_forceLexicalInitializationByName(JSContext *cx, unsigned argc, Va return true; } +static bool +DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) +{ + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "executeInGlobal", args, dbg, referent); + if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobal", 1)) + return false; + if (!RequireGlobalObject(cx, args.thisv(), referent)) + return false; + + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal", args[0], + stableChars)) + { + return false; + } + mozilla::Range chars = stableChars.twoByteRange(); + + EvalOptions options; + if (!ParseEvalOptions(cx, args.get(1), options)) + return false; + + RootedObject globalLexical(cx, &referent->as().lexicalScope()); + return DebuggerGenericEval(cx, chars, nullptr, options, args.rval(), dbg, globalLexical, + nullptr); +} + +static bool +DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* vp) +{ + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "executeInGlobalWithBindings", args, dbg, + referent); + if (!args.requireAtLeast(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", 2)) + return false; + if (!RequireGlobalObject(cx, args.thisv(), referent)) + return false; + + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0], + stableChars)) + { + return false; + } + mozilla::Range chars = stableChars.twoByteRange(); + + RootedObject bindings(cx, NonNullObject(cx, args[1])); + if (!bindings) + return false; + + EvalOptions options; + if (!ParseEvalOptions(cx, args.get(2), options)) + return false; + + RootedObject globalLexical(cx, &referent->as().lexicalScope()); + return DebuggerGenericEval(cx, chars, bindings, options, args.rval(), dbg, globalLexical, + nullptr); +} + static bool DebuggerObject_asEnvironment(JSContext* cx, unsigned argc, Value* vp) { @@ -8845,9 +8867,9 @@ const JSFunctionSpec DebuggerObject::methods_[] = { JS_FN("deleteProperty", DebuggerObject_deleteProperty, 1, 0), JS_FN("call", DebuggerObject_call, 0, 0), JS_FN("apply", DebuggerObject_apply, 0, 0), + JS_FN("makeDebuggeeValue", DebuggerObject_makeDebuggeeValue, 1, 0), JS_FN("executeInGlobal", DebuggerObject_executeInGlobal, 1, 0), JS_FN("executeInGlobalWithBindings", DebuggerObject_executeInGlobalWithBindings, 2, 0), - JS_FN("makeDebuggeeValue", DebuggerObject_makeDebuggeeValue, 1, 0), JS_FN("asEnvironment", DebuggerObject_asEnvironment, 0, 0), JS_FN("unwrap", DebuggerObject_unwrap, 0, 0), JS_FN("unsafeDereference", DebuggerObject_unsafeDereference, 0, 0), @@ -9180,23 +9202,6 @@ DebuggerObject::call(JSContext* cx, Handle object, HandleValue return dbg->receiveCompletionValue(ac, ok, result, result); } -/* static */ bool -DebuggerObject::executeInGlobal(JSContext* cx, Handle object, - mozilla::Range chars, HandleObject bindings, - const EvalOptions& options, MutableHandleValue result) -{ - RootedObject referent(cx, object->referent()); - Debugger* dbg = object->owner(); - - RootedValue dbgobj(cx, ObjectValue(*object)); - if (!RequireGlobalObject(cx, dbgobj, referent)) - return false; - RootedObject globalLexical(cx, &referent->as().lexicalScope()); - - return DebuggerGenericEval(cx, chars, bindings, options, result, dbg, globalLexical, - nullptr); -} - /*** Debugger.Environment ************************************************************************/ diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 197b9717f422..f7d6cc954d33 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -209,19 +209,6 @@ class AutoSuppressDebuggeeNoExecuteChecks } }; -class MOZ_RAII EvalOptions { - const char* filename_; - unsigned lineno_; - - public: - EvalOptions() : filename_(nullptr), lineno_(1) {} - ~EvalOptions(); - const char* filename() const { return filename_; } - unsigned lineno() const { return lineno_; } - bool setFilename(JSContext* cx, const char* filename); - void setLineno(unsigned lineno) { lineno_ = lineno; } -}; - /* * Env is the type of what ES5 calls "lexical environments" (runtime activations * of lexical scopes). This is currently just JSObject, and is implemented by @@ -1071,9 +1058,6 @@ class DebuggerObject : public NativeObject ObjectOpResult& result); static bool call(JSContext* cx, Handle object, HandleValue thisv, Handle args, MutableHandleValue result); - static bool executeInGlobal(JSContext* cx, Handle object, - mozilla::Range chars, HandleObject bindings, - const EvalOptions& options, MutableHandleValue result); private: enum { From 362c0ffafaf89b1efc4feaa94760f3c567936f97 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Tue, 31 May 2016 15:36:56 +0200 Subject: [PATCH 017/199] Backed out changeset b11a57f5b70b (bug 1271653) --HG-- extra : rebase_source : 8a453cd995fe688de9860f32431361c7b74f7f68 --- js/src/vm/Debugger.cpp | 73 +++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 9970c7aa4784..2f79d6313512 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -406,23 +406,6 @@ NukeDebuggerWrapper(NativeObject *wrapper) wrapper->setPrivate(nullptr); } -static bool -ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, - AutoStableStringChars& stableChars) -{ - if (!value.isString()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, - fnname, "string"); - return false; - } - RootedLinearString linear(cx, value.toString()->ensureLinear(cx)); - if (!linear) - return false; - if (!stableChars.initTwoByte(cx, linear)) - return false; - return true; -} - class MOZ_RAII EvalOptions { const char* filename_; unsigned lineno_; @@ -7569,10 +7552,30 @@ EvaluateInEnv(JSContext* cx, Handle env, AbstractFramePtr frame, return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address()); } +static bool +ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, + AutoStableStringChars& stableChars) +{ + if (!value.isString()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, + fnname, "string"); + return false; + } + RootedLinearString linear(cx, value.toString()->ensureLinear(cx)); + if (!linear) + return false; + if (!stableChars.initTwoByte(cx, linear)) + return false; + return true; +} + +enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false }; + static bool DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, - HandleObject bindings, const EvalOptions& options, MutableHandleValue vp, - Debugger* dbg, HandleObject scope, ScriptFrameIter* iter) + EvalBindings evalWithBindings, HandleValue bindings, + const EvalOptions& options, MutableHandleValue vp, Debugger* dbg, + HandleObject scope, ScriptFrameIter* iter) { /* Either we're specifying the frame, or a global. */ MOZ_ASSERT_IF(iter, !scope); @@ -7585,15 +7588,17 @@ DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, */ AutoIdVector keys(cx); AutoValueVector values(cx); - if (bindings) { - if (!GetPropertyKeys(cx, bindings, JSITER_OWNONLY, &keys) || + if (evalWithBindings) { + RootedObject bindingsobj(cx, NonNullObject(cx, bindings)); + if (!bindingsobj || + !GetPropertyKeys(cx, bindingsobj, JSITER_OWNONLY, &keys) || !values.growBy(keys.length())) { return false; } for (size_t i = 0; i < keys.length(); i++) { MutableHandleValue valp = values[i]; - if (!GetProperty(cx, bindings, bindings, keys[i], valp) || + if (!GetProperty(cx, bindingsobj, bindingsobj, keys[i], valp) || !dbg->unwrapDebuggeeValue(cx, valp)) { return false; @@ -7617,7 +7622,7 @@ DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, } /* If evalWithBindings, create the inner environment. */ - if (bindings) { + if (evalWithBindings) { RootedPlainObject nenv(cx, NewObjectWithGivenProto(cx, nullptr)); if (!nenv) return false; @@ -7673,7 +7678,8 @@ DebuggerFrame_eval(JSContext* cx, unsigned argc, Value* vp) if (!ParseEvalOptions(cx, args.get(1), options)) return false; - return DebuggerGenericEval(cx, chars, nullptr, options, args.rval(), dbg, nullptr, &iter); + return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, + options, args.rval(), dbg, nullptr, &iter); } static bool @@ -7693,15 +7699,12 @@ DebuggerFrame_evalWithBindings(JSContext* cx, unsigned argc, Value* vp) } mozilla::Range chars = stableChars.twoByteRange(); - RootedObject bindings(cx, NonNullObject(cx, args[1])); - if (!bindings) - return false; - EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; - return DebuggerGenericEval(cx, chars, bindings, options, args.rval(), dbg, nullptr, &iter); + return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, + args.rval(), dbg, nullptr, &iter); } static bool @@ -8724,8 +8727,8 @@ DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, chars, nullptr, options, args.rval(), dbg, globalLexical, - nullptr); + return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, + options, args.rval(), dbg, globalLexical, nullptr); } static bool @@ -8746,17 +8749,13 @@ DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* } mozilla::Range chars = stableChars.twoByteRange(); - RootedObject bindings(cx, NonNullObject(cx, args[1])); - if (!bindings) - return false; - EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, chars, bindings, options, args.rval(), dbg, globalLexical, - nullptr); + return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, + args.rval(), dbg, globalLexical, nullptr); } static bool From 90995928a37c02cebb8765aa2dcfa36bc7a5df80 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Tue, 31 May 2016 15:37:10 +0200 Subject: [PATCH 018/199] Backed out changeset 98a6ae087add (bug 1271653) for spidermonkey test failures --HG-- extra : rebase_source : fe73e023d41ac353be582b3c3522ded474539296 --- js/src/vm/Debugger.cpp | 74 +++++++++++++----------------------------- 1 file changed, 23 insertions(+), 51 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 2f79d6313512..a97e9ef89a89 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -7552,27 +7552,10 @@ EvaluateInEnv(JSContext* cx, Handle env, AbstractFramePtr frame, return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address()); } -static bool -ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, - AutoStableStringChars& stableChars) -{ - if (!value.isString()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, - fnname, "string"); - return false; - } - RootedLinearString linear(cx, value.toString()->ensureLinear(cx)); - if (!linear) - return false; - if (!stableChars.initTwoByte(cx, linear)) - return false; - return true; -} - enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false }; static bool -DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, +DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code, EvalBindings evalWithBindings, HandleValue bindings, const EvalOptions& options, MutableHandleValue vp, Debugger* dbg, HandleObject scope, ScriptFrameIter* iter) @@ -7581,6 +7564,16 @@ DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, MOZ_ASSERT_IF(iter, !scope); MOZ_ASSERT_IF(!iter, scope && IsGlobalLexicalScope(scope)); + /* Check the first argument, the eval code string. */ + if (!code.isString()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, + fullMethodName, "string", InformalValueTypeName(code)); + return false; + } + RootedLinearString linear(cx, code.toString()->ensureLinear(cx)); + if (!linear) + return false; + /* * Gather keys and values of bindings, if any. This must be done in the * debugger compartment, since that is where any exceptions must be @@ -7653,7 +7646,11 @@ DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, RootedValue rval(cx); AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr(); jsbytecode* pc = iter ? iter->pc() : nullptr; + AutoStableStringChars stableChars(cx); + if (!stableChars.initTwoByte(cx, linear)) + return false; + mozilla::Range chars = stableChars.twoByteRange(); bool ok = EvaluateInEnv(cx, env, frame, pc, chars, options.filename() ? options.filename() : "debugger eval code", options.lineno(), &rval); @@ -7669,16 +7666,12 @@ DebuggerFrame_eval(JSContext* cx, unsigned argc, Value* vp) Debugger* dbg = Debugger::fromChildJSObject(thisobj); UpdateFrameIterPc(iter); - AutoStableStringChars stableChars(cx); - if (!ValueToStableChars(cx, "Debugger.Frame.prototype.eval", args[0], stableChars)) - return false; - mozilla::Range chars = stableChars.twoByteRange(); - EvalOptions options; if (!ParseEvalOptions(cx, args.get(1), options)) return false; - return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, + return DebuggerGenericEval(cx, "Debugger.Frame.prototype.eval", + args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue, options, args.rval(), dbg, nullptr, &iter); } @@ -7691,19 +7684,12 @@ DebuggerFrame_evalWithBindings(JSContext* cx, unsigned argc, Value* vp) Debugger* dbg = Debugger::fromChildJSObject(thisobj); UpdateFrameIterPc(iter); - AutoStableStringChars stableChars(cx); - if (!ValueToStableChars(cx, "Debugger.Frame.prototype.evalWithBindings", args[0], - stableChars)) - { - return false; - } - mozilla::Range chars = stableChars.twoByteRange(); - EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; - return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, + return DebuggerGenericEval(cx, "Debugger.Frame.prototype.evalWithBindings", + args[0], EvalHasExtraBindings, args[1], options, args.rval(), dbg, nullptr, &iter); } @@ -8714,20 +8700,13 @@ DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) if (!RequireGlobalObject(cx, args.thisv(), referent)) return false; - AutoStableStringChars stableChars(cx); - if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal", args[0], - stableChars)) - { - return false; - } - mozilla::Range chars = stableChars.twoByteRange(); - EvalOptions options; if (!ParseEvalOptions(cx, args.get(1), options)) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, + return DebuggerGenericEval(cx, "Debugger.Object.prototype.executeInGlobal", + args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue, options, args.rval(), dbg, globalLexical, nullptr); } @@ -8741,20 +8720,13 @@ DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* if (!RequireGlobalObject(cx, args.thisv(), referent)) return false; - AutoStableStringChars stableChars(cx); - if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0], - stableChars)) - { - return false; - } - mozilla::Range chars = stableChars.twoByteRange(); - EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, + return DebuggerGenericEval(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", + args[0], EvalHasExtraBindings, args[1], options, args.rval(), dbg, globalLexical, nullptr); } From 939d620e96d45869677486a1617b263e10d4fc90 Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Fri, 27 May 2016 14:06:02 +0100 Subject: [PATCH 019/199] Bug 1276117 - part 1: fix URL bar state when loading about:home after about:preferences, r=mikedeboer MozReview-Commit-ID: D5ecLsiJF3R --HG-- extra : rebase_source : e2182ab760cb052fb9e6996a3c9e948c6d67fd66 --- browser/base/content/test/urlbar/browser.ini | 1 + .../urlbar/browser_urlbarAboutHomeLoading.js | 43 +++++++++++++++++++ .../components/sessionstore/SessionStore.jsm | 8 +++- .../content/content-sessionStore.js | 4 +- 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js diff --git a/browser/base/content/test/urlbar/browser.ini b/browser/base/content/test/urlbar/browser.ini index 32b65e05b0bb..5bc8b8076519 100644 --- a/browser/base/content/test/urlbar/browser.ini +++ b/browser/base/content/test/urlbar/browser.ini @@ -43,6 +43,7 @@ support-files = moz.png [browser_tabMatchesInAwesomebar_perwindowpb.js] skip-if = os == 'linux' # Bug 1104755 +[browser_urlbarAboutHomeLoading.js] [browser_urlbarAutoFillTrimURLs.js] [browser_urlbarCopying.js] subsuite = clipboard diff --git a/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js b/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js new file mode 100644 index 000000000000..323062afb33a --- /dev/null +++ b/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js @@ -0,0 +1,43 @@ +"use strict"; + +/** + * Test what happens if loading a URL that should clear the + * location bar after a parent process URL. + */ +add_task(function* clearURLBarAfterParentProcessURL() { + let tab = yield new Promise(resolve => { + gBrowser.selectedTab = gBrowser.addTab("about:preferences"); + let newTabBrowser = gBrowser.getBrowserForTab(gBrowser.selectedTab); + newTabBrowser.addEventListener("Initialized", function onInit() { + newTabBrowser.removeEventListener("Initialized", onInit, true); + resolve(gBrowser.selectedTab); + }, true); + }); + document.getElementById("home-button").click(); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + is(gURLBar.value, "", "URL bar should be empty"); + is(tab.linkedBrowser.userTypedValue, null, "The browser should have no recorded userTypedValue"); + yield BrowserTestUtils.removeTab(tab); +}); + +/** + * Same as above, but open the tab without passing the URL immediately + * which changes behaviour in tabbrowser.xml. + */ +add_task(function* clearURLBarAfterParentProcessURLInExistingTab() { + let tab = yield new Promise(resolve => { + gBrowser.selectedTab = gBrowser.addTab(); + let newTabBrowser = gBrowser.getBrowserForTab(gBrowser.selectedTab); + newTabBrowser.addEventListener("Initialized", function onInit() { + newTabBrowser.removeEventListener("Initialized", onInit, true); + resolve(gBrowser.selectedTab); + }, true); + newTabBrowser.loadURI("about:preferences"); + }); + document.getElementById("home-button").click(); + yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); + is(gURLBar.value, "", "URL bar should be empty"); + is(tab.linkedBrowser.userTypedValue, null, "The browser should have no recorded userTypedValue"); + yield BrowserTestUtils.removeTab(tab); +}); + diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index f6071c2b32ca..d8535d34a5df 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -784,7 +784,10 @@ var SessionStoreInternal = { // clear user input instead), so we shouldn't set them here either. // They also don't fall under the issues in bug 439675 where user input // needs to be preserved if the load doesn't succeed. - if (!browser.userTypedValue && uri && !win.gInitialPages.includes(uri)) { + // We also don't do this for remoteness updates, where it should not + // be necessary. + if (!browser.userTypedValue && uri && !data.isRemotenessUpdate && + !win.gInitialPages.includes(uri)) { browser.userTypedValue = uri; } @@ -3335,7 +3338,8 @@ var SessionStoreInternal = { browser.messageManager.sendAsyncMessage("SessionStore:restoreHistory", { tabData: tabData, epoch: epoch, - loadArguments: aLoadArguments + loadArguments: aLoadArguments, + isRemotenessUpdate, }); } diff --git a/browser/components/sessionstore/content/content-sessionStore.js b/browser/components/sessionstore/content/content-sessionStore.js index cef8a23078e7..7eabd970bde4 100644 --- a/browser/components/sessionstore/content/content-sessionStore.js +++ b/browser/components/sessionstore/content/content-sessionStore.js @@ -154,7 +154,7 @@ var MessageListener = { } }, - restoreHistory({epoch, tabData, loadArguments}) { + restoreHistory({epoch, tabData, loadArguments, isRemotenessUpdate}) { gContentRestore.restoreHistory(tabData, loadArguments, { // Note: The callbacks passed here will only be used when a load starts // that was not initiated by sessionstore itself. This can happen when @@ -179,7 +179,7 @@ var MessageListener = { // sync about the state of the restore (particularly regarding // docShell.currentURI). Using a synchronous message is the easiest way // to temporarily synchronize them. - sendSyncMessage("SessionStore:restoreHistoryComplete", {epoch}); + sendSyncMessage("SessionStore:restoreHistoryComplete", {epoch, isRemotenessUpdate}); }, restoreTabContent({loadArguments, isRemotenessUpdate}) { From c3bb1849df50282fd0fab688582d1d649fc2664a Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Mon, 30 May 2016 12:54:56 +0100 Subject: [PATCH 020/199] Bug 1276117 - part 2: fix the URL bar state when loading about:home from a new tab page, r=mikedeboer MozReview-Commit-ID: FADnX8bymNE --HG-- extra : rebase_source : 270661c701ef1296280e5caf40aa6d85d565f2d4 --- browser/base/content/tabbrowser.xml | 19 ++++++++++----- .../urlbar/browser_urlbarAboutHomeLoading.js | 23 +++++++++++++++++++ browser/base/content/urlbarBindings.xml | 3 +++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 9ea754e1be73..8b019a45a453 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -637,15 +637,22 @@ // pointing to their resolved jar: or file: URIs. if (!(originalLocation && gInitialPages.includes(originalLocation.spec) && originalLocation != "about:blank" && + this.mBrowser.initialPageLoadedFromURLBar != originalLocation.spec && this.mBrowser.currentURI && this.mBrowser.currentURI.spec == "about:blank")) { - // This will trigger clearing the location bar. Don't do it if - // we loaded off a blank browser and this is an initial page load - // (e.g. about:privatebrowsing, about:newtab, etc.) so we avoid - // clearing the location bar in case the user is typing in it. - // loading about:blank shouldn't trigger this, either, because its - // loads are "special". + // Indicating that we started a load will allow the location + // bar to be cleared when the load finishes. + // In order to not overwrite user-typed content, we avoid it + // (see if condition above) in a very specific case: + // If the load is of an 'initial' page (e.g. about:privatebrowsing, + // about:newtab, etc.), was not explicitly typed in the location + // bar by the user, is not about:blank (because about:blank can be + // loaded by websites under their principal), and the current + // page in the browser is about:blank (indicating it is a newly + // created or re-created browser, e.g. because it just switched + // remoteness or is a new tab/window). this.mBrowser.urlbarChangeTracker.startedLoad(); } + delete this.mBrowser.initialPageLoadedFromURLBar; // If the browser is loading it must not be crashed anymore this.mTab.removeAttribute("crashed"); } diff --git a/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js b/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js index 323062afb33a..00e17f658d02 100644 --- a/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js +++ b/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js @@ -41,3 +41,26 @@ add_task(function* clearURLBarAfterParentProcessURLInExistingTab() { yield BrowserTestUtils.removeTab(tab); }); +/** + * Load about:home directly from an about:newtab page. Because it is an + * 'initial' page, we need to treat this specially if the user actually + * loads a page like this from the URL bar. + */ +add_task(function* clearURLBarAfterManuallyLoadingAboutHome() { + let promiseTabOpenedAndSwitchedTo = BrowserTestUtils.switchTab(gBrowser, () => {}); + // This opens about:newtab: + BrowserOpenTab(); + let tab = yield promiseTabOpenedAndSwitchedTo; + is(gURLBar.value, "", "URL bar should be empty"); + is(tab.linkedBrowser.userTypedValue, null, "userTypedValue should be null"); + + gURLBar.value = "about:home"; + gURLBar.select(); + let aboutHomeLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, "about:home"); + EventUtils.sendKey("return"); + yield aboutHomeLoaded; + + is(gURLBar.value, "", "URL bar should be empty"); + is(tab.linkedBrowser.userTypedValue, null, "userTypedValue should be null"); + yield BrowserTestUtils.removeTab(tab); +}); diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index 21ce20a4b399..2f84d7e10d7c 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -382,6 +382,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. { this.value = url; gBrowser.userTypedValue = url; + if (gInitialPages.includes(url)) { + gBrowser.selectedBrowser.initialPageLoadedFromURLBar = url; + } try { addToUrlbarHistory(url); } catch (ex) { From a115a3405b1fec2aaeb45fcafc205de333ef6751 Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Mon, 30 May 2016 12:55:30 +0100 Subject: [PATCH 021/199] Bug 1276117 - part 3: fix 'about:home' appearing briefly in the URL bar while loading, r=mikedeboer MozReview-Commit-ID: IM5V1PhDhs9 --HG-- extra : rebase_source : a6a53361c11a3035d4d374d17e8b8d8a88029b8d --- browser/base/content/browser.js | 12 +++++- .../urlbar/browser_urlbarAboutHomeLoading.js | 38 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 6a09dce6191f..ef07313d0b81 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -6420,8 +6420,16 @@ function checkEmptyPageOrigin(browser = gBrowser.selectedBrowser, let contentPrincipal = browser.contentPrincipal; // Not all principals have URIs... if (contentPrincipal.URI) { - // A manually entered about:blank URI is slightly magical: - if (uri.spec == "about:blank" && contentPrincipal.isNullPrincipal) { + // There are two specialcases involving about:blank. One is where + // the user has manually loaded it and it got created with a null + // principal. The other involves the case where we load + // some other empty page in a browser and the current page is the + // initial about:blank page (which has that as its principal, not + // just URI in which case it could be web-based). Especially in + // e10s, we need to tackle that case specifically to avoid race + // conditions when updating the URL bar. + if ((uri.spec == "about:blank" && contentPrincipal.isNullPrincipal) || + contentPrincipal.URI.spec == "about:blank") { return true; } return contentPrincipal.URI.equals(uri); diff --git a/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js b/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js index 00e17f658d02..792826eb1268 100644 --- a/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js +++ b/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js @@ -1,5 +1,7 @@ "use strict"; +const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {}); + /** * Test what happens if loading a URL that should clear the * location bar after a parent process URL. @@ -64,3 +66,39 @@ add_task(function* clearURLBarAfterManuallyLoadingAboutHome() { is(tab.linkedBrowser.userTypedValue, null, "userTypedValue should be null"); yield BrowserTestUtils.removeTab(tab); }); + +/** + * Ensure we don't show 'about:home' in the URL bar temporarily in new tabs + * while we're switching remoteness (when the URL we're loading and the + * default content principal are different). + */ +add_task(function* dontTemporarilyShowAboutHome() { + yield SpecialPowers.pushPrefEnv({set: [["browser.startup.page", 1]]}); + let windowOpenedPromise = BrowserTestUtils.waitForNewWindow(); + let win = OpenBrowserWindow(); + yield windowOpenedPromise; + let promiseTabSwitch = BrowserTestUtils.switchTab(win.gBrowser, () => {}); + win.BrowserOpenTab(); + yield promiseTabSwitch; + yield TabStateFlusher.flush(win.gBrowser.selectedBrowser); + yield BrowserTestUtils.closeWindow(win); + ok(SessionStore.getClosedWindowCount(), "Should have a closed window"); + + windowOpenedPromise = BrowserTestUtils.waitForNewWindow(); + win = SessionStore.undoCloseWindow(0); + yield windowOpenedPromise; + let wpl = { + onLocationChange(wpl, request, location, flags) { + is(win.gURLBar.value, "", "URL bar value should stay empty."); + }, + }; + win.gBrowser.addProgressListener(wpl); + let otherTab = win.gBrowser.selectedTab.previousSibling; + let tabLoaded = BrowserTestUtils.browserLoaded(otherTab.linkedBrowser, false, "about:home"); + yield BrowserTestUtils.switchTab(win.gBrowser, otherTab); + yield tabLoaded; + win.gBrowser.removeProgressListener(wpl); + is(win.gURLBar.value, "", "URL bar value should be empty."); + + yield BrowserTestUtils.closeWindow(win); +}); From ab38c664790f0078f1319cce372cc10275f8351d Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 31 May 2016 15:52:37 +0200 Subject: [PATCH 022/199] Bug 1276590: Replace |ReadBytes| with |ReadBytesInto| in Gonk IPC code, r=froydnj MozReview-Commit-ID: FdjdoOT7j7j --- gfx/layers/ipc/GonkNativeHandleUtils.cpp | 2 +- gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gfx/layers/ipc/GonkNativeHandleUtils.cpp b/gfx/layers/ipc/GonkNativeHandleUtils.cpp index 8abfd4f69f8d..74e423d961b1 100644 --- a/gfx/layers/ipc/GonkNativeHandleUtils.cpp +++ b/gfx/layers/ipc/GonkNativeHandleUtils.cpp @@ -37,7 +37,7 @@ ParamTraits::Read(const Message* aMsg, size_t nbytes; const char* data; if (!aMsg->ReadSize(aIter, &nbytes) || - !aMsg->ReadBytes(aIter, &data, nbytes)) { + !aMsg->ReadBytesInto(aIter, &data, nbytes)) { return false; } diff --git a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp index e77ba8235edb..579997219ee8 100644 --- a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp +++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp @@ -123,7 +123,7 @@ ParamTraits::Read(const Message* aMsg, if (!aMsg->ReadInt(aIter, &owner) || !aMsg->ReadInt64(aIter, &index) || !aMsg->ReadSize(aIter, &nbytes) || - !aMsg->ReadBytes(aIter, &data, nbytes)) { + !aMsg->ReadBytesInto(aIter, &data, nbytes)) { printf_stderr("ParamTraits::Read() failed to read a message\n"); return false; } @@ -139,7 +139,7 @@ ParamTraits::Read(const Message* aMsg, } // If the GraphicBuffer was shared cross-process, SCM_RIGHTS does // the right thing and dup's the fd. If it's shared cross-thread, - // SCM_RIGHTS doesn't dup the fd. + // SCM_RIGHTS doesn't dup the fd. // But in shared cross-thread, dup fd is not necessary because we get // a pointer to the GraphicBuffer directly from SharedBufferManagerParent // and don't create a new GraphicBuffer around the fd. From b6eaf79e2fb2348dd38593f7169d0303f5686df9 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Tue, 31 May 2016 15:56:49 +0200 Subject: [PATCH 023/199] Backed out changeset 1f80fae4ca27 (bug 1276590) on developers request --- gfx/layers/ipc/GonkNativeHandleUtils.cpp | 2 +- gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gfx/layers/ipc/GonkNativeHandleUtils.cpp b/gfx/layers/ipc/GonkNativeHandleUtils.cpp index 74e423d961b1..8abfd4f69f8d 100644 --- a/gfx/layers/ipc/GonkNativeHandleUtils.cpp +++ b/gfx/layers/ipc/GonkNativeHandleUtils.cpp @@ -37,7 +37,7 @@ ParamTraits::Read(const Message* aMsg, size_t nbytes; const char* data; if (!aMsg->ReadSize(aIter, &nbytes) || - !aMsg->ReadBytesInto(aIter, &data, nbytes)) { + !aMsg->ReadBytes(aIter, &data, nbytes)) { return false; } diff --git a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp index 579997219ee8..e77ba8235edb 100644 --- a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp +++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp @@ -123,7 +123,7 @@ ParamTraits::Read(const Message* aMsg, if (!aMsg->ReadInt(aIter, &owner) || !aMsg->ReadInt64(aIter, &index) || !aMsg->ReadSize(aIter, &nbytes) || - !aMsg->ReadBytesInto(aIter, &data, nbytes)) { + !aMsg->ReadBytes(aIter, &data, nbytes)) { printf_stderr("ParamTraits::Read() failed to read a message\n"); return false; } @@ -139,7 +139,7 @@ ParamTraits::Read(const Message* aMsg, } // If the GraphicBuffer was shared cross-process, SCM_RIGHTS does // the right thing and dup's the fd. If it's shared cross-thread, - // SCM_RIGHTS doesn't dup the fd. + // SCM_RIGHTS doesn't dup the fd. // But in shared cross-thread, dup fd is not necessary because we get // a pointer to the GraphicBuffer directly from SharedBufferManagerParent // and don't create a new GraphicBuffer around the fd. From 3ac31d4e7029231b35d49813dceca1f2da6adf10 Mon Sep 17 00:00:00 2001 From: Alexandre Lissy Date: Thu, 26 May 2016 12:40:25 +0200 Subject: [PATCH 024/199] Bug 1275299 - Make SocketTask a CancelableRunnable r=tzimmermann MozReview-Commit-ID: JHVQ6BFvS1Z --HG-- extra : rebase_source : 716d827e989d7a9e94478612c73f54f4bfe5aca5 --- ipc/unixsocket/SocketBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipc/unixsocket/SocketBase.h b/ipc/unixsocket/SocketBase.h index d7a74d1f119c..191567fdb9a1 100644 --- a/ipc/unixsocket/SocketBase.h +++ b/ipc/unixsocket/SocketBase.h @@ -453,7 +453,7 @@ private: * the I/O thread to the consumer thread. */ template -class SocketTask : public Runnable +class SocketTask : public CancelableRunnable { public: virtual ~SocketTask() From 98ad4a3ef7b56096f3cc47d44bad5dfe393fc383 Mon Sep 17 00:00:00 2001 From: Zheng Xu Date: Tue, 31 May 2016 03:22:00 +0200 Subject: [PATCH 025/199] Bug 1143022 - Manually mmap on arm64 to ensure high 17 bits are clear. r=ehoogeveen There might be 48-bit VA on arm64 depending on kernel configuration. Manually mmap heap memory to align with the assumption made by JS engine. --- js/src/gc/Memory.cpp | 37 +++++++++++++++++++++++- js/src/jsapi-tests/testGCAllocator.cpp | 40 ++++++++++++++++++++------ 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/js/src/gc/Memory.cpp b/js/src/gc/Memory.cpp index ceab0b2f2cad..735e1080e2f8 100644 --- a/js/src/gc/Memory.cpp +++ b/js/src/gc/Memory.cpp @@ -435,7 +435,7 @@ static inline void* MapMemoryAt(void* desired, size_t length, int prot = PROT_READ | PROT_WRITE, int flags = MAP_PRIVATE | MAP_ANON, int fd = -1, off_t offset = 0) { -#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) +#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) || defined(__aarch64__) MOZ_ASSERT(0xffff800000000000ULL & (uintptr_t(desired) + length - 1) == 0); #endif void* region = mmap(desired, length, prot, flags, fd, offset); @@ -485,6 +485,41 @@ MapMemory(size_t length, int prot = PROT_READ | PROT_WRITE, return nullptr; } return region; +#elif defined(__aarch64__) + /* + * There might be similar virtual address issue on arm64 which depends on + * hardware and kernel configurations. But the work around is slightly + * different due to the different mmap behavior. + * + * TODO: Merge with the above code block if this implementation works for + * ia64 and sparc64. + */ + const uintptr_t start = UINT64_C(0x0000070000000000); + const uintptr_t end = UINT64_C(0x0000800000000000); + const uintptr_t step = ChunkSize; + /* + * Optimization options if there are too many retries in practice: + * 1. Examine /proc/self/maps to find an available address. This file is + * not always available, however. In addition, even if we examine + * /proc/self/maps, we may still need to retry several times due to + * racing with other threads. + * 2. Use a global/static variable with lock to track the addresses we have + * allocated or tried. + */ + uintptr_t hint; + void* region = MAP_FAILED; + for (hint = start; region == MAP_FAILED && hint + length <= end; hint += step) { + region = mmap((void*)hint, length, prot, flags, fd, offset); + if (region != MAP_FAILED) { + if ((uintptr_t(region) + (length - 1)) & 0xffff800000000000) { + if (munmap(region, length)) { + MOZ_ASSERT(errno == ENOMEM); + } + region = MAP_FAILED; + } + } + } + return region == MAP_FAILED ? nullptr : region; #else void* region = MozTaggedAnonymousMmap(nullptr, length, prot, flags, fd, offset, "js-gc-heap"); if (region == MAP_FAILED) diff --git a/js/src/jsapi-tests/testGCAllocator.cpp b/js/src/jsapi-tests/testGCAllocator.cpp index 025ff307d5df..366a0274814a 100644 --- a/js/src/jsapi-tests/testGCAllocator.cpp +++ b/js/src/jsapi-tests/testGCAllocator.cpp @@ -312,7 +312,7 @@ void unmapPages(void* p, size_t size) { } void* mapMemoryAt(void* desired, size_t length) { -#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) +#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) || defined(__aarch64__) MOZ_RELEASE_ASSERT(0xffff800000000000ULL & (uintptr_t(desired) + length - 1) == 0); #endif void* region = mmap(desired, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); @@ -329,21 +329,45 @@ mapMemoryAt(void* desired, size_t length) void* mapMemory(size_t length) { - void* hint = nullptr; + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANON; + int fd = -1; + off_t offset = 0; + // The test code must be aligned with the implementation in gc/Memory.cpp. #if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) - hint = (void*)0x0000070000000000ULL; -#endif - void* region = mmap(hint, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + void* region = mmap((void*)0x0000070000000000, length, prot, flags, fd, offset); if (region == MAP_FAILED) return nullptr; -#if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__)) - if ((uintptr_t(region) + (length - 1)) & 0xffff800000000000ULL) { + if ((uintptr_t(region) + (length - 1)) & 0xffff800000000000) { if (munmap(region, length)) MOZ_RELEASE_ASSERT(errno == ENOMEM); return nullptr; } -#endif return region; +#elif defined(__aarch64__) + const uintptr_t start = UINT64_C(0x0000070000000000); + const uintptr_t end = UINT64_C(0x0000800000000000); + const uintptr_t step = ChunkSize; + uintptr_t hint; + void* region = MAP_FAILED; + for (hint = start; region == MAP_FAILED && hint + length <= end; hint += step) { + region = mmap((void*)hint, length, prot, flags, fd, offset); + if (region != MAP_FAILED) { + if ((uintptr_t(region) + (length - 1)) & 0xffff800000000000) { + if (munmap(region, length)) { + MOZ_RELEASE_ASSERT(errno == ENOMEM); + } + region = MAP_FAILED; + } + } + } + return region == MAP_FAILED ? nullptr : region; +#else + void* region = mmap(nullptr, length, prot, flags, fd, offset); + if (region == MAP_FAILED) + return nullptr; + return region; +#endif } void From b5b5c036859803eb5710e63d766ffde0847a72c6 Mon Sep 17 00:00:00 2001 From: Dragana Damjanovic Date: Tue, 31 May 2016 03:43:00 +0200 Subject: [PATCH 026/199] Bug 968273 - On reload load from the original uri, so that all redirects are reloader.r=mayhemer --- .../sessionstore/SessionHistory.jsm | 7 --- docshell/base/nsDocShell.cpp | 51 ++++--------------- docshell/base/nsDocShell.h | 5 -- docshell/base/nsDocShellLoadInfo.cpp | 17 +------ docshell/base/nsDocShellLoadInfo.h | 1 - docshell/base/nsIDocShell.idl | 12 ++--- docshell/base/nsIDocShellLoadInfo.idl | 5 -- docshell/shistory/nsISHEntry.idl | 5 -- docshell/shistory/nsSHEntry.cpp | 16 ------ docshell/shistory/nsSHEntry.h | 1 - docshell/shistory/nsSHistory.cpp | 4 -- docshell/test/bug968273_new.html | 1 + docshell/test/bug968273_redirect.html | 1 + .../test/bug968273_redirect.html^headers^ | 2 + docshell/test/mochitest.ini | 4 ++ docshell/test/test_bug968273.html | 40 +++++++++++++++ mobile/android/components/SessionStore.js | 8 --- 17 files changed, 64 insertions(+), 116 deletions(-) create mode 100644 docshell/test/bug968273_new.html create mode 100644 docshell/test/bug968273_redirect.html create mode 100644 docshell/test/bug968273_redirect.html^headers^ create mode 100644 docshell/test/test_bug968273.html diff --git a/browser/components/sessionstore/SessionHistory.jsm b/browser/components/sessionstore/SessionHistory.jsm index 0735fee6a94c..fc35bb49e95b 100644 --- a/browser/components/sessionstore/SessionHistory.jsm +++ b/browser/components/sessionstore/SessionHistory.jsm @@ -145,10 +145,6 @@ var SessionHistoryInternal = { entry.originalURI = shEntry.originalURI.spec; } - if (shEntry.loadReplace) { - entry.loadReplace = shEntry.loadReplace; - } - if (shEntry.srcdocData) entry.srcdocData = shEntry.srcdocData; @@ -315,9 +311,6 @@ var SessionHistoryInternal = { if (entry.originalURI) { shEntry.originalURI = Utils.makeURI(entry.originalURI); } - if (entry.loadReplace) { - shEntry.loadReplace = entry.loadReplace; - } if (entry.isSrcdocEntry) shEntry.srcdocData = entry.srcdocData; if (entry.baseURI) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 8447b7142f39..c4479a29f325 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -1253,7 +1253,6 @@ nsDocShell::LoadURI(nsIURI* aURI, nsCOMPtr referrer; nsCOMPtr originalURI; - bool loadReplace = false; nsCOMPtr postStream; nsCOMPtr headersStream; nsCOMPtr owner; @@ -1281,7 +1280,6 @@ nsDocShell::LoadURI(nsIURI* aURI, if (aLoadInfo) { aLoadInfo->GetReferrer(getter_AddRefs(referrer)); aLoadInfo->GetOriginalURI(getter_AddRefs(originalURI)); - aLoadInfo->GetLoadReplace(&loadReplace); nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal; aLoadInfo->GetLoadType(<); // Get the appropriate loadType from nsIDocShellLoadInfo type @@ -1541,7 +1539,6 @@ nsDocShell::LoadURI(nsIURI* aURI, return InternalLoad(aURI, originalURI, - loadReplace, referrer, referrerPolicy, owner, @@ -5288,7 +5285,7 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL, rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl); NS_ENSURE_SUCCESS(rv, rv); - return InternalLoad(errorPageURI, nullptr, false, nullptr, + return InternalLoad(errorPageURI, nullptr, nullptr, mozilla::net::RP_Default, nullptr, INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr, NullString(), nullptr, nullptr, LOAD_ERROR_PAGE, @@ -5340,7 +5337,6 @@ nsDocShell::Reload(uint32_t aReloadFlags) nsAutoString contentTypeHint; nsCOMPtr baseURI; nsCOMPtr originalURI; - bool loadReplace = false; if (doc) { principal = doc->NodePrincipal(); doc->GetContentType(contentTypeHint); @@ -5352,9 +5348,6 @@ nsDocShell::Reload(uint32_t aReloadFlags) } nsCOMPtr chan = doc->GetChannel(); if (chan) { - uint32_t loadFlags; - chan->GetLoadFlags(&loadFlags); - loadReplace = loadFlags & nsIChannel::LOAD_REPLACE; nsCOMPtr httpChan(do_QueryInterface(chan)); if (httpChan) { httpChan->GetOriginalURI(getter_AddRefs(originalURI)); @@ -5364,7 +5357,6 @@ nsDocShell::Reload(uint32_t aReloadFlags) rv = InternalLoad(mCurrentURI, originalURI, - loadReplace, mReferrerURI, mReferrerPolicy, principal, @@ -9506,7 +9498,7 @@ class InternalLoadEvent : public Runnable { public: InternalLoadEvent(nsDocShell* aDocShell, nsIURI* aURI, - nsIURI* aOriginalURI, bool aLoadReplace, + nsIURI* aOriginalURI, nsIURI* aReferrer, uint32_t aReferrerPolicy, nsISupports* aOwner, uint32_t aFlags, const char* aTypeHint, nsIInputStream* aPostData, @@ -9518,7 +9510,6 @@ public: , mDocShell(aDocShell) , mURI(aURI) , mOriginalURI(aOriginalURI) - , mLoadReplace(aLoadReplace) , mReferrer(aReferrer) , mReferrerPolicy(aReferrerPolicy) , mOwner(aOwner) @@ -9541,7 +9532,6 @@ public: Run() { return mDocShell->InternalLoad(mURI, mOriginalURI, - mLoadReplace, mReferrer, mReferrerPolicy, mOwner, mFlags, @@ -9561,7 +9551,6 @@ private: RefPtr mDocShell; nsCOMPtr mURI; nsCOMPtr mOriginalURI; - bool mLoadReplace; nsCOMPtr mReferrer; uint32_t mReferrerPolicy; nsCOMPtr mOwner; @@ -9628,7 +9617,6 @@ nsDocShell::IsAboutNewtab(nsIURI* aURI) NS_IMETHODIMP nsDocShell::InternalLoad(nsIURI* aURI, nsIURI* aOriginalURI, - bool aLoadReplace, nsIURI* aReferrer, uint32_t aReferrerPolicy, nsISupports* aOwner, @@ -9892,7 +9880,6 @@ nsDocShell::InternalLoad(nsIURI* aURI, if (NS_SUCCEEDED(rv) && targetDocShell) { rv = targetDocShell->InternalLoad(aURI, aOriginalURI, - aLoadReplace, aReferrer, aReferrerPolicy, owner, @@ -9973,7 +9960,7 @@ nsDocShell::InternalLoad(nsIURI* aURI, // Do this asynchronously nsCOMPtr ev = - new InternalLoadEvent(this, aURI, aOriginalURI, aLoadReplace, + new InternalLoadEvent(this, aURI, aOriginalURI, aReferrer, aReferrerPolicy, aOwner, aFlags, aTypeHint, aPostData, aHeadersData, aLoadType, aSHEntry, aFirstParty, aSrcdoc, @@ -10490,7 +10477,10 @@ nsDocShell::InternalLoad(nsIURI* aURI, nsINetworkPredictor::PREDICT_LOAD, this, nullptr); nsCOMPtr req; - rv = DoURILoad(aURI, aOriginalURI, aLoadReplace, aReferrer, + // At this point we will open a new channel to load data. If aOriginalURI + // is present, we load aOriginalURI instead of aURI because we want to load + // all redirects again. + rv = DoURILoad(aOriginalURI ? aOriginalURI : aURI, aReferrer, !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER), aReferrerPolicy, owner, aTypeHint, aFileName, aPostData, aHeadersData, @@ -10567,8 +10557,6 @@ nsDocShell::GetInheritedPrincipal(bool aConsiderCurrentDocument) nsresult nsDocShell::DoURILoad(nsIURI* aURI, - nsIURI* aOriginalURI, - bool aLoadReplace, nsIURI* aReferrerURI, bool aSendReferrer, uint32_t aReferrerPolicy, @@ -10841,17 +10829,7 @@ nsDocShell::DoURILoad(nsIURI* aURI, NS_ADDREF(*aRequest = channel); } - if (aOriginalURI) { - channel->SetOriginalURI(aOriginalURI); - if (aLoadReplace) { - uint32_t loadFlags; - channel->GetLoadFlags(&loadFlags); - NS_ENSURE_SUCCESS(rv, rv); - channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE); - } - } else { - channel->SetOriginalURI(aURI); - } + channel->SetOriginalURI(aURI); if (aTypeHint && *aTypeHint) { channel->SetContentType(nsDependentCString(aTypeHint)); @@ -10988,8 +10966,8 @@ nsDocShell::DoURILoad(nsIURI* aURI, httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy); } // set Content-Signature enforcing bit if aOriginalURI == about:newtab - if (aOriginalURI && httpChannel) { - if (IsAboutNewtab(aOriginalURI)) { + if (httpChannel) { + if (IsAboutNewtab(aURI)) { nsCOMPtr loadInfo = httpChannel->GetLoadInfo(); if (loadInfo) { loadInfo->SetVerifySignedContent(true); @@ -11853,7 +11831,6 @@ nsDocShell::AddState(JS::Handle aData, const nsAString& aTitle, newSHEntry = mOSHE; newSHEntry->SetURI(newURI); newSHEntry->SetOriginalURI(newURI); - newSHEntry->SetLoadReplace(false); } // Step 4: Modify new/original session history entry and clear its POST @@ -12064,7 +12041,6 @@ nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel, // Get the post data & referrer nsCOMPtr inputStream; nsCOMPtr originalURI; - bool loadReplace = false; nsCOMPtr referrerURI; uint32_t referrerPolicy = mozilla::net::RP_Default; nsCOMPtr cacheKey; @@ -12095,7 +12071,6 @@ nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel, httpChannel->GetOriginalURI(getter_AddRefs(originalURI)); uint32_t loadFlags; aChannel->GetLoadFlags(&loadFlags); - loadReplace = loadFlags & nsIChannel::LOAD_REPLACE; httpChannel->GetReferrer(getter_AddRefs(referrerURI)); httpChannel->GetReferrerPolicy(&referrerPolicy); @@ -12139,7 +12114,6 @@ nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel, mDynamicallyCreated); entry->SetOriginalURI(originalURI); - entry->SetLoadReplace(loadReplace); entry->SetReferrerURI(referrerURI); entry->SetReferrerPolicy(referrerPolicy); nsCOMPtr inStrmChan = do_QueryInterface(aChannel); @@ -12239,7 +12213,6 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) nsCOMPtr uri; nsCOMPtr originalURI; - bool loadReplace = false; nsCOMPtr postData; nsCOMPtr referrerURI; uint32_t referrerPolicy; @@ -12251,8 +12224,6 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(aEntry->GetOriginalURI(getter_AddRefs(originalURI)), NS_ERROR_FAILURE); - NS_ENSURE_SUCCESS(aEntry->GetLoadReplace(&loadReplace), - NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(aEntry->GetReferrerPolicy(&referrerPolicy), @@ -12333,7 +12304,6 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) // first created. bug 947716 has been created to address this issue. rv = InternalLoad(uri, originalURI, - loadReplace, referrerURI, referrerPolicy, owner, @@ -13820,7 +13790,6 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent, nsresult rv = InternalLoad(clonedURI, // New URI nullptr, // Original URI - false, // LoadReplace referer, // Referer URI refererPolicy, // Referer policy aContent->NodePrincipal(), // Owner is our node's diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index b19a691eaa9c..030a5a793d16 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -352,12 +352,7 @@ protected: // not have an owner on the channel should just pass null. // If aSrcdoc is not void, the load will be considered as a srcdoc load, // and the contents of aSrcdoc will be loaded instead of aURI. - // aOriginalURI will be set as the originalURI on the channel that does the - // load. If aOriginalURI is null, aURI will be set as the originalURI. - // If aLoadReplace is true, OLOAD_REPLACE flag will be set to the nsIChannel. nsresult DoURILoad(nsIURI* aURI, - nsIURI* aOriginalURI, - bool aLoadReplace, nsIURI* aReferrer, bool aSendReferrer, uint32_t aReferrerPolicy, diff --git a/docshell/base/nsDocShellLoadInfo.cpp b/docshell/base/nsDocShellLoadInfo.cpp index 0dac1cfc1815..ad302d27c043 100644 --- a/docshell/base/nsDocShellLoadInfo.cpp +++ b/docshell/base/nsDocShellLoadInfo.cpp @@ -12,8 +12,7 @@ #include "mozilla/net/ReferrerPolicy.h" nsDocShellLoadInfo::nsDocShellLoadInfo() - : mLoadReplace(false) - , mInheritOwner(false) + : mInheritOwner(false) , mOwnerIsExplicit(false) , mSendReferrer(true) , mReferrerPolicy(mozilla::net::RP_Default) @@ -68,20 +67,6 @@ nsDocShellLoadInfo::SetOriginalURI(nsIURI* aOriginalURI) return NS_OK; } -NS_IMETHODIMP -nsDocShellLoadInfo::GetLoadReplace(bool* aLoadReplace) -{ - *aLoadReplace = mLoadReplace; - return NS_OK; -} - -NS_IMETHODIMP -nsDocShellLoadInfo::SetLoadReplace(bool aLoadReplace) -{ - mLoadReplace = aLoadReplace; - return NS_OK; -} - NS_IMETHODIMP nsDocShellLoadInfo::GetOwner(nsISupports** aOwner) { diff --git a/docshell/base/nsDocShellLoadInfo.h b/docshell/base/nsDocShellLoadInfo.h index 105a53d433c0..7879ad9984bb 100644 --- a/docshell/base/nsDocShellLoadInfo.h +++ b/docshell/base/nsDocShellLoadInfo.h @@ -34,7 +34,6 @@ protected: nsCOMPtr mReferrer; nsCOMPtr mOriginalURI; nsCOMPtr mOwner; - bool mLoadReplace; bool mInheritOwner; bool mOwnerIsExplicit; bool mSendReferrer; diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index 5f4bb2fb9541..8d75d57be52c 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -125,12 +125,11 @@ interface nsIDocShell : nsIDocShellTreeItem * of an nsIDocShellLoadInfo object... * * @param aURI - The URI to load. - * @param aOriginalURI - The URI to set as the originalURI on the channel - * that does the load. If null, aURI will be set as - * the originalURI. - * @param aLoadReplace - If set LOAD_REPLACE flag will be set on the - * channel. aOriginalURI is null, this argument is - * ignored. + * @param aOriginalURI - If aOriginalURI is present and this function ends + * up actually loading data from network or cache, + * but not from bfcache, (e.g. in case of a reload) + * aOriginalURI will be loaded instead of aURI. + * Thereby we will load all redirects again. * @param aReferrer - Referring URI * @param aReferrerPolicy - Referrer policy * @param aOwner - Owner (security principal) @@ -158,7 +157,6 @@ interface nsIDocShell : nsIDocShellTreeItem */ [noscript]void internalLoad(in nsIURI aURI, in nsIURI aOriginalURI, - in boolean aLoadReplace, in nsIURI aReferrer, in unsigned long aReferrerPolicy, in nsISupports aOwner, diff --git a/docshell/base/nsIDocShellLoadInfo.idl b/docshell/base/nsIDocShellLoadInfo.idl index 89337152d72c..d06a4f72b7fb 100644 --- a/docshell/base/nsIDocShellLoadInfo.idl +++ b/docshell/base/nsIDocShellLoadInfo.idl @@ -30,11 +30,6 @@ interface nsIDocShellLoadInfo : nsISupports */ attribute nsIURI originalURI; - /** - * loadReplace flag to be passed to nsIDocShell.internalLoad. - */ - attribute boolean loadReplace; - /** The owner of the load, that is, the entity responsible for * causing the load to occur. This should be a nsIPrincipal typically. */ diff --git a/docshell/shistory/nsISHEntry.idl b/docshell/shistory/nsISHEntry.idl index b212ebd8522c..9eee45ea5733 100644 --- a/docshell/shistory/nsISHEntry.idl +++ b/docshell/shistory/nsISHEntry.idl @@ -47,11 +47,6 @@ interface nsISHEntry : nsISupports */ attribute nsIURI originalURI; - /** - * This flag remembers whether channel has LOAD_REPLACE set. - */ - attribute boolean loadReplace; - /** * A readonly property that returns the title * of the current entry. The object returned diff --git a/docshell/shistory/nsSHEntry.cpp b/docshell/shistory/nsSHEntry.cpp index 3d6b96ee9c9f..7185e2d7e717 100644 --- a/docshell/shistory/nsSHEntry.cpp +++ b/docshell/shistory/nsSHEntry.cpp @@ -24,7 +24,6 @@ static uint32_t gEntryID = 0; nsSHEntry::nsSHEntry() : mShared(new nsSHEntryShared()) - , mLoadReplace(false) , mReferrerPolicy(mozilla::net::RP_Default) , mLoadType(0) , mID(gEntryID++) @@ -41,7 +40,6 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther) : mShared(aOther.mShared) , mURI(aOther.mURI) , mOriginalURI(aOther.mOriginalURI) - , mLoadReplace(aOther.mLoadReplace) , mReferrerURI(aOther.mReferrerURI) , mReferrerPolicy(aOther.mReferrerPolicy) , mTitle(aOther.mTitle) @@ -137,20 +135,6 @@ nsSHEntry::SetOriginalURI(nsIURI* aOriginalURI) return NS_OK; } -NS_IMETHODIMP -nsSHEntry::GetLoadReplace(bool* aLoadReplace) -{ - *aLoadReplace = mLoadReplace; - return NS_OK; -} - -NS_IMETHODIMP -nsSHEntry::SetLoadReplace(bool aLoadReplace) -{ - mLoadReplace = aLoadReplace; - return NS_OK; -} - NS_IMETHODIMP nsSHEntry::GetReferrerURI(nsIURI** aReferrerURI) { diff --git a/docshell/shistory/nsSHEntry.h b/docshell/shistory/nsSHEntry.h index 0181e9e6c13a..a10fc9df0b76 100644 --- a/docshell/shistory/nsSHEntry.h +++ b/docshell/shistory/nsSHEntry.h @@ -50,7 +50,6 @@ private: // See nsSHEntry.idl for comments on these members. nsCOMPtr mURI; nsCOMPtr mOriginalURI; - bool mLoadReplace; nsCOMPtr mReferrerURI; uint32_t mReferrerPolicy; nsString mTitle; diff --git a/docshell/shistory/nsSHistory.cpp b/docshell/shistory/nsSHistory.cpp index 6e1ca4283449..80796a9bf77c 100644 --- a/docshell/shistory/nsSHistory.cpp +++ b/docshell/shistory/nsSHistory.cpp @@ -1767,10 +1767,6 @@ nsSHistory::InitiateLoad(nsISHEntry* aFrameEntry, nsIDocShell* aFrameDS, aFrameEntry->GetOriginalURI(getter_AddRefs(originalURI)); loadInfo->SetOriginalURI(originalURI); - bool loadReplace; - aFrameEntry->GetLoadReplace(&loadReplace); - loadInfo->SetLoadReplace(loadReplace); - nsCOMPtr nextURI; aFrameEntry->GetURI(getter_AddRefs(nextURI)); // Time to initiate a document load diff --git a/docshell/test/bug968273_new.html b/docshell/test/bug968273_new.html new file mode 100644 index 000000000000..d31d24e21a16 --- /dev/null +++ b/docshell/test/bug968273_new.html @@ -0,0 +1 @@ +This is bug968273_new.html. diff --git a/docshell/test/bug968273_redirect.html b/docshell/test/bug968273_redirect.html new file mode 100644 index 000000000000..a1ee6c19a8c4 --- /dev/null +++ b/docshell/test/bug968273_redirect.html @@ -0,0 +1 @@ +This document is redirected to bug968273_new.html. diff --git a/docshell/test/bug968273_redirect.html^headers^ b/docshell/test/bug968273_redirect.html^headers^ new file mode 100644 index 000000000000..86334513cf3b --- /dev/null +++ b/docshell/test/bug968273_redirect.html^headers^ @@ -0,0 +1,2 @@ +HTTP 302 Moved Temporarily +Location: bug968273_new.html diff --git a/docshell/test/mochitest.ini b/docshell/test/mochitest.ini index 8cbff7962eee..6c8b63494848 100644 --- a/docshell/test/mochitest.ini +++ b/docshell/test/mochitest.ini @@ -11,6 +11,9 @@ support-files = bug668513_redirect.html bug668513_redirect.html^headers^ bug691547_frame.html + bug968273_new.html + bug968273_redirect.html + bug968273_redirect.html^headers^ file_anchor_scroll_after_document_open.html file_bug385434_1.html file_bug385434_2.html @@ -108,3 +111,4 @@ support-files = file_framedhistoryframes.html skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bug1121701.html] skip-if = (buildapp == 'b2g' || buildapp == 'mulet') +[test_bug968273.html] diff --git a/docshell/test/test_bug968273.html b/docshell/test/test_bug968273.html new file mode 100644 index 000000000000..8ffe3d1bfdff --- /dev/null +++ b/docshell/test/test_bug968273.html @@ -0,0 +1,40 @@ + + + + + Test for Bug 968273 + + + + + + + + + diff --git a/mobile/android/components/SessionStore.js b/mobile/android/components/SessionStore.js index eca8a9c98d23..29f8309969e7 100644 --- a/mobile/android/components/SessionStore.js +++ b/mobile/android/components/SessionStore.js @@ -1035,10 +1035,6 @@ SessionStore.prototype = { entry.originalURI = aEntry.originalURI.spec; } - if (aEntry.loadReplace) { - entry.loadReplace = aEntry.loadReplace; - } - if (aEntry.contentType) { entry.contentType = aEntry.contentType; } @@ -1127,10 +1123,6 @@ SessionStore.prototype = { shEntry.originalURI = Services.io.newURI(aEntry.originalURI, null, null); } - if (aEntry.loadReplace) { - shEntry.loadReplace = aEntry.loadReplace; - } - if (aEntry.cacheKey) { let cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].createInstance(Ci.nsISupportsPRUint32); cacheKey.data = aEntry.cacheKey; From 97f671937b788b5f67bc976b1a806160aa63c297 Mon Sep 17 00:00:00 2001 From: Honza Bambas Date: Fri, 20 May 2016 08:33:00 +0200 Subject: [PATCH 027/199] Bug 428916 - support Cache-control directives in HTTP requests, r=mcmanus+michal+froydnj --- netwerk/protocol/http/CacheControlParser.cpp | 123 ++++++ netwerk/protocol/http/CacheControlParser.h | 44 ++ netwerk/protocol/http/moz.build | 1 + netwerk/protocol/http/nsHttpChannel.cpp | 67 +++- netwerk/protocol/http/nsHttpHeaderArray.cpp | 1 + netwerk/test/unit/head_cache2.js | 10 + .../test/unit/test_cache-control_request.js | 379 ++++++++++++++++++ netwerk/test/unit/xpcshell.ini | 2 +- 8 files changed, 615 insertions(+), 12 deletions(-) create mode 100644 netwerk/protocol/http/CacheControlParser.cpp create mode 100644 netwerk/protocol/http/CacheControlParser.h create mode 100644 netwerk/test/unit/test_cache-control_request.js diff --git a/netwerk/protocol/http/CacheControlParser.cpp b/netwerk/protocol/http/CacheControlParser.cpp new file mode 100644 index 000000000000..68c637706368 --- /dev/null +++ b/netwerk/protocol/http/CacheControlParser.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CacheControlParser.h" + +namespace mozilla { +namespace net { + +CacheControlParser::CacheControlParser(nsACString const &aHeader) + : Tokenizer(aHeader, nullptr, "-_") + , mMaxAgeSet(false) + , mMaxAge(0) + , mMaxStaleSet(false) + , mMaxStale(0) + , mMinFreshSet(false) + , mMinFresh(0) + , mNoCache(false) + , mNoStore(false) +{ + SkipWhites(); + if (!CheckEOF()) { + Directive(); + } +} + +void CacheControlParser::Directive() +{ + if (CheckWord("no-cache")) { + mNoCache = true; + IgnoreDirective(); // ignore any optionally added values + } else if (CheckWord("no-store")) { + mNoStore = true; + } else if (CheckWord("max-age")) { + mMaxAgeSet = SecondsValue(&mMaxAge); + } else if (CheckWord("max-stale")) { + mMaxStaleSet = SecondsValue(&mMaxStale, PR_UINT32_MAX); + } else if (CheckWord("min-fresh")) { + mMinFreshSet = SecondsValue(&mMinFresh); + } else { + IgnoreDirective(); + } + + SkipWhites(); + if (CheckEOF()) { + return; + } + if (CheckChar(',')) { + SkipWhites(); + Directive(); + return; + } + + NS_WARNING("Unexpected input in Cache-control header value"); +} + +bool CacheControlParser::SecondsValue(uint32_t *seconds, uint32_t defaultVal) +{ + SkipWhites(); + if (!CheckChar('=')) { + *seconds = defaultVal; + return !!defaultVal; + } + + SkipWhites(); + if (!ReadInteger(seconds)) { + NS_WARNING("Unexpected value in Cache-control header value"); + return false; + } + + return true; +} + +void CacheControlParser::IgnoreDirective() +{ + Token t; + while (Next(t)) { + if (t.Equals(Token::Char(',')) || t.Equals(Token::EndOfFile())) { + Rollback(); + break; + } + if (t.Equals(Token::Char('"'))) { + SkipUntil(Token::Char('"')); + if (!CheckChar('"')) { + NS_WARNING("Missing quoted string expansion in Cache-control header value"); + break; + } + } + } +} + +bool CacheControlParser::MaxAge(uint32_t *seconds) +{ + *seconds = mMaxAge; + return mMaxAgeSet; +} + +bool CacheControlParser::MaxStale(uint32_t *seconds) +{ + *seconds = mMaxStale; + return mMaxStaleSet; +} + +bool CacheControlParser::MinFresh(uint32_t *seconds) +{ + *seconds = mMinFresh; + return mMinFreshSet; +} + +bool CacheControlParser::NoCache() +{ + return mNoCache; +} + +bool CacheControlParser::NoStore() +{ + return mNoStore; +} + +} // net +} // mozilla diff --git a/netwerk/protocol/http/CacheControlParser.h b/netwerk/protocol/http/CacheControlParser.h new file mode 100644 index 000000000000..5f1b44213ab0 --- /dev/null +++ b/netwerk/protocol/http/CacheControlParser.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CacheControlParser_h__ +#define CacheControlParser_h__ + +#include "mozilla/Tokenizer.h" + +namespace mozilla { +namespace net { + +class CacheControlParser final : Tokenizer +{ +public: + explicit CacheControlParser(nsACString const &header); + + bool MaxAge(uint32_t *seconds); + bool MaxStale(uint32_t *seconds); + bool MinFresh(uint32_t *seconds); + bool NoCache(); + bool NoStore(); + +private: + void Directive(); + void IgnoreDirective(); + bool SecondsValue(uint32_t *seconds, uint32_t defaultVal = 0); + + bool mMaxAgeSet; + uint32_t mMaxAge; + bool mMaxStaleSet; + uint32_t mMaxStale; + bool mMinFreshSet; + uint32_t mMinFresh; + bool mNoCache; + bool mNoStore; +}; + +} // net +} // mozilla + +#endif diff --git a/netwerk/protocol/http/moz.build b/netwerk/protocol/http/moz.build index a208f4957875..cee49a9eb2fa 100644 --- a/netwerk/protocol/http/moz.build +++ b/netwerk/protocol/http/moz.build @@ -52,6 +52,7 @@ SOURCES += [ ] UNIFIED_SOURCES += [ + 'CacheControlParser.cpp', 'ConnectionDiagnostics.cpp', 'Http2Compression.cpp', 'Http2Push.cpp', diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 7ce9901d1046..b2a98e2f859b 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -95,6 +95,7 @@ #include "nsCORSListenerProxy.h" #include "nsISocketProvider.h" #include "mozilla/net/Predictor.h" +#include "CacheControlParser.h" namespace mozilla { namespace net { @@ -3146,6 +3147,14 @@ nsHttpChannel::OpenCacheEntry(bool isHttps) uint32_t cacheEntryOpenFlags; bool offline = gIOService->IsOffline() || appOffline; + + nsAutoCString cacheControlRequestHeader; + mRequestHead.GetHeader(nsHttp::Cache_Control, cacheControlRequestHeader); + CacheControlParser cacheControlRequest(cacheControlRequestHeader); + if (cacheControlRequest.NoStore() && !PossiblyIntercepted()) { + goto bypassCacheEntryOpen; + } + if (offline || (mLoadFlags & INHIBIT_CACHING)) { if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline && !PossiblyIntercepted()) { goto bypassCacheEntryOpen; @@ -3314,6 +3323,16 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC LOG(("nsHttpChannel::OnCacheEntryCheck enter [channel=%p entry=%p]", this, entry)); + nsAutoCString cacheControlRequestHeader; + mRequestHead.GetHeader(nsHttp::Cache_Control, cacheControlRequestHeader); + CacheControlParser cacheControlRequest(cacheControlRequestHeader); + + if (cacheControlRequest.NoStore()) { + LOG(("Not using cached response based on no-store request cache directive\n")); + *aResult = ENTRY_NOT_WANTED; + return NS_OK; + } + // Remember the request is a custom conditional request so that we can // process any 304 response correctly. mCustomConditionalRequest = @@ -3522,26 +3541,52 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC // and didn't do heuristic on it. but defacto that is allowed now. // // Check if the cache entry has expired... - uint32_t time = 0; // a temporary variable for storing time values... - rv = entry->GetExpirationTime(&time); + uint32_t now = NowInSeconds(); + + uint32_t age = 0; + rv = mCachedResponseHead->ComputeCurrentAge(now, now, &age); NS_ENSURE_SUCCESS(rv, rv); - LOG((" NowInSeconds()=%u, time=%u", NowInSeconds(), time)); - if (NowInSeconds() <= time) - doValidation = false; - else if (mCachedResponseHead->MustValidateIfExpired()) + uint32_t freshness = 0; + rv = mCachedResponseHead->ComputeFreshnessLifetime(&freshness); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t expiration = 0; + rv = entry->GetExpirationTime(&expiration); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t maxAgeRequest, maxStaleRequest, minFreshRequest; + + LOG((" NowInSeconds()=%u, expiration time=%u, freshness lifetime=%u, age=%u", + now, expiration, freshness, age)); + + if (cacheControlRequest.NoCache()) { + LOG((" validating, no-cache request")); doValidation = true; - else if (mLoadFlags & nsIRequest::VALIDATE_ONCE_PER_SESSION) { + } else if (cacheControlRequest.MaxStale(&maxStaleRequest)) { + uint32_t staleTime = age > freshness ? age - freshness : 0; + doValidation = staleTime > maxStaleRequest; + LOG((" validating=%d, max-stale=%u requested", doValidation, maxStaleRequest)); + } else if (cacheControlRequest.MaxAge(&maxAgeRequest)) { + doValidation = age > maxAgeRequest; + LOG((" validating=%d, max-age=%u requested", doValidation, maxAgeRequest)); + } else if (cacheControlRequest.MinFresh(&minFreshRequest)) { + uint32_t freshTime = freshness > age ? freshness - age : 0; + doValidation = freshTime < minFreshRequest; + LOG((" validating=%d, min-fresh=%u requested", doValidation, minFreshRequest)); + } else if (now <= expiration) { + doValidation = false; + LOG((" not validating, expire time not in the past")); + } else if (mCachedResponseHead->MustValidateIfExpired()) { + doValidation = true; + } else if (mLoadFlags & nsIRequest::VALIDATE_ONCE_PER_SESSION) { // If the cached response does not include expiration infor- // mation, then we must validate the response, despite whether // or not this is the first access this session. This behavior // is consistent with existing browsers and is generally expected // by web authors. - rv = mCachedResponseHead->ComputeFreshnessLifetime(&time); - NS_ENSURE_SUCCESS(rv, rv); - - if (time == 0) + if (freshness == 0) doValidation = true; else doValidation = fromPreviousSession; diff --git a/netwerk/protocol/http/nsHttpHeaderArray.cpp b/netwerk/protocol/http/nsHttpHeaderArray.cpp index 9c27724bbfe3..c14051d84df7 100644 --- a/netwerk/protocol/http/nsHttpHeaderArray.cpp +++ b/netwerk/protocol/http/nsHttpHeaderArray.cpp @@ -10,6 +10,7 @@ #include "nsHttpHeaderArray.h" #include "nsURLHelper.h" #include "nsIHttpHeaderVisitor.h" +#include "nsHttpHandler.h" namespace mozilla { namespace net { diff --git a/netwerk/test/unit/head_cache2.js b/netwerk/test/unit/head_cache2.js index 2b70a2510d08..decf04f90bfa 100644 --- a/netwerk/test/unit/head_cache2.js +++ b/netwerk/test/unit/head_cache2.js @@ -410,6 +410,16 @@ function MultipleCallbacks(number, goon, delayed) this.delayed = delayed; } +function wait_for_cache_index(continue_func) +{ + // This callback will not fire before the index is in the ready state. nsICacheStorage.exists() will + // no longer throw after this point. + get_cache_service().asyncGetDiskConsumption({ + onNetworkCacheDiskConsumption: function() { continue_func(); }, + QueryInterface() { return this; } + }); +} + function finish_cache2_test() { callbacks.forEach(function(callback, index) { diff --git a/netwerk/test/unit/test_cache-control_request.js b/netwerk/test/unit/test_cache-control_request.js new file mode 100644 index 000000000000..6a622c8fcc7f --- /dev/null +++ b/netwerk/test/unit/test_cache-control_request.js @@ -0,0 +1,379 @@ +Cu.import("resource://testing-common/httpd.js"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + +var httpserver = new HttpServer(); +httpserver.start(-1); +var cache = null; + +var base_url = "http://localhost:" + httpserver.identity.primaryPort; +var resource_age_100 = "/resource_age_100"; +var resource_age_100_url = base_url + resource_age_100; +var resource_stale_100 = "/resource_stale_100"; +var resource_stale_100_url = base_url + resource_stale_100; +var resource_fresh_100 = "/resource_fresh_100"; +var resource_fresh_100_url = base_url + resource_fresh_100; + +// Test flags +var hit_server = false; + + +function make_channel(url, cache_control) +{ + // Reset test global status + hit_server = false; + + var req = NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); + req.QueryInterface(Ci.nsIHttpChannel); + if (cache_control) { + req.setRequestHeader("Cache-control", cache_control, false); + } + + return req; +} + +function make_uri(url) { + var ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + return ios.newURI(url, null, null); +} + +function resource_age_100_handler(metadata, response) +{ + hit_server = true; + + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/plain", false); + response.setHeader("Age", "100", false); + response.setHeader("Last-Modified", date_string_from_now(-100), false); + response.setHeader("Expires", date_string_from_now(+9999), false); + + const body = "data1"; + response.bodyOutputStream.write(body, body.length); +} + +function resource_stale_100_handler(metadata, response) +{ + hit_server = true; + + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/plain", false); + response.setHeader("Date", date_string_from_now(-200), false); + response.setHeader("Last-Modified", date_string_from_now(-200), false); + response.setHeader("Cache-Control", "max-age=100", false); + response.setHeader("Expires", date_string_from_now(-100), false); + + const body = "data2"; + response.bodyOutputStream.write(body, body.length); +} + +function resource_fresh_100_handler(metadata, response) +{ + hit_server = true; + + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/plain", false); + response.setHeader("Last-Modified", date_string_from_now(0), false); + response.setHeader("Cache-Control", "max-age=100", false); + response.setHeader("Expires", date_string_from_now(+100), false); + + const body = "data3"; + response.bodyOutputStream.write(body, body.length); +} + + +function run_test() +{ + do_get_profile(); + do_test_pending(); + + httpserver.registerPathHandler(resource_age_100, resource_age_100_handler); + httpserver.registerPathHandler(resource_stale_100, resource_stale_100_handler); + httpserver.registerPathHandler(resource_fresh_100, resource_fresh_100_handler); + cache = getCacheStorage("disk"); + + wait_for_cache_index(run_next_test); +} + +// Here starts the list of tests + +// ============================================================================ +// Cache-Control: no-store + +add_test(() => { + // Must not create a cache entry + var ch = make_channel(resource_age_100_url, "no-store"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_false(cache.exists(make_uri(resource_age_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + // Prepare state only, cache the entry + var ch = make_channel(resource_age_100_url); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_age_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + // Check the prepared cache entry is used when no special directives are added + var ch = make_channel(resource_age_100_url); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_false(hit_server); + do_check_true(cache.exists(make_uri(resource_age_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + // Try again, while we already keep a cache entry, + // the channel must not use it, entry should stay in the cache + var ch = make_channel(resource_age_100_url, "no-store"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_age_100_url), "")); + + run_next_test(); + }, null), null); +}); + +// ============================================================================ +// Cache-Control: no-cache + +add_test(() => { + // Check the prepared cache entry is used when no special directives are added + var ch = make_channel(resource_age_100_url); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_false(hit_server); + do_check_true(cache.exists(make_uri(resource_age_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + // The existing entry should be revalidated (we expect a server hit) + var ch = make_channel(resource_age_100_url, "no-cache"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_age_100_url), "")); + + run_next_test(); + }, null), null); +}); + +// ============================================================================ +// Cache-Control: max-age + +add_test(() => { + // Check the prepared cache entry is used when no special directives are added + var ch = make_channel(resource_age_100_url); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_false(hit_server); + do_check_true(cache.exists(make_uri(resource_age_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + // The existing entry's age is greater than the maximum requested, + // should hit server + var ch = make_channel(resource_age_100_url, "max-age=10"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_age_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + // The existing entry's age is greater than the maximum requested, + // but the max-stale directive says to use it when it's fresh enough + var ch = make_channel(resource_age_100_url, "max-age=10, max-stale=99999"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_false(hit_server); + do_check_true(cache.exists(make_uri(resource_age_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + // The existing entry's age is lesser than the maximum requested, + // should go from cache + var ch = make_channel(resource_age_100_url, "max-age=1000"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_false(hit_server); + do_check_true(cache.exists(make_uri(resource_age_100_url), "")); + + run_next_test(); + }, null), null); +}); + +// ============================================================================ +// Cache-Control: max-stale + +add_test(() => { + // Preprate the entry first + var ch = make_channel(resource_stale_100_url); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_stale_100_url), "")); + + // Must shift the expiration time set on the entry to |now| be in the past + do_timeout(1500, run_next_test); + }, null), null); +}); + +add_test(() => { + // Check it's not reused (as it's stale) when no special directives + // are provided + var ch = make_channel(resource_stale_100_url); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_stale_100_url), "")); + + do_timeout(1500, run_next_test); + }, null), null); +}); + +add_test(() => { + // Accept cached responses of any stale time + var ch = make_channel(resource_stale_100_url, "max-stale"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_false(hit_server); + do_check_true(cache.exists(make_uri(resource_stale_100_url), "")); + + do_timeout(1500, run_next_test); + }, null), null); +}); + +add_test(() => { + // The entry is stale only by 100 seconds, accept it + var ch = make_channel(resource_stale_100_url, "max-stale=1000"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_false(hit_server); + do_check_true(cache.exists(make_uri(resource_stale_100_url), "")); + + do_timeout(1500, run_next_test); + }, null), null); +}); + +add_test(() => { + // The entry is stale by 100 seconds but we only accept a 10 seconds stale + // entry, go from server + var ch = make_channel(resource_stale_100_url, "max-stale=10"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_stale_100_url), "")); + + run_next_test(); + }, null), null); +}); + +// ============================================================================ +// Cache-Control: min-fresh + +add_test(() => { + // Preprate the entry first + var ch = make_channel(resource_fresh_100_url); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_fresh_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + // Check it's reused when no special directives are provided + var ch = make_channel(resource_fresh_100_url); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_false(hit_server); + do_check_true(cache.exists(make_uri(resource_fresh_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + // Entry fresh enough to be served from the cache + var ch = make_channel(resource_fresh_100_url, "min-fresh=10"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_false(hit_server); + do_check_true(cache.exists(make_uri(resource_fresh_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + // The entry is not fresh enough + var ch = make_channel(resource_fresh_100_url, "min-fresh=1000"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_fresh_100_url), "")); + + run_next_test(); + }, null), null); +}); + +// ============================================================================ +// Parser test, if the Cache-Control header would not parse correctly, the entry +// doesn't load from the server. + +add_test(() => { + var ch = make_channel(resource_fresh_100_url, "unknown1,unknown2 = \"a,b\", min-fresh = 1000 "); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_fresh_100_url), "")); + + run_next_test(); + }, null), null); +}); + +add_test(() => { + var ch = make_channel(resource_fresh_100_url, "no-cache = , min-fresh = 10"); + ch.asyncOpen(new ChannelListener(function(request, data) { + do_check_true(hit_server); + do_check_true(cache.exists(make_uri(resource_fresh_100_url), "")); + + run_next_test(); + }, null), null); +}); + +// ============================================================================ +// Done + +add_test(() => { + run_next_test(); + httpserver.stop(do_test_finished); +}); + +// ============================================================================ +// Helpers + +function date_string_from_now(delta_secs) { + var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', + 'Sep', 'Oct', 'Nov', 'Dec']; + var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + + var d = new Date(); + d.setTime(d.getTime() + delta_secs * 1000); + return days[d.getUTCDay()] + ", " + + d.getUTCDate() + " " + + months[d.getUTCMonth()] + " " + + d.getUTCFullYear() + " " + + d.getUTCHours() + ":" + + d.getUTCMinutes() + ":" + + d.getUTCSeconds() + " UTC"; +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 3e5294bd0413..9f5ddccc05ce 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -356,4 +356,4 @@ skip-if = os == "android" [test_packaged_app_bug1214079.js] [test_bug412457.js] [test_bug464591.js] - +[test_cache-control_request.js] From 4968e533af1422ca29ea5f557b4acaeef508cbcf Mon Sep 17 00:00:00 2001 From: bechen Date: Tue, 24 May 2016 11:30:22 +0800 Subject: [PATCH 028/199] Bug 882718 - Implement ActiveFlag at TextTrackCue object. r=rillian MozReview-Commit-ID: 4FksCKzB0Ep --HG-- extra : transplant_source : j%D413W%98%E7%9Es%7D%E9%A4A%FE%AD%8F%D4X%3C%7E --- dom/html/HTMLMediaElement.cpp | 5 +++++ dom/media/TextTrack.cpp | 13 +++++++++++++ dom/media/TextTrack.h | 2 ++ dom/media/TextTrackCue.cpp | 1 + dom/media/TextTrackCue.h | 12 ++++++++++++ dom/media/TextTrackCueList.cpp | 8 ++++++++ dom/media/TextTrackCueList.h | 2 ++ dom/media/TextTrackList.cpp | 8 ++++++++ dom/media/TextTrackList.h | 1 + 9 files changed, 52 insertions(+) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 734a85148ba7..61a6339e5846 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -776,6 +776,11 @@ void HTMLMediaElement::AbortExistingLoads() ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY); ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING); + //TODO: Apply the rules for text track cue rendering Bug 865407 + if (mTextTrackManager) { + mTextTrackManager->GetTextTracks()->SetCuesInactive(); + } + if (fireTimeUpdate) { // Since we destroyed the decoder above, the current playback position // will now be reported as 0. The playback position was non-zero when diff --git a/dom/media/TextTrack.cpp b/dom/media/TextTrack.cpp index 12f63580dbb0..6dded2583ff7 100644 --- a/dom/media/TextTrack.cpp +++ b/dom/media/TextTrack.cpp @@ -92,6 +92,10 @@ TextTrack::SetMode(TextTrackMode aValue) { if (mMode != aValue) { mMode = aValue; + if (aValue == TextTrackMode::Disabled) { + SetCuesInactive(); + //TODO: Apply the rules for text track cue rendering Bug 865407 + } if (mTextTrackList) { mTextTrackList->CreateAndDispatchChangeEvent(); } @@ -125,6 +129,9 @@ TextTrack::AddCue(TextTrackCue& aCue) void TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv) { + //TODO: Apply the rules for text track cue rendering Bug 865407 + aCue.SetActive(false); + mCueList->RemoveCue(aCue, aRv); SetDirty(); } @@ -263,5 +270,11 @@ TextTrack::SetTrackElement(HTMLTrackElement* aTrackElement) { mTrackElement = aTrackElement; } +void +TextTrack::SetCuesInactive() +{ + mCueList->SetCuesInactive(); +} + } // namespace dom } // namespace mozilla diff --git a/dom/media/TextTrack.h b/dom/media/TextTrack.h index a9725c2fc2cc..7eaeeacb1293 100644 --- a/dom/media/TextTrack.h +++ b/dom/media/TextTrack.h @@ -118,6 +118,8 @@ public: return mTextTrackSource; } + void SetCuesInactive(); + private: ~TextTrack(); diff --git a/dom/media/TextTrackCue.cpp b/dom/media/TextTrackCue.cpp index 44d2babae553..970b61772852 100644 --- a/dom/media/TextTrackCue.cpp +++ b/dom/media/TextTrackCue.cpp @@ -41,6 +41,7 @@ TextTrackCue::SetDefaultCueSettings() mAlign = AlignSetting::Middle; mLineAlign = AlignSetting::Start; mVertical = DirectionSetting::_empty; + mActive = false; } TextTrackCue::TextTrackCue(nsPIDOMWindowInner* aOwnerWindow, diff --git a/dom/media/TextTrackCue.h b/dom/media/TextTrackCue.h index d4b83ab6b1e7..103cb36b1fb1 100644 --- a/dom/media/TextTrackCue.h +++ b/dom/media/TextTrackCue.h @@ -332,6 +332,16 @@ public: void SetTrackElement(HTMLTrackElement* aTrackElement); + void SetActive(bool aActive) + { + mActive = aActive; + } + + bool GetActive() + { + return mActive; + } + private: ~TextTrackCue(); @@ -366,6 +376,8 @@ private: // changed. bool mReset; + bool mActive; + static StaticRefPtr sParserWrapper; }; diff --git a/dom/media/TextTrackCueList.cpp b/dom/media/TextTrackCueList.cpp index 0a54ed654445..09d09f9eb297 100644 --- a/dom/media/TextTrackCueList.cpp +++ b/dom/media/TextTrackCueList.cpp @@ -117,5 +117,13 @@ TextTrackCueList::GetArray(nsTArray >& aCues) } +void +TextTrackCueList::SetCuesInactive() +{ + for(uint32_t i = 0; i < mList.Length(); ++i) { + mList[i]->SetActive(false); + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/media/TextTrackCueList.h b/dom/media/TextTrackCueList.h index 0fe435bb98cb..5220cbb2a281 100644 --- a/dom/media/TextTrackCueList.h +++ b/dom/media/TextTrackCueList.h @@ -54,6 +54,8 @@ public: void RemoveAll(); void GetArray(nsTArray >& aCues); + void SetCuesInactive(); + private: ~TextTrackCueList(); diff --git a/dom/media/TextTrackList.cpp b/dom/media/TextTrackList.cpp index 14fb2b6d6e00..259475a3b880 100644 --- a/dom/media/TextTrackList.cpp +++ b/dom/media/TextTrackList.cpp @@ -211,5 +211,13 @@ TextTrackList::SetTextTrackManager(TextTrackManager* aTextTrackManager) mTextTrackManager = aTextTrackManager; } +void +TextTrackList::SetCuesInactive() +{ + for (uint32_t i = 0; i < Length(); i++) { + mTextTracks[i]->SetCuesInactive(); + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/media/TextTrackList.h b/dom/media/TextTrackList.h index b05dc7b1efe6..dc470b1634ba 100644 --- a/dom/media/TextTrackList.h +++ b/dom/media/TextTrackList.h @@ -61,6 +61,7 @@ public: nsresult DispatchTrackEvent(nsIDOMEvent* aEvent); void CreateAndDispatchChangeEvent(); + void SetCuesInactive(); IMPL_EVENT_HANDLER(change) IMPL_EVENT_HANDLER(addtrack) From f0d0134a05f9a193a24b16172557144171827cf4 Mon Sep 17 00:00:00 2001 From: bechen Date: Wed, 25 May 2016 16:11:24 +0800 Subject: [PATCH 029/199] Bug 882718 - Implement GetCueListByTimeInterval() at TextTrackCueList object. r=rillian MozReview-Commit-ID: 1T3EEfG83ec --HG-- extra : transplant_source : %13%EA%F55%E5k%0C%5D%CC%60_k%B5%03%FDnq%97L%B2 --- dom/media/TextTrackCueList.cpp | 14 ++++++++++++++ dom/media/TextTrackCueList.h | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/dom/media/TextTrackCueList.cpp b/dom/media/TextTrackCueList.cpp index 09d09f9eb297..655ebf7065e7 100644 --- a/dom/media/TextTrackCueList.cpp +++ b/dom/media/TextTrackCueList.cpp @@ -125,5 +125,19 @@ TextTrackCueList::SetCuesInactive() } } +already_AddRefed +TextTrackCueList::GetCueListByTimeInterval(media::Interval& aInterval) +{ + RefPtr output = new TextTrackCueList(mParent); + for (uint32_t i = 0; i < mList.Length(); ++i) { + TextTrackCue* cue = mList[i]; + if (cue->StartTime() <= aInterval.mEnd && + aInterval.mStart <= cue->EndTime()) { + output->AddCue(*cue); + } + } + return output.forget(); +} + } // namespace dom } // namespace mozilla diff --git a/dom/media/TextTrackCueList.h b/dom/media/TextTrackCueList.h index 5220cbb2a281..6ae019c8daac 100644 --- a/dom/media/TextTrackCueList.h +++ b/dom/media/TextTrackCueList.h @@ -12,6 +12,7 @@ #include "nsCycleCollectionParticipant.h" #include "nsWrapperCache.h" #include "mozilla/ErrorResult.h" +#include "Intervals.h" namespace mozilla { namespace dom { @@ -56,6 +57,9 @@ public: void SetCuesInactive(); + already_AddRefed + GetCueListByTimeInterval(media::Interval& aInterval); + private: ~TextTrackCueList(); From cce4f6652cf2124b046af9958b960f5155b0f365 Mon Sep 17 00:00:00 2001 From: Andrew Quartey Date: Wed, 25 May 2016 16:11:27 +0800 Subject: [PATCH 030/199] Bug 882718 - Implement "TimeMarchesOn". r=rillian MozReview-Commit-ID: 1RqUmgz056N * * * [mq]: hotfix MozReview-Commit-ID: CPByIPsUag4 --HG-- extra : transplant_source : %B6%EE%C8%C6%3F%A5%A5%3C%9E%92%D5%16%C6%9EX%CF%C9l%B5%D1 --- dom/html/TextTrackManager.cpp | 295 ++++++++++++++++++++++++++++++++- dom/html/TextTrackManager.h | 19 ++- dom/media/TextTrackCueList.cpp | 7 + dom/media/TextTrackCueList.h | 2 +- 4 files changed, 320 insertions(+), 3 deletions(-) diff --git a/dom/html/TextTrackManager.cpp b/dom/html/TextTrackManager.cpp index e86490287be7..fed128d30a04 100644 --- a/dom/html/TextTrackManager.cpp +++ b/dom/html/TextTrackManager.cpp @@ -76,7 +76,8 @@ CompareTextTracks::LessThan(TextTrack* aOne, TextTrack* aTwo) const } NS_IMPL_CYCLE_COLLECTION(TextTrackManager, mMediaElement, mTextTracks, - mPendingTextTracks, mNewCues) + mPendingTextTracks, mNewCues, + mLastActiveCues) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextTrackManager) NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) @@ -89,6 +90,9 @@ StaticRefPtr TextTrackManager::sParserWrapper; TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement) : mMediaElement(aMediaElement) + , mHasSeeked(false) + , mLastTimeMarchesOnCalled(0.0) + , mTimeMarchesOnDispatched(false) , performedTrackSelection(false) { nsISupports* parentObject = @@ -98,6 +102,7 @@ TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement) nsCOMPtr window = do_QueryInterface(parentObject); mNewCues = new TextTrackCueList(window); + mLastActiveCues = new TextTrackCueList(window); mTextTracks = new TextTrackList(window, this); mPendingTextTracks = new TextTrackList(window, this); @@ -191,6 +196,7 @@ TextTrackManager::DidSeek() if (mTextTracks) { mTextTracks->DidSeek(); } + mHasSeeked = true; } void @@ -380,5 +386,292 @@ TextTrackManager::HandleEvent(nsIDOMEvent* aEvent) return NS_OK; } + +class SimpleTextTrackEvent : public Runnable +{ +public: + friend class CompareSimpleTextTrackEvents; + SimpleTextTrackEvent(const nsAString& aEventName, double aTime, + TextTrack* aTrack, TextTrackCue* aCue) + : mName(aEventName), + mTime(aTime), + mTrack(aTrack), + mCue(aCue) + {} + + NS_IMETHOD Run() { + mCue->DispatchTrustedEvent(mName); + return NS_OK; + } + +private: + nsString mName; + double mTime; + TextTrack* mTrack; + RefPtr mCue; +}; + +class CompareSimpleTextTrackEvents { +private: + int32_t TrackChildPosition(SimpleTextTrackEvent* aEvent) const + { + HTMLTrackElement* trackElement = aEvent->mTrack->GetTrackElement();; + if (!trackElement) { + return -1; + } + return mMediaElement->IndexOf(trackElement); + } + HTMLMediaElement* mMediaElement; +public: + explicit CompareSimpleTextTrackEvents(HTMLMediaElement* aMediaElement) + { + mMediaElement = aMediaElement; + } + + bool Equals(SimpleTextTrackEvent* aOne, SimpleTextTrackEvent* aTwo) const + { + return false; + } + + bool LessThan(SimpleTextTrackEvent* aOne, SimpleTextTrackEvent* aTwo) const + { + if (aOne->mTime < aTwo->mTime) { + return true; + } else if (aOne->mTime > aTwo->mTime) { + return false; + } + + int32_t positionOne = TrackChildPosition(aOne); + int32_t positionTwo = TrackChildPosition(aTwo); + if (positionOne < positionTwo) { + return true; + } else if (positionOne > positionTwo) { + return false; + } + + if (aOne->mName.EqualsLiteral("enter") || + aTwo->mName.EqualsLiteral("exit")) { + return true; + } + return false; + } +}; + +class TextTrackListInternal +{ +public: + void AddTextTrack(TextTrack* aTextTrack, + const CompareTextTracks& aCompareTT) + { + if (!mTextTracks.Contains(aTextTrack)) { + mTextTracks.InsertElementSorted(aTextTrack, aCompareTT); + } + } + uint32_t Length() const + { + return mTextTracks.Length(); + } + TextTrack* operator[](uint32_t aIndex) + { + return mTextTracks.SafeElementAt(aIndex, nullptr); + } +private: + nsTArray> mTextTracks; +}; + +void +TextTrackManager::DispatchTimeMarchesOn() +{ + // Run the algorithm if no previous instance is still running, otherwise + // enqueue the current playback position and whether only that changed + // through its usual monotonic increase during normal playback; current + // executing call upon completion will check queue for further 'work'. + if (!mTimeMarchesOnDispatched) { + NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::TimeMarchesOn)); + mTimeMarchesOnDispatched = true; + } +} + +// https://html.spec.whatwg.org/multipage/embedded-content.html#time-marches-on +void +TextTrackManager::TimeMarchesOn() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + mTimeMarchesOnDispatched = false; + + nsISupports* parentObject = + mMediaElement->OwnerDoc()->GetParentObject(); + if (NS_WARN_IF(!parentObject)) { + return; + } + nsCOMPtr window = do_QueryInterface(parentObject); + + // Step 3. + double currentPlaybackTime = mMediaElement->CurrentTime(); + bool hasNormalPlayback = !mHasSeeked; + mHasSeeked = false; + + // Step 1, 2. + RefPtr currentCues = + new TextTrackCueList(window); + RefPtr otherCues = + new TextTrackCueList(window); + bool dummy; + for (uint32_t index = 0; index < mTextTracks->Length(); ++index) { + TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy); + if (ttrack && dummy) { + TextTrackCueList* activeCueList = ttrack->GetActiveCues(); + if (activeCueList) { + for (uint32_t i = 0; i < activeCueList->Length(); ++i) { + currentCues->AddCue(*((*activeCueList)[i])); + } + } + } + } + // Populate otherCues with 'non-active" cues. + if (hasNormalPlayback) { + media::Interval interval(mLastTimeMarchesOnCalled, + currentPlaybackTime); + otherCues = mNewCues->GetCueListByTimeInterval(interval);; + } else { + // Seek case. Put the mLastActiveCues into otherCues. + otherCues = mLastActiveCues; + } + for (uint32_t i = 0; i < currentCues->Length(); ++i) { + TextTrackCue* cue = (*currentCues)[i]; + ErrorResult dummy; + otherCues->RemoveCue(*cue, dummy); + } + + // Step 4. + RefPtr missedCues = new TextTrackCueList(window); + if (hasNormalPlayback) { + for (uint32_t i = 0; i < otherCues->Length(); ++i) { + TextTrackCue* cue = (*otherCues)[i]; + if (cue->StartTime() >= mLastTimeMarchesOnCalled && + cue->EndTime() <= currentPlaybackTime) { + missedCues->AddCue(*cue); + } + } + } + + // Step 5. Empty now. + // TODO: Step 6: fire timeupdate? + + // Step 7. Abort steps if condition 1, 2, 3 are satisfied. + // 1. All of the cues in current cues have their active flag set. + // 2. None of the cues in other cues have their active flag set. + // 3. Missed cues is empty. + bool c1 = true; + for (uint32_t i = 0; i < currentCues->Length(); ++i) { + if (!(*currentCues)[i]->GetActive()) { + c1 = false; + break; + } + } + bool c2 = true; + for (uint32_t i = 0; i < otherCues->Length(); ++i) { + if ((*otherCues)[i]->GetActive()) { + c2 = false; + break; + } + } + bool c3 = (missedCues->Length() == 0); + if (c1 && c2 && c3) { + mLastTimeMarchesOnCalled = currentPlaybackTime; + return; + } + + // Step 8. Respect PauseOnExit flag if not seek. + if (hasNormalPlayback) { + for (uint32_t i = 0; i < otherCues->Length(); ++i) { + TextTrackCue* cue = (*otherCues)[i]; + if (cue && cue->PauseOnExit() && cue->GetActive()) { + mMediaElement->Pause(); + break; + } + } + for (uint32_t i = 0; i < missedCues->Length(); ++i) { + TextTrackCue* cue = (*missedCues)[i]; + if (cue && cue->PauseOnExit()) { + mMediaElement->Pause(); + break; + } + } + } + + // Step 15. + // Sort text tracks in the same order as the text tracks appear + // in the media element's list of text tracks, and remove + // duplicates. + TextTrackListInternal affectedTracks; + // Step 13, 14. + nsTArray> eventList; + // Step 9, 10. + // For each text track cue in missed cues, prepare an event named + // enter for the TextTrackCue object with the cue start time. + for (uint32_t i = 0; i < missedCues->Length(); ++i) { + TextTrackCue* cue = (*missedCues)[i]; + if (cue) { + SimpleTextTrackEvent* event = + new SimpleTextTrackEvent(NS_LITERAL_STRING("enter"), + cue->StartTime(), cue->GetTrack(), + cue); + eventList.InsertElementSorted(event, + CompareSimpleTextTrackEvents(mMediaElement)); + affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement)); + } + } + + // Step 11, 17. + for (uint32_t i = 0; i < otherCues->Length(); ++i) { + TextTrackCue* cue = (*otherCues)[i]; + if (cue->GetActive() || + missedCues->GetCueById(cue->Id()) != nullptr) { + double time = cue->StartTime() > cue->EndTime() ? cue->StartTime() + : cue->EndTime(); + SimpleTextTrackEvent* event = + new SimpleTextTrackEvent(NS_LITERAL_STRING("exit"), time, + cue->GetTrack(), cue); + eventList.InsertElementSorted(event, + CompareSimpleTextTrackEvents(mMediaElement)); + affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement)); + } + cue->SetActive(false); + } + + // Step 12, 17. + for (uint32_t i = 0; i < currentCues->Length(); ++i) { + TextTrackCue* cue = (*currentCues)[i]; + if (!cue->GetActive()) { + SimpleTextTrackEvent* event = + new SimpleTextTrackEvent(NS_LITERAL_STRING("enter"), + cue->StartTime(), cue->GetTrack(), + cue); + eventList.InsertElementSorted(event, + CompareSimpleTextTrackEvents(mMediaElement)); + affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement)); + } + cue->SetActive(true); + } + + // Fire the eventList + for (uint32_t i = 0; i < eventList.Length(); ++i) { + NS_DispatchToMainThread(eventList[i].forget()); + } + + // Step 16. + for (uint32_t i = 0; i < affectedTracks.Length(); ++i) { + TextTrack* ttrack = affectedTracks[i]; + if (ttrack) { + ttrack->DispatchTrustedEvent(NS_LITERAL_STRING("cuechange")); + } + } + + mLastTimeMarchesOnCalled = currentPlaybackTime; + mLastActiveCues = currentCues; +} + } // namespace dom } // namespace mozilla diff --git a/dom/html/TextTrackManager.h b/dom/html/TextTrackManager.h index 40e585fb7b2d..e6e8d94289c6 100644 --- a/dom/html/TextTrackManager.h +++ b/dom/html/TextTrackManager.h @@ -22,9 +22,9 @@ class HTMLMediaElement; class CompareTextTracks { private: HTMLMediaElement* mMediaElement; + int32_t TrackChildPosition(TextTrack* aTrack) const; public: explicit CompareTextTracks(HTMLMediaElement* aMediaElement); - int32_t TrackChildPosition(TextTrack* aTrack) const; bool Equals(TextTrack* aOne, TextTrack* aTwo) const; bool LessThan(TextTrack* aOne, TextTrack* aTwo) const; }; @@ -94,13 +94,30 @@ public: // The HTMLMediaElement that this TextTrackManager manages the TextTracks of. RefPtr mMediaElement; + + void DispatchTimeMarchesOn(); + private: + void TimeMarchesOn(); + // List of the TextTrackManager's owning HTMLMediaElement's TextTracks. RefPtr mTextTracks; // List of text track objects awaiting loading. RefPtr mPendingTextTracks; // List of newly introduced Text Track cues. + + // Contain all cues for a MediaElement. RefPtr mNewCues; + // The active cues for the last TimeMarchesOn iteration. + RefPtr mLastActiveCues; + + // True if the media player playback changed due to seeking prior to and + // during running the "Time Marches On" algorithm. + bool mHasSeeked; + // Playback position at the time of last "Time Marches On" call + double mLastTimeMarchesOnCalled; + + bool mTimeMarchesOnDispatched; static StaticRefPtr sParserWrapper; diff --git a/dom/media/TextTrackCueList.cpp b/dom/media/TextTrackCueList.cpp index 655ebf7065e7..e3f98d38d7c1 100644 --- a/dom/media/TextTrackCueList.cpp +++ b/dom/media/TextTrackCueList.cpp @@ -62,6 +62,13 @@ TextTrackCueList::operator[](uint32_t aIndex) return mList.SafeElementAt(aIndex, nullptr); } +TextTrackCueList& +TextTrackCueList::operator=(const TextTrackCueList& aOther) +{ + mList = aOther.mList; + return *this; +} + TextTrackCue* TextTrackCueList::GetCueById(const nsAString& aId) { diff --git a/dom/media/TextTrackCueList.h b/dom/media/TextTrackCueList.h index 6ae019c8daac..aa88f3c062cb 100644 --- a/dom/media/TextTrackCueList.h +++ b/dom/media/TextTrackCueList.h @@ -44,7 +44,7 @@ public: TextTrackCue* IndexedGetter(uint32_t aIndex, bool& aFound); TextTrackCue* operator[](uint32_t aIndex); TextTrackCue* GetCueById(const nsAString& aId); - + TextTrackCueList& operator=(const TextTrackCueList& aOther); // Adds a cue to mList by performing an insertion sort on mList. // We expect most files to already be sorted, so an insertion sort starting // from the end of the current array should be more efficient than a general From bb5b50f7ee6700b965beb99885b5ac5694b3e891 Mon Sep 17 00:00:00 2001 From: bechen Date: Wed, 25 May 2016 16:11:28 +0800 Subject: [PATCH 031/199] Bug 882718 - triggerTimeMarchesOn. r=rillian MozReview-Commit-ID: 2OOqr1Z6X9 --HG-- extra : transplant_source : %A8%E2KG%82%29M%8EVA%20%9BJ%D1u%9B%85%8C%1D%1E --- dom/html/HTMLMediaElement.h | 5 +++++ dom/html/TextTrackManager.cpp | 30 ++++++++++++++++++++++++++++++ dom/html/TextTrackManager.h | 2 +- dom/media/TextTrack.cpp | 4 ++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index f514419d67cb..cda703eafe8e 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -699,6 +699,11 @@ public: mTextTrackManager->AddCue(aCue); } } + void NotifyCueRemoved(TextTrackCue& aCue) { + if (mTextTrackManager) { + mTextTrackManager->NotifyCueRemoved(aCue); + } + } /** * A public wrapper for FinishDecoderSetup() diff --git a/dom/html/TextTrackManager.cpp b/dom/html/TextTrackManager.cpp index fed128d30a04..44ea8e6a36d6 100644 --- a/dom/html/TextTrackManager.cpp +++ b/dom/html/TextTrackManager.cpp @@ -172,6 +172,7 @@ TextTrackManager::AddCues(TextTrack* aTextTrack) for (uint32_t i = 0; i < cueList->Length(); ++i) { mNewCues->AddCue(*cueList->IndexedGetter(i, dummy)); } + DispatchTimeMarchesOn(); } } @@ -188,6 +189,15 @@ TextTrackManager::RemoveTextTrack(TextTrack* aTextTrack, bool aPendingListOnly) } mTextTracks->RemoveTextTrack(aTextTrack); + // Remove the cues in mNewCues belong to aTextTrack. + TextTrackCueList* removeCueList = aTextTrack->GetCues(); + if (removeCueList) { + for (uint32_t i = 0; i < removeCueList->Length(); ++i) { + ErrorResult dummyRv; + mNewCues->RemoveCue(*((*removeCueList)[i]), dummyRv); + } + DispatchTimeMarchesOn(); + } } void @@ -235,6 +245,10 @@ TextTrackManager::UpdateCueDisplay() } else if (overlay->Length() > 0) { nsContentUtils::SetNodeTextContent(overlay, EmptyString(), true); } + // Call TimeMarchesOn() directly instead DispatchTimeMarchesOn() + // because we had render the new cue, so we must run + // TimeMarchesOn immediately. + TimeMarchesOn(); } void @@ -243,6 +257,17 @@ TextTrackManager::AddCue(TextTrackCue& aCue) if (mNewCues) { mNewCues->AddCue(aCue); } + DispatchTimeMarchesOn(); +} + +void +TextTrackManager::NotifyCueRemoved(TextTrackCue& aCue) +{ + if (mNewCues) { + ErrorResult dummyRv; + mNewCues->RemoveCue(aCue, dummyRv); + } + DispatchTimeMarchesOn(); } void @@ -507,6 +532,10 @@ TextTrackManager::TimeMarchesOn() } nsCOMPtr window = do_QueryInterface(parentObject); + if (mMediaElement && !(mMediaElement->GetPlayedOrSeeked())) { + return; + } + // Step 3. double currentPlaybackTime = mMediaElement->CurrentTime(); bool hasNormalPlayback = !mHasSeeked; @@ -521,6 +550,7 @@ TextTrackManager::TimeMarchesOn() for (uint32_t index = 0; index < mTextTracks->Length(); ++index) { TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy); if (ttrack && dummy) { + // TODO: call GetCueListByTimeInterval on mNewCues? TextTrackCueList* activeCueList = ttrack->GetActiveCues(); if (activeCueList) { for (uint32_t i = 0; i < activeCueList->Length(); ++i) { diff --git a/dom/html/TextTrackManager.h b/dom/html/TextTrackManager.h index e6e8d94289c6..b67a819e3166 100644 --- a/dom/html/TextTrackManager.h +++ b/dom/html/TextTrackManager.h @@ -57,7 +57,7 @@ public: void AddCue(TextTrackCue& aCue); void AddCues(TextTrack* aTextTrack); - + void NotifyCueRemoved(TextTrackCue& aCue); /** * Overview of WebVTT cuetext and anonymous content setup. * diff --git a/dom/media/TextTrack.cpp b/dom/media/TextTrack.cpp index 6dded2583ff7..26834089113c 100644 --- a/dom/media/TextTrack.cpp +++ b/dom/media/TextTrack.cpp @@ -133,6 +133,10 @@ TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv) aCue.SetActive(false); mCueList->RemoveCue(aCue, aRv); + HTMLMediaElement* mediaElement = mTextTrackList->GetMediaElement(); + if (mediaElement) { + mediaElement->NotifyCueRemoved(aCue); + } SetDirty(); } From db0d961dacc658a0e4a79f5b83dd380474d1f12b Mon Sep 17 00:00:00 2001 From: bechen Date: Thu, 26 May 2016 11:45:43 +0800 Subject: [PATCH 032/199] Bug 882718 - 1. Fix testcase crash/failed . 2. The cuechange event should be fired in TimeMarchesOn. r=rillian MozReview-Commit-ID: EYi9iZ1mfjg --HG-- extra : transplant_source : %B3%A1%B5g%D1s%08%18%25%8A%09%C0%A1%D7%7C%E9%D7F%97%A1 --- dom/html/HTMLMediaElement.cpp | 6 +++--- dom/html/TextTrackManager.cpp | 19 ++++++++++++------- dom/media/TextTrack.cpp | 15 --------------- dom/media/TextTrackCueList.cpp | 6 ++++++ dom/media/TextTrackCueList.h | 1 + dom/media/test/test_texttrackcue.html | 4 ++++ 6 files changed, 26 insertions(+), 25 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 61a6339e5846..88c494ffd01c 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -3740,13 +3740,13 @@ void HTMLMediaElement::SeekCompleted() { mPlayingBeforeSeek = false; SetPlayedOrSeeked(true); + if (mTextTrackManager) { + mTextTrackManager->DidSeek(); + } FireTimeUpdate(false); DispatchAsyncEvent(NS_LITERAL_STRING("seeked")); // We changed whether we're seeking so we need to AddRemoveSelfReference AddRemoveSelfReference(); - if (mTextTrackManager) { - mTextTrackManager->DidSeek(); - } if (mCurrentPlayRangeStart == -1.0) { mCurrentPlayRangeStart = CurrentTime(); } diff --git a/dom/html/TextTrackManager.cpp b/dom/html/TextTrackManager.cpp index 44ea8e6a36d6..773dbc5236ca 100644 --- a/dom/html/TextTrackManager.cpp +++ b/dom/html/TextTrackManager.cpp @@ -193,8 +193,7 @@ TextTrackManager::RemoveTextTrack(TextTrack* aTextTrack, bool aPendingListOnly) TextTrackCueList* removeCueList = aTextTrack->GetCues(); if (removeCueList) { for (uint32_t i = 0; i < removeCueList->Length(); ++i) { - ErrorResult dummyRv; - mNewCues->RemoveCue(*((*removeCueList)[i]), dummyRv); + mNewCues->RemoveCue(*((*removeCueList)[i])); } DispatchTimeMarchesOn(); } @@ -206,6 +205,9 @@ TextTrackManager::DidSeek() if (mTextTracks) { mTextTracks->DidSeek(); } + if (mMediaElement) { + mLastTimeMarchesOnCalled = mMediaElement->CurrentTime(); + } mHasSeeked = true; } @@ -264,8 +266,7 @@ void TextTrackManager::NotifyCueRemoved(TextTrackCue& aCue) { if (mNewCues) { - ErrorResult dummyRv; - mNewCues->RemoveCue(aCue, dummyRv); + mNewCues->RemoveCue(aCue); } DispatchTimeMarchesOn(); } @@ -532,7 +533,8 @@ TextTrackManager::TimeMarchesOn() } nsCOMPtr window = do_QueryInterface(parentObject); - if (mMediaElement && !(mMediaElement->GetPlayedOrSeeked())) { + if (mMediaElement && + (!(mMediaElement->GetPlayedOrSeeked())|| mMediaElement->Seeking())) { return; } @@ -570,8 +572,7 @@ TextTrackManager::TimeMarchesOn() } for (uint32_t i = 0; i < currentCues->Length(); ++i) { TextTrackCue* cue = (*currentCues)[i]; - ErrorResult dummy; - otherCues->RemoveCue(*cue, dummy); + otherCues->RemoveCue(*cue); } // Step 4. @@ -696,6 +697,10 @@ TextTrackManager::TimeMarchesOn() TextTrack* ttrack = affectedTracks[i]; if (ttrack) { ttrack->DispatchTrustedEvent(NS_LITERAL_STRING("cuechange")); + HTMLTrackElement* trackElement = ttrack->GetTrackElement(); + if (trackElement) { + trackElement->DispatchTrackRunnable(NS_LITERAL_STRING("cuechange")); + } } } diff --git a/dom/media/TextTrack.cpp b/dom/media/TextTrack.cpp index 26834089113c..1eb21ccf832d 100644 --- a/dom/media/TextTrack.cpp +++ b/dom/media/TextTrack.cpp @@ -160,10 +160,6 @@ TextTrack::UpdateActiveCueList() return; } - // Flag that indicates whether or not this call of UpdateActiveCueList has - // changed the activeCueList. - bool hasChanged = false; - // If we are dirty, i.e. an event happened that may cause the sorted mCueList // to have changed like a seek or an insert for a cue, than we need to rebuild // the active cue list from scratch. @@ -179,7 +175,6 @@ TextTrack::UpdateActiveCueList() for (uint32_t i = mActiveCueList->Length(); i > 0; i--) { if ((*mActiveCueList)[i - 1]->EndTime() < playbackTime) { mActiveCueList->RemoveCueAt(i - 1); - hasChanged = true; } } // Add all the cues, starting from the position of the last cue that was @@ -190,16 +185,6 @@ TextTrack::UpdateActiveCueList() (*mCueList)[mCuePos]->StartTime() <= playbackTime; mCuePos++) { if ((*mCueList)[mCuePos]->EndTime() >= playbackTime) { mActiveCueList->AddCue(*(*mCueList)[mCuePos]); - hasChanged = true; - } - } - - if (hasChanged) { - RefPtr asyncDispatcher = - new AsyncEventDispatcher(this, NS_LITERAL_STRING("cuechange"), false); - asyncDispatcher->PostDOMEvent(); - if (mTrackElement) { - mTrackElement->DispatchTrackRunnable(NS_LITERAL_STRING("cuechange")); } } } diff --git a/dom/media/TextTrackCueList.cpp b/dom/media/TextTrackCueList.cpp index e3f98d38d7c1..f3fe66662adb 100644 --- a/dom/media/TextTrackCueList.cpp +++ b/dom/media/TextTrackCueList.cpp @@ -103,6 +103,12 @@ TextTrackCueList::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv) mList.RemoveElement(&aCue); } +void +TextTrackCueList::RemoveCue(TextTrackCue& aCue) +{ + mList.RemoveElement(&aCue); +} + void TextTrackCueList::RemoveCueAt(uint32_t aIndex) { diff --git a/dom/media/TextTrackCueList.h b/dom/media/TextTrackCueList.h index aa88f3c062cb..8a77045f76e6 100644 --- a/dom/media/TextTrackCueList.h +++ b/dom/media/TextTrackCueList.h @@ -50,6 +50,7 @@ public: // from the end of the current array should be more efficient than a general // sort step after all cues are loaded. void AddCue(TextTrackCue& aCue); + void RemoveCue(TextTrackCue& aCue); void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv); void RemoveCueAt(uint32_t aIndex); void RemoveAll(); diff --git a/dom/media/test/test_texttrackcue.html b/dom/media/test/test_texttrackcue.html index 29fc9b3cf869..08a2016ec540 100644 --- a/dom/media/test/test_texttrackcue.html +++ b/dom/media/test/test_texttrackcue.html @@ -83,6 +83,10 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]}, is(cue.endTime, 0.71, "Cue's end time should be 0.71."); cue.pauseOnExit = true; is(cue.pauseOnExit, true, "Cue's pause on exit flag should be true."); + video.addEventListener("pause", function pauseOnExit() { + video.removeEventListener("pause", pauseOnExit, false); + video.play(); + }); var exceptionHappened; function checkPercentageValue(prop) { From 0ae4309ec5ca6c49bf53c8598241a306779ef025 Mon Sep 17 00:00:00 2001 From: bechen Date: Mon, 30 May 2016 12:03:15 +0800 Subject: [PATCH 033/199] Bug 882718 - Do not dispatch task to main thread when shutdown. r=rillian MozReview-Commit-ID: 5Y79Fbhyoc3 --HG-- extra : transplant_source : %FEi%D2%BB%10%EFj%CE%EA%C9%E6%95%99%A9v%8C%7D%C9%5B%05 --- dom/html/TextTrackManager.cpp | 7 ++++++- dom/html/TextTrackManager.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/dom/html/TextTrackManager.cpp b/dom/html/TextTrackManager.cpp index 773dbc5236ca..e232c817c10f 100644 --- a/dom/html/TextTrackManager.cpp +++ b/dom/html/TextTrackManager.cpp @@ -22,6 +22,8 @@ namespace mozilla { namespace dom { +NS_IMPL_ISUPPORTS(TextTrackManager::ShutdownObserverProxy, nsIObserver); + CompareTextTracks::CompareTextTracks(HTMLMediaElement* aMediaElement) { mMediaElement = aMediaElement; @@ -94,6 +96,7 @@ TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement) , mLastTimeMarchesOnCalled(0.0) , mTimeMarchesOnDispatched(false) , performedTrackSelection(false) + , mShutdown(false) { nsISupports* parentObject = mMediaElement->OwnerDoc()->GetParentObject(); @@ -112,10 +115,12 @@ TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement) sParserWrapper = parserWrapper; ClearOnShutdown(&sParserWrapper); } + mShutdownProxy = new ShutdownObserverProxy(this); } TextTrackManager::~TextTrackManager() { + nsContentUtils::UnregisterShutdownObserver(mShutdownProxy); } TextTrackList* @@ -512,7 +517,7 @@ TextTrackManager::DispatchTimeMarchesOn() // enqueue the current playback position and whether only that changed // through its usual monotonic increase during normal playback; current // executing call upon completion will check queue for further 'work'. - if (!mTimeMarchesOnDispatched) { + if (!mTimeMarchesOnDispatched && !mShutdown) { NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::TimeMarchesOn)); mTimeMarchesOnDispatched = true; } diff --git a/dom/html/TextTrackManager.h b/dom/html/TextTrackManager.h index b67a819e3166..97cee41abfc7 100644 --- a/dom/html/TextTrackManager.h +++ b/dom/html/TextTrackManager.h @@ -11,6 +11,7 @@ #include "mozilla/dom/TextTrackList.h" #include "mozilla/dom/TextTrackCueList.h" #include "mozilla/StaticPtr.h" +#include "nsContentUtils.h" class nsIWebVTTParserWrapper; @@ -97,6 +98,11 @@ public: void DispatchTimeMarchesOn(); + void NotifyShutdown() + { + mShutdown = true; + } + private: void TimeMarchesOn(); @@ -135,6 +141,35 @@ private: void GetTextTracksOfKind(TextTrackKind aTextTrackKind, nsTArray& aTextTracks); bool TrackIsDefault(TextTrack* aTextTrack); + + class ShutdownObserverProxy final : public nsIObserver + { + NS_DECL_ISUPPORTS + + public: + explicit ShutdownObserverProxy(TextTrackManager* aManager) + : mManager(aManager) + { + nsContentUtils::RegisterShutdownObserver(this); + } + + NS_IMETHODIMP Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) override + { + MOZ_ASSERT(NS_IsMainThread()); + if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { + nsContentUtils::UnregisterShutdownObserver(this); + mManager->NotifyShutdown(); + } + return NS_OK; + } + + private: + ~ShutdownObserverProxy() {}; + TextTrackManager* mManager; + }; + + RefPtr mShutdownProxy; + bool mShutdown; }; } // namespace dom From 3b1413d4495f1b66215fc2767873bcfccdaab6b5 Mon Sep 17 00:00:00 2001 From: Ethan Lin Date: Sat, 28 May 2016 06:23:00 +0200 Subject: [PATCH 034/199] Bug 1275966 - Part 1. Fix wrong rendering of background-repeat:space. r=mstange --- layout/base/nsCSSRendering.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 8074849bd238..0609443d15fa 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -3327,15 +3327,19 @@ ComputeDrawnSizeForBackground(const CSSSizeOrRatio& aIntrinsicSize, * aImageDimension: the image width/height * aAvailableSpace: the background positioning area width/height * aRepeatSize: the image size plus gap size of app units for use as spacing + * aRepeat: determine whether the image is repeated */ static nscoord ComputeSpacedRepeatSize(nscoord aImageDimension, - nscoord aAvailableSpace) { + nscoord aAvailableSpace, + bool& aRepeat) { float ratio = aAvailableSpace / aImageDimension; if (ratio < 2.0f) { // If you can't repeat at least twice, then don't repeat. + aRepeat = false; return aImageDimension; } else { + aRepeat = true; return (aAvailableSpace - aImageDimension) / (NSToIntFloor(ratio) - 1); } } @@ -3489,9 +3493,11 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext, &imageTopLeft, &state.mAnchor); state.mRepeatSize = imageSize; if (repeatX == NS_STYLE_IMAGELAYER_REPEAT_SPACE) { + bool isRepeat; state.mRepeatSize.width = ComputeSpacedRepeatSize(imageSize.width, - bgPositionSize.width); - if (state.mRepeatSize.width > imageSize.width) { + bgPositionSize.width, + isRepeat); + if (isRepeat) { imageTopLeft.x = 0; state.mAnchor.x = 0; } else { @@ -3500,9 +3506,11 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext, } if (repeatY == NS_STYLE_IMAGELAYER_REPEAT_SPACE) { + bool isRepeat; state.mRepeatSize.height = ComputeSpacedRepeatSize(imageSize.height, - bgPositionSize.height); - if (state.mRepeatSize.height > imageSize.height) { + bgPositionSize.height, + isRepeat); + if (isRepeat) { imageTopLeft.y = 0; state.mAnchor.y = 0; } else { From 4df0a8d12ec60680835a8f13ba6aee7db4e26bcf Mon Sep 17 00:00:00 2001 From: Ethan Lin Date: Sat, 28 May 2016 06:27:00 +0200 Subject: [PATCH 035/199] Bug 1275966 - Part 2. Add reftests for background-repeat: space. r=mstange --- .../background-repeat-space-10-ref.html | 41 +++++++++++++++++++ .../background-repeat-space-10.html | 25 +++++++++++ .../background-repeat-space-9-ref.html | 41 +++++++++++++++++++ .../background/background-repeat-space-9.html | 25 +++++++++++ .../w3c-css/submitted/background/reftest.list | 2 + 5 files changed, 134 insertions(+) create mode 100644 layout/reftests/w3c-css/submitted/background/background-repeat-space-10-ref.html create mode 100644 layout/reftests/w3c-css/submitted/background/background-repeat-space-10.html create mode 100644 layout/reftests/w3c-css/submitted/background/background-repeat-space-9-ref.html create mode 100644 layout/reftests/w3c-css/submitted/background/background-repeat-space-9.html diff --git a/layout/reftests/w3c-css/submitted/background/background-repeat-space-10-ref.html b/layout/reftests/w3c-css/submitted/background/background-repeat-space-10-ref.html new file mode 100644 index 000000000000..35ed461c8d70 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/background/background-repeat-space-10-ref.html @@ -0,0 +1,41 @@ + + + + + CSS Background: background-repeat: position background image + + + + + +
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/background/background-repeat-space-10.html b/layout/reftests/w3c-css/submitted/background/background-repeat-space-10.html new file mode 100644 index 000000000000..db8c910d725a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/background/background-repeat-space-10.html @@ -0,0 +1,25 @@ + + + + + CSS Background: background-repeat: background image space + + + + + + + + +
+ + + diff --git a/layout/reftests/w3c-css/submitted/background/background-repeat-space-9-ref.html b/layout/reftests/w3c-css/submitted/background/background-repeat-space-9-ref.html new file mode 100644 index 000000000000..1476eca646f7 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/background/background-repeat-space-9-ref.html @@ -0,0 +1,41 @@ + + + + + CSS Background: background-repeat: position background image + + + + + +
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/layout/reftests/w3c-css/submitted/background/background-repeat-space-9.html b/layout/reftests/w3c-css/submitted/background/background-repeat-space-9.html new file mode 100644 index 000000000000..66f6c225890a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/background/background-repeat-space-9.html @@ -0,0 +1,25 @@ + + + + + CSS Background: background-repeat: background image space + + + + + + + + +
+ + + diff --git a/layout/reftests/w3c-css/submitted/background/reftest.list b/layout/reftests/w3c-css/submitted/background/reftest.list index 9808ac45e672..707929d00979 100644 --- a/layout/reftests/w3c-css/submitted/background/reftest.list +++ b/layout/reftests/w3c-css/submitted/background/reftest.list @@ -9,6 +9,8 @@ == background-repeat-space-6.html background-repeat-space-6-ref.html == background-repeat-space-7.html background-repeat-space-7-ref.html == background-repeat-space-8.html background-repeat-space-8-ref.html +== background-repeat-space-9.html background-repeat-space-9-ref.html +== background-repeat-space-10.html background-repeat-space-10-ref.html == background-repeat-round-1a.html background-repeat-round-1-ref.html == background-repeat-round-1b.html background-repeat-round-1-ref.html == background-repeat-round-1c.html background-repeat-round-1-ref.html From d466f0aab2eb6a6a57174b6805b3b04e92ae3d8d Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 31 May 2016 17:22:39 +0200 Subject: [PATCH 036/199] Bug 1048291 - Deprecate File::lastModifiedDate, r=smaug --- dom/base/nsDeprecatedOperationList.h | 1 + dom/locales/en-US/chrome/dom/dom.properties | 1 + dom/webidl/File.webidl | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/base/nsDeprecatedOperationList.h b/dom/base/nsDeprecatedOperationList.h index 885f25244ceb..aad87ba8aa9d 100644 --- a/dom/base/nsDeprecatedOperationList.h +++ b/dom/base/nsDeprecatedOperationList.h @@ -47,3 +47,4 @@ DEPRECATED_OPERATION(AppCache) DEPRECATED_OPERATION(PrefixedFullscreenAPI) DEPRECATED_OPERATION(LenientSetter) DEPRECATED_OPERATION(NavigatorBattery) +DEPRECATED_OPERATION(FileLastModifiedDate) diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index b4dd3faed132..e8e0c1c00421 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -229,3 +229,4 @@ PushMessageDecryptionFailure=The ServiceWorker for scope ‘%1$S’ encountered # LOCALIZATION NOTE: %1$S is the type of a DOM event. 'passive' is a literal parameter from the DOM spec. PreventDefaultFromPassiveListenerWarning=Ignoring ‘preventDefault()’ call on event of type ‘%1$S’ from a listener registered as ‘passive’. NavigatorBatteryWarning=navigator.battery is deprecated. Use navigator.getBattery() instead. +FileLastModifiedDateWarning=File.lastModifiedDate is deprecated. Use File.lastModified instead. diff --git a/dom/webidl/File.webidl b/dom/webidl/File.webidl index a6b42c70c9d4..31f0aecf2923 100644 --- a/dom/webidl/File.webidl +++ b/dom/webidl/File.webidl @@ -41,7 +41,7 @@ dictionary ChromeFilePropertyBag : FilePropertyBag { // Mozilla extensions partial interface File { - [GetterThrows] + [GetterThrows, Deprecated="FileLastModifiedDate"] readonly attribute Date lastModifiedDate; [BinaryName="path", Func="mozilla::dom::Directory::WebkitBlinkDirectoryPickerEnabled"] From 93397ba994e77e3fab7cb6fdd5dc0cf95e9f5e88 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 31 May 2016 17:23:57 +0200 Subject: [PATCH 037/199] Bug 1276874 - Add spec URLs in File and Blob webidl files, r=smaug --- dom/webidl/Blob.webidl | 4 +--- dom/webidl/File.webidl | 18 +++++++----------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/dom/webidl/Blob.webidl b/dom/webidl/Blob.webidl index b9314c7f10f1..78781e85054d 100644 --- a/dom/webidl/Blob.webidl +++ b/dom/webidl/Blob.webidl @@ -32,11 +32,9 @@ interface Blob { // void close(); TODO bug 1048325 }; -enum EndingTypes{"transparent", "native"}; +enum EndingTypes { "transparent", "native" }; dictionary BlobPropertyBag { - DOMString type = ""; EndingTypes endings = "transparent"; - }; diff --git a/dom/webidl/File.webidl b/dom/webidl/File.webidl index 31f0aecf2923..73f9a3870df7 100644 --- a/dom/webidl/File.webidl +++ b/dom/webidl/File.webidl @@ -2,6 +2,9 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * https://w3c.github.io/FileAPI/#file */ interface nsIFile; @@ -16,31 +19,24 @@ interface nsIFile; Exposed=(Window,Worker)] interface File : Blob { - readonly attribute DOMString name; [GetterThrows] readonly attribute long long lastModified; - }; - dictionary FilePropertyBag { - - DOMString type = ""; - long long lastModified; - + DOMString type = ""; + long long lastModified; }; dictionary ChromeFilePropertyBag : FilePropertyBag { - - DOMString name = ""; - boolean temporary = false; + DOMString name = ""; + boolean temporary = false; }; // Mozilla extensions partial interface File { - [GetterThrows, Deprecated="FileLastModifiedDate"] readonly attribute Date lastModifiedDate; From 0f69ca549fe35ce4660061ca52037b6bd1e27d12 Mon Sep 17 00:00:00 2001 From: Mason Chang Date: Tue, 31 May 2016 08:28:42 -0700 Subject: [PATCH 038/199] Bug 1046205. Fix depth pitch read out of bounds. r=bas --- gfx/layers/d3d11/TextureD3D11.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index fb028bb83d19..86907a8a1fef 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -935,7 +935,7 @@ DataTextureSourceD3D11::Update(DataSourceSurface* aSurface, void* data = map.mData + map.mStride * rect.y + BytesPerPixel(aSurface->GetFormat()) * rect.x; - mCompositor->GetDC()->UpdateSubresource(mTexture, 0, &box, data, map.mStride, map.mStride * mSize.height); + mCompositor->GetDC()->UpdateSubresource(mTexture, 0, &box, data, map.mStride, map.mStride * rect.height); } } else { mCompositor->GetDC()->UpdateSubresource(mTexture, 0, nullptr, aSurface->GetData(), From 5f4030b30a95bb48309540e0c2a362494aa916a7 Mon Sep 17 00:00:00 2001 From: Mason Chang Date: Thu, 26 May 2016 16:34:21 -0700 Subject: [PATCH 039/199] Bug 1211647. Use ::ShowWindow instead of ::ShowWindowPos for toplevel windows widgets that can't take focus. r=jimm --- widget/windows/nsWindow.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index fae6491c0e40..22f707f1d228 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -1257,15 +1257,7 @@ NS_METHOD nsWindow::Show(bool bState) if (CanTakeFocus()) { ::ShowWindow(mWnd, SW_SHOWNORMAL); } else { - // Place the window behind the foreground window - // (as long as it is not topmost) - HWND wndAfter = ::GetForegroundWindow(); - if (!wndAfter) - wndAfter = HWND_BOTTOM; - else if (GetWindowLongPtrW(wndAfter, GWL_EXSTYLE) & WS_EX_TOPMOST) - wndAfter = HWND_TOP; - ::SetWindowPos(mWnd, wndAfter, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | - SWP_NOMOVE | SWP_NOACTIVATE); + ::ShowWindow(mWnd, SW_SHOWNOACTIVATE); GetAttention(2); } break; From e7015103b0bde13f9ace6772d6d58944ddab2789 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 31 May 2016 17:46:52 +0200 Subject: [PATCH 040/199] Bug 1276887 - webidl "Deprecated" keyword should work in workers, r=smaug --- dom/bindings/BindingUtils.cpp | 118 +++++++++++++++++++++++++++++++++- dom/bindings/Exceptions.cpp | 2 +- 2 files changed, 116 insertions(+), 4 deletions(-) diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index ec2ac207c49a..2ad824fe4f08 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -26,6 +26,8 @@ #include "nsIPrincipal.h" #include "nsIXPConnect.h" #include "nsUTF8Utils.h" +#include "WorkerPrivate.h" +#include "WorkerRunnable.h" #include "WrapperFactory.h" #include "xpcprivate.h" #include "XrayWrapper.h" @@ -56,6 +58,8 @@ namespace mozilla { namespace dom { +using namespace workers; + const JSErrorFormatString ErrorFormatString[] = { #define MSG_DEF(_name, _argc, _exn, _str) \ { #_name, _str, _argc, _exn }, @@ -3285,6 +3289,101 @@ SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject, } } +namespace { + +// This runnable is used to write a deprecation message from a worker to the +// console running on the main-thread. +class DeprecationWarningRunnable final : public Runnable + , public WorkerFeature +{ + WorkerPrivate* mWorkerPrivate; + nsIDocument::DeprecatedOperations mOperation; + +public: + DeprecationWarningRunnable(WorkerPrivate* aWorkerPrivate, + nsIDocument::DeprecatedOperations aOperation) + : mWorkerPrivate(aWorkerPrivate) + , mOperation(aOperation) + { + MOZ_ASSERT(aWorkerPrivate); + } + + void + Dispatch() + { + if (NS_WARN_IF(!mWorkerPrivate->AddFeature(this))) { + return; + } + + if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) { + mWorkerPrivate->RemoveFeature(this); + return; + } + } + + virtual bool + Notify(workers::Status aStatus) override + { + // We don't care about the notification. We just want to keep the + // mWorkerPrivate alive. + return true; + } + +private: + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + + // Walk up to our containing page + WorkerPrivate* wp = mWorkerPrivate; + while (wp->GetParent()) { + wp = wp->GetParent(); + } + + nsPIDOMWindowInner* window = wp->GetWindow(); + if (window && window->GetExtantDoc()) { + window->GetExtantDoc()->WarnOnceAbout(mOperation); + } + + ReleaseWorker(); + return NS_OK; + } + + void + ReleaseWorker() + { + class ReleaseRunnable final : public WorkerRunnable + { + RefPtr mRunnable; + + public: + ReleaseRunnable(WorkerPrivate* aWorkerPrivate, + DeprecationWarningRunnable* aRunnable) + : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) + , mRunnable(aRunnable) + {} + + virtual bool + WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + aWorkerPrivate->RemoveFeature(mRunnable); + return true; + } + }; + + RefPtr runnable = + new ReleaseRunnable(mWorkerPrivate, this); + NS_WARN_IF(!runnable->Dispatch()); + } +}; + +} // anonymous namespace + void DeprecationWarning(JSContext* aCx, JSObject* aObject, nsIDocument::DeprecatedOperations aOperation) @@ -3295,10 +3394,23 @@ DeprecationWarning(JSContext* aCx, JSObject* aObject, return; } - nsCOMPtr window = do_QueryInterface(global.GetAsSupports()); - if (window && window->GetExtantDoc()) { - window->GetExtantDoc()->WarnOnceAbout(aOperation); + if (NS_IsMainThread()) { + nsCOMPtr window = do_QueryInterface(global.GetAsSupports()); + if (window && window->GetExtantDoc()) { + window->GetExtantDoc()->WarnOnceAbout(aOperation); + } + + return; } + + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); + if (!workerPrivate) { + return; + } + + RefPtr runnable = + new DeprecationWarningRunnable(workerPrivate, aOperation); + runnable->Dispatch(); } namespace binding_detail { diff --git a/dom/bindings/Exceptions.cpp b/dom/bindings/Exceptions.cpp index 6b495aa30c25..b85a9ee2995b 100644 --- a/dom/bindings/Exceptions.cpp +++ b/dom/bindings/Exceptions.cpp @@ -203,7 +203,7 @@ GetCurrentJSStack(int32_t aMaxDepth) return nullptr; } - return exceptions::CreateStack(cx, aMaxDepth); + return dom::exceptions::CreateStack(cx, aMaxDepth); } AutoForceSetExceptionOnContext::AutoForceSetExceptionOnContext(JSContext* aCx) From 5ceb9125e1a4554fdf7fb929340f14bdd86fa027 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:17 -0700 Subject: [PATCH 041/199] Bug 1136226 - Provide shared NYI implementations of SIMD visitors. r=bbouvier Since most targets don't implement SIMD and don't enable the generation of SIMD instructions, it makes sense to provide a default implementation of the SIMD visitor functions that simply crashes with an NYI error. Remove the many identical copies of these visitors from the non-SIMD targets. --- js/src/jit/arm/Lowering-arm.cpp | 24 ------------------- js/src/jit/arm/Lowering-arm.h | 4 ---- js/src/jit/arm64/Lowering-arm64.cpp | 24 ------------------- js/src/jit/arm64/Lowering-arm64.h | 4 ---- .../jit/mips-shared/Lowering-mips-shared.cpp | 24 ------------------- js/src/jit/mips-shared/Lowering-mips-shared.h | 4 ---- js/src/jit/shared/Lowering-shared.h | 11 ++++++++- 7 files changed, 10 insertions(+), 85 deletions(-) diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index f47beb03e5ef..5a968b333202 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -567,30 +567,6 @@ LIRGeneratorARM::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic MOZ_CRASH("NYI"); } -void -LIRGeneratorARM::visitSimdBinaryArith(MSimdBinaryArith* ins) -{ - MOZ_CRASH("NYI"); -} - -void -LIRGeneratorARM::visitSimdSelect(MSimdSelect* ins) -{ - MOZ_CRASH("NYI"); -} - -void -LIRGeneratorARM::visitSimdSplat(MSimdSplat* ins) -{ - MOZ_CRASH("NYI"); -} - -void -LIRGeneratorARM::visitSimdValueX4(MSimdValueX4* ins) -{ - MOZ_CRASH("NYI"); -} - void LIRGeneratorARM::visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins) { diff --git a/js/src/jit/arm/Lowering-arm.h b/js/src/jit/arm/Lowering-arm.h index b4d9b6dd90dd..435b5a6ef3d5 100644 --- a/js/src/jit/arm/Lowering-arm.h +++ b/js/src/jit/arm/Lowering-arm.h @@ -104,10 +104,6 @@ class LIRGeneratorARM : public LIRGeneratorShared void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins); void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins); void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins); - void visitSimdBinaryArith(MSimdBinaryArith* ins); - void visitSimdSelect(MSimdSelect* ins); - void visitSimdSplat(MSimdSplat* ins); - void visitSimdValueX4(MSimdValueX4* ins); void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins); void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins); void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins); diff --git a/js/src/jit/arm64/Lowering-arm64.cpp b/js/src/jit/arm64/Lowering-arm64.cpp index 80b32322f1b4..5995e7ac0e14 100644 --- a/js/src/jit/arm64/Lowering-arm64.cpp +++ b/js/src/jit/arm64/Lowering-arm64.cpp @@ -285,30 +285,6 @@ LIRGeneratorARM64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStat MOZ_CRASH("NYI"); } -void -LIRGeneratorARM64::visitSimdBinaryArith(MSimdBinaryArith* ins) -{ - MOZ_CRASH("NYI"); -} - -void -LIRGeneratorARM64::visitSimdSelect(MSimdSelect* ins) -{ - MOZ_CRASH("NYI"); -} - -void -LIRGeneratorARM64::visitSimdSplat(MSimdSplat* ins) -{ - MOZ_CRASH("NYI"); -} - -void -LIRGeneratorARM64::visitSimdValueX4(MSimdValueX4* ins) -{ - MOZ_CRASH("NYI"); -} - void LIRGeneratorARM64::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins) { diff --git a/js/src/jit/arm64/Lowering-arm64.h b/js/src/jit/arm64/Lowering-arm64.h index bd98fb7df750..25137a424a42 100644 --- a/js/src/jit/arm64/Lowering-arm64.h +++ b/js/src/jit/arm64/Lowering-arm64.h @@ -107,10 +107,6 @@ class LIRGeneratorARM64 : public LIRGeneratorShared void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins); void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins); void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins); - void visitSimdBinaryArith(MSimdBinaryArith* ins); - void visitSimdSelect(MSimdSelect* ins); - void visitSimdSplat(MSimdSplat* ins); - void visitSimdValueX4(MSimdValueX4* ins); void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins); void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins); void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins); diff --git a/js/src/jit/mips-shared/Lowering-mips-shared.cpp b/js/src/jit/mips-shared/Lowering-mips-shared.cpp index a18a77441b5e..08b1e1bd8caa 100644 --- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp +++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp @@ -404,30 +404,6 @@ LIRGeneratorMIPSShared::visitStoreTypedArrayElementStatic(MStoreTypedArrayElemen MOZ_CRASH("NYI"); } -void -LIRGeneratorMIPSShared::visitSimdBinaryArith(MSimdBinaryArith* ins) -{ - MOZ_CRASH("NYI"); -} - -void -LIRGeneratorMIPSShared::visitSimdSelect(MSimdSelect* ins) -{ - MOZ_CRASH("NYI"); -} - -void -LIRGeneratorMIPSShared::visitSimdSplat(MSimdSplat* ins) -{ - MOZ_CRASH("NYI"); -} - -void -LIRGeneratorMIPSShared::visitSimdValueX4(MSimdValueX4* ins) -{ - MOZ_CRASH("NYI"); -} - void LIRGeneratorMIPSShared::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins) { diff --git a/js/src/jit/mips-shared/Lowering-mips-shared.h b/js/src/jit/mips-shared/Lowering-mips-shared.h index 0c5e153838e5..dd8544922dd9 100644 --- a/js/src/jit/mips-shared/Lowering-mips-shared.h +++ b/js/src/jit/mips-shared/Lowering-mips-shared.h @@ -89,10 +89,6 @@ class LIRGeneratorMIPSShared : public LIRGeneratorShared void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins); void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins); void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins); - void visitSimdBinaryArith(MSimdBinaryArith* ins); - void visitSimdSelect(MSimdSelect* ins); - void visitSimdSplat(MSimdSplat* ins); - void visitSimdValueX4(MSimdValueX4* ins); void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins); void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins); void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins); diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index 08f1bb362a3c..bdaaadf68be6 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -254,7 +254,7 @@ class LIRGeneratorShared : public MDefinitionVisitor define(new(alloc()) LFloat32(f), mir); } - void visitConstant(MConstant* ins); + void visitConstant(MConstant* ins) override; // Whether to generate typed reads for element accesses with hole checks. static bool allowTypedElementHoleCheck() { @@ -266,6 +266,15 @@ class LIRGeneratorShared : public MDefinitionVisitor return false; } + // Provide NYI default implementations of the SIMD visitor functions. + // Many targets don't implement SIMD at all, and we don't want to duplicate + // these stubs in the specific sub-classes. + // Some SIMD visitors are implemented in LIRGenerator in Lowering.cpp. These + // shared implementations are not included here. + void visitSimdBinaryArith(MSimdBinaryArith*) override { MOZ_CRASH("NYI"); } + void visitSimdSelect(MSimdSelect*) override { MOZ_CRASH("NYI"); } + void visitSimdSplat(MSimdSplat*) override { MOZ_CRASH("NYI"); } + void visitSimdValueX4(MSimdValueX4*) override { MOZ_CRASH("NYI"); } }; } // namespace jit From 53b831540f901a3be2f44c673180b968799a9d53 Mon Sep 17 00:00:00 2001 From: Jakob Olesen Date: Tue, 31 May 2016 09:00:17 -0700 Subject: [PATCH 042/199] Bug 1136226 - Implement MSimdExtractElement for small integer types. r=bbouvier Move visitSimdExtractElement into x86-specific code in order to set proper register allocation requirements. --- js/src/jit/Lowering.cpp | 38 ------- js/src/jit/Lowering.h | 1 - js/src/jit/shared/LIR-shared.h | 8 +- js/src/jit/x86-shared/Assembler-x86-shared.h | 20 ++-- .../jit/x86-shared/BaseAssembler-x86-shared.h | 15 ++- .../x86-shared/CodeGenerator-x86-shared.cpp | 98 +++++++++++++++++-- .../jit/x86-shared/CodeGenerator-x86-shared.h | 6 +- js/src/jit/x86-shared/Encoding-x86-shared.h | 1 + js/src/jit/x86-shared/Lowering-x86-shared.cpp | 52 ++++++++++ js/src/jit/x86-shared/Lowering-x86-shared.h | 1 + 10 files changed, 166 insertions(+), 74 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 5f84ae6f14b0..1d57c62f3146 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4385,44 +4385,6 @@ LIRGenerator::visitSimdReinterpretCast(MSimdReinterpretCast* ins) define(new(alloc()) LSimdReinterpretCast(use), ins); } -void -LIRGenerator::visitSimdExtractElement(MSimdExtractElement* ins) -{ - MOZ_ASSERT(IsSimdType(ins->input()->type())); - MOZ_ASSERT(!IsSimdType(ins->type())); - - switch (ins->input()->type()) { - case MIRType::Int32x4: { - MOZ_ASSERT(ins->signedness() != SimdSign::NotApplicable); - // Note: there could be int16x8 in the future, which doesn't use the - // same instruction. We either need to pass the arity or create new LIns. - LUse use = useRegisterAtStart(ins->input()); - if (ins->type() == MIRType::Double) { - // Extract an Uint32 lane into a double. - MOZ_ASSERT(ins->signedness() == SimdSign::Unsigned); - define(new (alloc()) LSimdExtractElementU2D(use, temp()), ins); - } else { - define(new (alloc()) LSimdExtractElementI(use), ins); - } - break; - } - case MIRType::Float32x4: { - MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable); - LUse use = useRegisterAtStart(ins->input()); - define(new(alloc()) LSimdExtractElementF(use), ins); - break; - } - case MIRType::Bool32x4: { - MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable); - LUse use = useRegisterAtStart(ins->input()); - define(new(alloc()) LSimdExtractElementB(use), ins); - break; - } - default: - MOZ_CRASH("Unknown SIMD kind when extracting element"); - } -} - void LIRGenerator::visitSimdInsertElement(MSimdInsertElement* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 36b403a64452..b6e354680f32 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -294,7 +294,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitRecompileCheck(MRecompileCheck* ins); void visitSimdBox(MSimdBox* ins); void visitSimdUnbox(MSimdUnbox* ins); - void visitSimdExtractElement(MSimdExtractElement* ins); void visitSimdInsertElement(MSimdInsertElement* ins); void visitSimdSwizzle(MSimdSwizzle* ins); void visitSimdGeneralShuffle(MSimdGeneralShuffle* ins); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index e8feb309f939..9068cacb1a16 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -236,8 +236,8 @@ class LSimdExtractElementBase : public LInstructionHelper<1, 1, 0> const LAllocation* getBase() { return getOperand(0); } - unsigned lane() const { - return mir_->toSimdExtractElement()->lane(); + MSimdExtractElement* mir() const { + return mir_->toSimdExtractElement(); } }; @@ -280,8 +280,8 @@ class LSimdExtractElementU2D : public LInstructionHelper<1, 1, 1> setOperand(0, base); setTemp(0, temp); } - unsigned lane() const { - return mir_->toSimdExtractElement()->lane(); + MSimdExtractElement* mir() const { + return mir_->toSimdExtractElement(); } const LDefinition* temp() { return getTemp(0); diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index 451f1a0a20a6..d193c31e1e27 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -2123,23 +2123,17 @@ class AssemblerX86Shared : public AssemblerShared MOZ_CRASH("unexpected operand kind"); } } + void vpextrb(unsigned lane, FloatRegister src, Register dest) { + MOZ_ASSERT(HasSSE41()); + masm.vpextrb_irr(lane, src.encoding(), dest.encoding()); + } + void vpextrw(unsigned lane, FloatRegister src, Register dest) { + masm.vpextrw_irr(lane, src.encoding(), dest.encoding()); + } void vpextrd(unsigned lane, FloatRegister src, Register dest) { MOZ_ASSERT(HasSSE41()); masm.vpextrd_irr(lane, src.encoding(), dest.encoding()); } - void vpextrd(unsigned lane, FloatRegister src, const Operand& dest) { - MOZ_ASSERT(HasSSE41()); - switch (dest.kind()) { - case Operand::REG: - masm.vpextrd_irr(lane, src.encoding(), dest.reg()); - break; - case Operand::MEM_REG_DISP: - masm.vpextrd_irm(lane, src.encoding(), dest.disp(), dest.base()); - break; - default: - MOZ_CRASH("unexpected operand kind"); - } - } void vpsrldq(Imm32 shift, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); masm.vpsrldq_ir(shift.value, src0.encoding(), dest.encoding()); diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index 00fa2856a3d3..e27b9f2bc828 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -3262,21 +3262,18 @@ public: threeByteOpImmInt32Simd("vpinsrd", VEX_PD, OP3_PINSRD_VdqEdIb, ESCAPE_3A, lane, offset, base, src0, dst); } + void vpextrb_irr(unsigned lane, XMMRegisterID src, RegisterID dst) + { + MOZ_ASSERT(lane < 16); + threeByteOpImmSimdInt32("vpextrb", VEX_PD, OP3_PEXTRB_EdVdqIb, ESCAPE_3A, lane, (XMMRegisterID)dst, (RegisterID)src); + } + void vpextrd_irr(unsigned lane, XMMRegisterID src, RegisterID dst) { MOZ_ASSERT(lane < 4); threeByteOpImmSimdInt32("vpextrd", VEX_PD, OP3_PEXTRD_EdVdqIb, ESCAPE_3A, lane, (XMMRegisterID)dst, (RegisterID)src); } - void vpextrd_irm(unsigned lane, XMMRegisterID src, int32_t offset, RegisterID base) - { - MOZ_ASSERT(lane < 4); - spew("pextrd $0x%x, %s, " MEM_ob, lane, XMMRegName(src), ADDR_ob(offset, base)); - m_formatter.prefix(PRE_SSE_66); - m_formatter.threeByteOp(OP3_PEXTRD_EdVdqIb, ESCAPE_3A, offset, base, (RegisterID)src); - m_formatter.immediate8u(lane); - } - void vblendps_irr(unsigned imm, XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { MOZ_ASSERT(imm < 16); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index f7a9d54c9438..fb618e68951e 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2647,9 +2647,10 @@ CodeGeneratorX86Shared::visitSimdReinterpretCast(LSimdReinterpretCast* ins) } } -// Extract an integer lane from the vector register |input| and place it in |output|. +// Extract an integer lane from the 32x4 vector register |input| and place it in +// |output|. void -CodeGeneratorX86Shared::emitSimdExtractLane(FloatRegister input, Register output, unsigned lane) +CodeGeneratorX86Shared::emitSimdExtractLane32x4(FloatRegister input, Register output, unsigned lane) { if (lane == 0) { // The value we want to extract is in the low double-word @@ -2663,15 +2664,81 @@ CodeGeneratorX86Shared::emitSimdExtractLane(FloatRegister input, Register output } } +// Extract an integer lane from the 16x8 vector register |input|, sign- or +// zero-extend to 32 bits and place the result in |output|. +void +CodeGeneratorX86Shared::emitSimdExtractLane16x8(FloatRegister input, Register output, + unsigned lane, SimdSign signedness) +{ + // Unlike pextrd and pextrb, this is available in SSE2. + masm.vpextrw(lane, input, output); + + if (signedness == SimdSign::Signed) + masm.movswl(output, output); +} + +// Extract an integer lane from the 8x16 vector register |input|, sign- or +// zero-extend to 32 bits and place the result in |output|. +void +CodeGeneratorX86Shared::emitSimdExtractLane8x16(FloatRegister input, Register output, + unsigned lane, SimdSign signedness) +{ + if (AssemblerX86Shared::HasSSE41()) { + masm.vpextrb(lane, input, output); + // vpextrb clears the high bits, so no further extension required. + if (signedness == SimdSign::Unsigned) + signedness = SimdSign::NotApplicable; + } else { + // Extract the relevant 16 bits containing our lane, then shift the + // right 8 bits into place. + emitSimdExtractLane16x8(input, output, lane / 2, SimdSign::Unsigned); + if (lane % 2) { + masm.shrl(Imm32(8), output); + // The shrl handles the zero-extension. Don't repeat it. + if (signedness == SimdSign::Unsigned) + signedness = SimdSign::NotApplicable; + } + } + + // We have the right low 8 bits in |output|, but we may need to fix the high + // bits. + switch (signedness) { + case SimdSign::Signed: + masm.movsbl(output, output); + break; + case SimdSign::Unsigned: + masm.movzbl(output, output); + break; + case SimdSign::NotApplicable: + // No adjustment needed. + break; + } +} + void CodeGeneratorX86Shared::visitSimdExtractElementB(LSimdExtractElementB* ins) { FloatRegister input = ToFloatRegister(ins->input()); Register output = ToRegister(ins->output()); + MSimdExtractElement* mir = ins->mir(); + unsigned length = SimdTypeToLength(mir->specialization()); - emitSimdExtractLane(input, output, ins->lane()); + switch (length) { + case 4: + emitSimdExtractLane32x4(input, output, mir->lane()); + break; + case 8: + // Get a lane, don't bother fixing the high bits since we'll mask below. + emitSimdExtractLane16x8(input, output, mir->lane(), SimdSign::NotApplicable); + break; + case 16: + emitSimdExtractLane8x16(input, output, mir->lane(), SimdSign::NotApplicable); + break; + default: + MOZ_CRASH("Unhandled SIMD length"); + } - // We need to generate a 0/1 value. We have 0/-1. + // We need to generate a 0/1 value. We have 0/-1 and possibly dirty high bits. masm.and32(Imm32(1), output); } @@ -2680,8 +2747,22 @@ CodeGeneratorX86Shared::visitSimdExtractElementI(LSimdExtractElementI* ins) { FloatRegister input = ToFloatRegister(ins->input()); Register output = ToRegister(ins->output()); + MSimdExtractElement* mir = ins->mir(); + unsigned length = SimdTypeToLength(mir->specialization()); - emitSimdExtractLane(input, output, ins->lane()); + switch (length) { + case 4: + emitSimdExtractLane32x4(input, output, mir->lane()); + break; + case 8: + emitSimdExtractLane16x8(input, output, mir->lane(), mir->signedness()); + break; + case 16: + emitSimdExtractLane8x16(input, output, mir->lane(), mir->signedness()); + break; + default: + MOZ_CRASH("Unhandled SIMD length"); + } } void @@ -2690,8 +2771,9 @@ CodeGeneratorX86Shared::visitSimdExtractElementU2D(LSimdExtractElementU2D* ins) FloatRegister input = ToFloatRegister(ins->input()); FloatRegister output = ToFloatRegister(ins->output()); Register temp = ToRegister(ins->temp()); - - emitSimdExtractLane(input, temp, ins->lane()); + MSimdExtractElement* mir = ins->mir(); + MOZ_ASSERT(mir->specialization() == MIRType::Int32x4); + emitSimdExtractLane32x4(input, temp, mir->lane()); masm.convertUInt32ToDouble(temp, output); } @@ -2701,7 +2783,7 @@ CodeGeneratorX86Shared::visitSimdExtractElementF(LSimdExtractElementF* ins) FloatRegister input = ToFloatRegister(ins->input()); FloatRegister output = ToFloatRegister(ins->output()); - unsigned lane = ins->lane(); + unsigned lane = ins->mir()->lane(); if (lane == 0) { // The value we want to extract is in the low double-word if (input != output) diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 132ae5a2f224..ad200f3470ce 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -208,7 +208,11 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base); - void emitSimdExtractLane(FloatRegister input, Register output, unsigned lane); + void emitSimdExtractLane8x16(FloatRegister input, Register output, unsigned lane, + SimdSign signedness); + void emitSimdExtractLane16x8(FloatRegister input, Register output, unsigned lane, + SimdSign signedness); + void emitSimdExtractLane32x4(FloatRegister input, Register output, unsigned lane); public: CodeGeneratorX86Shared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm); diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h index 6e462391f306..3f3294968636 100644 --- a/js/src/jit/x86-shared/Encoding-x86-shared.h +++ b/js/src/jit/x86-shared/Encoding-x86-shared.h @@ -266,6 +266,7 @@ enum ThreeByteOpcodeID { OP3_ROUNDSS_VsdWsd = 0x0A, OP3_ROUNDSD_VsdWsd = 0x0B, OP3_BLENDVPS_VdqWdq = 0x14, + OP3_PEXTRB_EdVdqIb = 0x14, OP3_PEXTRD_EdVdqIb = 0x16, OP3_BLENDPS_VpsWpsIb = 0x0C, OP3_PTEST_VdVd = 0x17, diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index a9123b5e1002..38a970cd5003 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -634,6 +634,58 @@ LIRGeneratorX86Shared::lowerAtomicTypedArrayElementBinop(MAtomicTypedArrayElemen define(lir, ins); } +void +LIRGeneratorX86Shared::visitSimdExtractElement(MSimdExtractElement* ins) +{ + MOZ_ASSERT(IsSimdType(ins->input()->type())); + MOZ_ASSERT(!IsSimdType(ins->type())); + + switch (ins->input()->type()) { + case MIRType::Int8x16: + case MIRType::Int16x8: + case MIRType::Int32x4: { + MOZ_ASSERT(ins->signedness() != SimdSign::NotApplicable); + LUse use = useRegisterAtStart(ins->input()); + if (ins->type() == MIRType::Double) { + // Extract an Uint32 lane into a double. + MOZ_ASSERT(ins->signedness() == SimdSign::Unsigned); + define(new (alloc()) LSimdExtractElementU2D(use, temp()), ins); + } else { + auto* lir = new (alloc()) LSimdExtractElementI(use); +#if defined(JS_CODEGEN_X86) + // On x86 (32-bit), we may need to use movsbl or movzbl instructions + // to sign or zero extend the extracted lane to 32 bits. The 8-bit + // version of these instructions require a source register that is + // %al, %bl, %cl, or %dl. + // Fix it to %ebx since we can't express that constraint better. + if (ins->input()->type() == MIRType::Int8x16) { + defineFixed(lir, ins, LAllocation(AnyRegister(ebx))); + return; + } +#endif + define(lir, ins); + } + break; + } + case MIRType::Float32x4: { + MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable); + LUse use = useRegisterAtStart(ins->input()); + define(new(alloc()) LSimdExtractElementF(use), ins); + break; + } + case MIRType::Bool8x16: + case MIRType::Bool16x8: + case MIRType::Bool32x4: { + MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable); + LUse use = useRegisterAtStart(ins->input()); + define(new(alloc()) LSimdExtractElementB(use), ins); + break; + } + default: + MOZ_CRASH("Unknown SIMD kind when extracting element"); + } +} + void LIRGeneratorX86Shared::visitSimdBinaryArith(MSimdBinaryArith* ins) { diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.h b/js/src/jit/x86-shared/Lowering-x86-shared.h index 6809f3129f1c..345da0eb3b48 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.h +++ b/js/src/jit/x86-shared/Lowering-x86-shared.h @@ -56,6 +56,7 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared void lowerUrshD(MUrsh* mir); void lowerTruncateDToInt32(MTruncateToInt32* ins); void lowerTruncateFToInt32(MTruncateToInt32* ins); + void visitSimdExtractElement(MSimdExtractElement* ins); void visitSimdBinaryArith(MSimdBinaryArith* ins); void visitSimdSelect(MSimdSelect* ins); void visitSimdSplat(MSimdSplat* ins); From 43fd82ad3eeaa5dfa31249eae3f5aa7aeb96a812 Mon Sep 17 00:00:00 2001 From: Jakob Olesen Date: Tue, 31 May 2016 09:00:18 -0700 Subject: [PATCH 043/199] Bug 1136226 - Implement MSimdInsertElement for small integer types. r=bbouvier Use vpinsrw to insert 16x8 lanes. This instruction is available since SSE2, so it can be used unconditionally. Move visitSimdInsertElement into x86-specific code in order to set the proper register allocation constraints. --- js/src/jit/Lowering.cpp | 20 ----------- js/src/jit/Lowering.h | 1 - js/src/jit/shared/LIR-shared.h | 3 ++ js/src/jit/shared/Lowering-shared.h | 2 ++ js/src/jit/x86-shared/Assembler-x86-shared.h | 22 +++++------- .../jit/x86-shared/BaseAssembler-x86-shared.h | 34 ++++++++++++++---- .../x86-shared/CodeGenerator-x86-shared.cpp | 35 ++++++++++++++++--- js/src/jit/x86-shared/Encoding-x86-shared.h | 2 ++ js/src/jit/x86-shared/Lowering-x86-shared.cpp | 33 +++++++++++++++++ js/src/jit/x86-shared/Lowering-x86-shared.h | 1 + 10 files changed, 108 insertions(+), 45 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 1d57c62f3146..a0bdb477731d 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4385,26 +4385,6 @@ LIRGenerator::visitSimdReinterpretCast(MSimdReinterpretCast* ins) define(new(alloc()) LSimdReinterpretCast(use), ins); } -void -LIRGenerator::visitSimdInsertElement(MSimdInsertElement* ins) -{ - MOZ_ASSERT(IsSimdType(ins->type())); - - LUse vec = useRegisterAtStart(ins->vector()); - LUse val = useRegister(ins->value()); - switch (ins->type()) { - case MIRType::Int32x4: - case MIRType::Bool32x4: - defineReuseInput(new(alloc()) LSimdInsertElementI(vec, val), ins, 0); - break; - case MIRType::Float32x4: - defineReuseInput(new(alloc()) LSimdInsertElementF(vec, val), ins, 0); - break; - default: - MOZ_CRASH("Unknown SIMD kind when generating constant"); - } -} - void LIRGenerator::visitSimdAllTrue(MSimdAllTrue* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index b6e354680f32..41f2a89e220b 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -294,7 +294,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitRecompileCheck(MRecompileCheck* ins); void visitSimdBox(MSimdBox* ins); void visitSimdUnbox(MSimdUnbox* ins); - void visitSimdInsertElement(MSimdInsertElement* ins); void visitSimdSwizzle(MSimdSwizzle* ins); void visitSimdGeneralShuffle(MSimdGeneralShuffle* ins); void visitSimdShuffle(MSimdShuffle* ins); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 9068cacb1a16..0e9b7283401c 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -308,6 +308,9 @@ class LSimdInsertElementBase : public LInstructionHelper<1, 2, 0> unsigned lane() const { return mir_->toSimdInsertElement()->lane(); } + unsigned length() const { + return SimdTypeToLength(mir_->toSimdInsertElement()->type()); + } }; // Replace an element from a given SIMD integer or boolean lane with a given value. diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index bdaaadf68be6..b75b696b1a30 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -271,6 +271,8 @@ class LIRGeneratorShared : public MDefinitionVisitor // these stubs in the specific sub-classes. // Some SIMD visitors are implemented in LIRGenerator in Lowering.cpp. These // shared implementations are not included here. + void visitSimdInsertElement(MSimdInsertElement*) override { MOZ_CRASH("NYI"); } + void visitSimdExtractElement(MSimdExtractElement*) override { MOZ_CRASH("NYI"); } void visitSimdBinaryArith(MSimdBinaryArith*) override { MOZ_CRASH("NYI"); } void visitSimdSelect(MSimdSelect*) override { MOZ_CRASH("NYI"); } void visitSimdSplat(MSimdSplat*) override { MOZ_CRASH("NYI"); } diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index d193c31e1e27..0d7c90c0ba2a 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -2106,23 +2106,19 @@ class AssemblerX86Shared : public AssemblerShared masm.divl_r(divisor.encoding()); } + void vpinsrb(unsigned lane, Register src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE41()); + masm.vpinsrb_irr(lane, src1.encoding(), src0.encoding(), dest.encoding()); + } + void vpinsrw(unsigned lane, Register src1, FloatRegister src0, FloatRegister dest) { + masm.vpinsrw_irr(lane, src1.encoding(), src0.encoding(), dest.encoding()); + } + void vpinsrd(unsigned lane, Register src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE41()); masm.vpinsrd_irr(lane, src1.encoding(), src0.encoding(), dest.encoding()); } - void vpinsrd(unsigned lane, const Operand& src1, FloatRegister src0, FloatRegister dest) { - MOZ_ASSERT(HasSSE41()); - switch (src1.kind()) { - case Operand::REG: - masm.vpinsrd_irr(lane, src1.reg(), src0.encoding(), dest.encoding()); - break; - case Operand::MEM_REG_DISP: - masm.vpinsrd_imr(lane, src1.disp(), src1.base(), src0.encoding(), dest.encoding()); - break; - default: - MOZ_CRASH("unexpected operand kind"); - } - } + void vpextrb(unsigned lane, FloatRegister src, Register dest) { MOZ_ASSERT(HasSSE41()); masm.vpextrb_irr(lane, src.encoding(), dest.encoding()); diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index e27b9f2bc828..3b8132a8cbc4 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -3085,6 +3085,12 @@ public: twoByteOpSimd("vmulss", VEX_SS, OP2_MULSD_VsdWsd, offset, base, src0, dst); } + void vpinsrw_irr(uint32_t whichWord, RegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + MOZ_ASSERT(whichWord < 8); + twoByteOpImmInt32Simd("vpinsrw", VEX_PD, OP2_PINSRW, whichWord, src1, src0, dst); + } + void vpextrw_irr(uint32_t whichWord, XMMRegisterID src, RegisterID dst) { MOZ_ASSERT(whichWord < 8); @@ -3250,18 +3256,18 @@ public: threeByteOpImmSimd("vinsertps", VEX_PD, OP3_INSERTPS_VpsUps, ESCAPE_3A, mask, offset, base, src0, dst); } + void vpinsrb_irr(unsigned lane, RegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + MOZ_ASSERT(lane < 16); + threeByteOpImmInt32Simd("vpinsrb", VEX_PD, OP3_PINSRB_VdqEdIb, ESCAPE_3A, lane, src1, src0, dst); + } + void vpinsrd_irr(unsigned lane, RegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { MOZ_ASSERT(lane < 4); threeByteOpImmInt32Simd("vpinsrd", VEX_PD, OP3_PINSRD_VdqEdIb, ESCAPE_3A, lane, src1, src0, dst); } - void vpinsrd_imr(unsigned lane, int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) - { - MOZ_ASSERT(lane < 4); - threeByteOpImmInt32Simd("vpinsrd", VEX_PD, OP3_PINSRD_VdqEdIb, ESCAPE_3A, lane, offset, base, src0, dst); - } - void vpextrb_irr(unsigned lane, XMMRegisterID src, RegisterID dst) { MOZ_ASSERT(lane < 16); @@ -3853,6 +3859,22 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off m_formatter.immediate8u(imm); } + void twoByteOpImmInt32Simd(const char* name, VexOperandType ty, TwoByteOpcodeID opcode, + uint32_t imm, RegisterID rm, XMMRegisterID src0, XMMRegisterID dst) + { + if (useLegacySSEEncodingForOtherOutput()) { + spew("%-11s$0x%x, %s, %s", legacySSEOpName(name), imm, GPReg32Name(rm), XMMRegName(dst)); + m_formatter.legacySSEPrefix(ty); + m_formatter.twoByteOp(opcode, rm, dst); + m_formatter.immediate8u(imm); + return; + } + + spew("%-11s$0x%x, %s, %s", name, imm, GPReg32Name(rm), XMMRegName(dst)); + m_formatter.twoByteOpVex(ty, opcode, rm, src0, dst); + m_formatter.immediate8u(imm); + } + void twoByteOpSimdFlags(const char* name, VexOperandType ty, TwoByteOpcodeID opcode, XMMRegisterID rm, XMMRegisterID reg) { diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index fb618e68951e..fe6edc21b278 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2701,7 +2701,8 @@ CodeGeneratorX86Shared::emitSimdExtractLane8x16(FloatRegister input, Register ou } // We have the right low 8 bits in |output|, but we may need to fix the high - // bits. + // bits. Note that this requires |output| to be one of the %eax-%edx + // registers. switch (signedness) { case SimdSign::Signed: masm.movsbl(output, output); @@ -2810,20 +2811,44 @@ CodeGeneratorX86Shared::visitSimdInsertElementI(LSimdInsertElementI* ins) FloatRegister output = ToFloatRegister(ins->output()); MOZ_ASSERT(vector == output); // defineReuseInput(0) - unsigned component = unsigned(ins->lane()); + unsigned lane = ins->lane(); + unsigned length = ins->length(); + + if (length == 8) { + // Available in SSE 2. + masm.vpinsrw(lane, value, vector, output); + return; + } // Note that, contrarily to float32x4, we cannot use vmovd if the inserted // value goes into the first component, as vmovd clears out the higher lanes // of the output. if (AssemblerX86Shared::HasSSE41()) { // TODO: Teach Lowering that we don't need defineReuseInput if we have AVX. - masm.vpinsrd(component, value, vector, output); - return; + switch (length) { + case 4: + masm.vpinsrd(lane, value, vector, output); + return; + case 16: + masm.vpinsrb(lane, value, vector, output); + return; + } } masm.reserveStack(Simd128DataSize); masm.storeAlignedSimd128Int(vector, Address(StackPointer, 0)); - masm.store32(value, Address(StackPointer, component * sizeof(int32_t))); + switch (length) { + case 4: + masm.store32(value, Address(StackPointer, lane * sizeof(int32_t))); + break; + case 16: + // Note that this requires `value` to be in one the registers where the + // low 8 bits are addressible (%eax - %edx on x86, all of them on x86-64). + masm.store8(value, Address(StackPointer, lane * sizeof(int8_t))); + break; + default: + MOZ_CRASH("Unsupported SIMD length"); + } masm.loadAlignedSimd128Int(Address(StackPointer, 0), output); masm.freeStack(Simd128DataSize); } diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h index 3f3294968636..20e1f5943d34 100644 --- a/js/src/jit/x86-shared/Encoding-x86-shared.h +++ b/js/src/jit/x86-shared/Encoding-x86-shared.h @@ -247,6 +247,7 @@ enum TwoByteOpcodeID { OP2_XADD_EbGb = 0xC0, OP2_XADD_EvGv = 0xC1, OP2_CMPPS_VpsWps = 0xC2, + OP2_PINSRW = 0xC4, OP2_PEXTRW_GdUdIb = 0xC5, OP2_SHUFPS_VpsWpsIb = 0xC6, OP2_PSRLD_VdqWdq = 0xD2, @@ -270,6 +271,7 @@ enum ThreeByteOpcodeID { OP3_PEXTRD_EdVdqIb = 0x16, OP3_BLENDPS_VpsWpsIb = 0x0C, OP3_PTEST_VdVd = 0x17, + OP3_PINSRB_VdqEdIb = 0x20, OP3_INSERTPS_VpsUps = 0x21, OP3_PINSRD_VdqEdIb = 0x22, OP3_PMULLD_VdqWdq = 0x40, diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index 38a970cd5003..229020e066fe 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -634,6 +634,39 @@ LIRGeneratorX86Shared::lowerAtomicTypedArrayElementBinop(MAtomicTypedArrayElemen define(lir, ins); } +void +LIRGeneratorX86Shared::visitSimdInsertElement(MSimdInsertElement* ins) +{ + MOZ_ASSERT(IsSimdType(ins->type())); + + LUse vec = useRegisterAtStart(ins->vector()); + LUse val = useRegister(ins->value()); + switch (ins->type()) { + case MIRType::Int8x16: + case MIRType::Bool8x16: + // When SSE 4.1 is not available, we need to go via the stack. + // This requires the value to be inserted to be in %eax-%edx. + // Pick %ebx since other instructions use %eax or %ecx hard-wired. +#if defined(JS_CODEGEN_X86) + if (!AssemblerX86Shared::HasSSE41()) + val = useFixed(ins->value(), ebx); +#endif + defineReuseInput(new(alloc()) LSimdInsertElementI(vec, val), ins, 0); + break; + case MIRType::Int16x8: + case MIRType::Int32x4: + case MIRType::Bool16x8: + case MIRType::Bool32x4: + defineReuseInput(new(alloc()) LSimdInsertElementI(vec, val), ins, 0); + break; + case MIRType::Float32x4: + defineReuseInput(new(alloc()) LSimdInsertElementF(vec, val), ins, 0); + break; + default: + MOZ_CRASH("Unknown SIMD kind when generating constant"); + } +} + void LIRGeneratorX86Shared::visitSimdExtractElement(MSimdExtractElement* ins) { diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.h b/js/src/jit/x86-shared/Lowering-x86-shared.h index 345da0eb3b48..60e850d99a43 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.h +++ b/js/src/jit/x86-shared/Lowering-x86-shared.h @@ -56,6 +56,7 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared void lowerUrshD(MUrsh* mir); void lowerTruncateDToInt32(MTruncateToInt32* ins); void lowerTruncateFToInt32(MTruncateToInt32* ins); + void visitSimdInsertElement(MSimdInsertElement* ins); void visitSimdExtractElement(MSimdExtractElement* ins); void visitSimdBinaryArith(MSimdBinaryArith* ins); void visitSimdSelect(MSimdSelect* ins); From 62a36df31c802b1123e4e1bfc52ea1351b76cfe8 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:18 -0700 Subject: [PATCH 044/199] Bug 1136226 - Unary functions for small integer SIMD types. r=bbouvier - Implement 'not' and 'neg' for 8x16 and 16x8 types. - Rename some 'bitwiseFooX4' masm functions to 'bitwiseFooSimd128'. - Rename the zeroInt32x4 and zeroFloat32x4 to zeroSimd128{Int,Float}. - Add support for the paddb/paddw and psubb/psubw SSE2 instructions in the assembler. --- js/src/jit/Lowering.cpp | 24 ++++-- js/src/jit/shared/LIR-shared.h | 16 ++++ js/src/jit/shared/LOpcodes-shared.h | 2 + js/src/jit/x64/Assembler-x64.cpp | 12 ++- js/src/jit/x86-shared/Assembler-x86-shared.h | 64 +++++++++++++++ .../jit/x86-shared/BaseAssembler-x86-shared.h | 52 ++++++++++++ .../x86-shared/CodeGenerator-x86-shared.cpp | 80 +++++++++++++++---- .../jit/x86-shared/CodeGenerator-x86-shared.h | 2 + js/src/jit/x86-shared/Encoding-x86-shared.h | 4 + .../MacroAssembler-x86-shared-inl.h | 6 +- .../x86-shared/MacroAssembler-x86-shared.h | 28 +++++-- js/src/jit/x86/Assembler-x86.cpp | 4 + 12 files changed, 260 insertions(+), 34 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index a0bdb477731d..9b2b54990190 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4483,13 +4483,23 @@ LIRGenerator::visitSimdUnaryArith(MSimdUnaryArith* ins) // Cannot be at start, as the ouput is used as a temporary to store values. LUse in = use(ins->input()); - if (ins->type() == MIRType::Int32x4 || ins->type() == MIRType::Bool32x4) { - LSimdUnaryArithIx4* lir = new(alloc()) LSimdUnaryArithIx4(in); - define(lir, ins); - } else if (ins->type() == MIRType::Float32x4) { - LSimdUnaryArithFx4* lir = new(alloc()) LSimdUnaryArithFx4(in); - define(lir, ins); - } else { + switch (ins->type()) { + case MIRType::Int8x16: + case MIRType::Bool8x16: + define(new (alloc()) LSimdUnaryArithIx16(in), ins); + break; + case MIRType::Int16x8: + case MIRType::Bool16x8: + define(new (alloc()) LSimdUnaryArithIx8(in), ins); + break; + case MIRType::Int32x4: + case MIRType::Bool32x4: + define(new (alloc()) LSimdUnaryArithIx4(in), ins); + break; + case MIRType::Float32x4: + define(new (alloc()) LSimdUnaryArithFx4(in), ins); + break; + default: MOZ_CRASH("Unknown SIMD kind for unary operation"); } } diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 0e9b7283401c..2160adee5b76 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -525,6 +525,22 @@ class LSimdUnaryArith : public LInstructionHelper<1, 1, 0> } }; +// Unary SIMD arithmetic operation on a Int8x16 operand +class LSimdUnaryArithIx16 : public LSimdUnaryArith +{ + public: + LIR_HEADER(SimdUnaryArithIx16); + explicit LSimdUnaryArithIx16(const LAllocation& in) : LSimdUnaryArith(in) {} +}; + +// Unary SIMD arithmetic operation on a Int16x8 operand +class LSimdUnaryArithIx8 : public LSimdUnaryArith +{ + public: + LIR_HEADER(SimdUnaryArithIx8); + explicit LSimdUnaryArithIx8(const LAllocation& in) : LSimdUnaryArith(in) {} +}; + // Unary SIMD arithmetic operation on a Int32x4 operand class LSimdUnaryArithIx4 : public LSimdUnaryArith { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index bc6359e3f90f..f868a7a26c44 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -37,6 +37,8 @@ _(SimdSwizzleI) \ _(SimdSwizzleF) \ _(SimdShuffle) \ + _(SimdUnaryArithIx16) \ + _(SimdUnaryArithIx8) \ _(SimdUnaryArithIx4) \ _(SimdUnaryArithFx4) \ _(SimdBinaryCompIx4) \ diff --git a/js/src/jit/x64/Assembler-x64.cpp b/js/src/jit/x64/Assembler-x64.cpp index 40ac2042dcb7..817172a3c269 100644 --- a/js/src/jit/x64/Assembler-x64.cpp +++ b/js/src/jit/x64/Assembler-x64.cpp @@ -55,9 +55,13 @@ ABIArgGenerator::next(MIRType type) case MIRType::Double: current_ = ABIArg(FloatArgRegs[regIndex_++]); break; - case MIRType::Bool32x4: + case MIRType::Int8x16: + case MIRType::Int16x8: case MIRType::Int32x4: case MIRType::Float32x4: + case MIRType::Bool8x16: + case MIRType::Bool16x8: + case MIRType::Bool32x4: // On Win64, >64 bit args need to be passed by reference, but asm.js // doesn't allow passing SIMD values to FFIs. The only way to reach // here is asm to asm calls, so we can break the ABI here. @@ -91,9 +95,13 @@ ABIArgGenerator::next(MIRType type) else current_ = ABIArg(FloatArgRegs[floatRegIndex_++]); break; - case MIRType::Bool32x4: + case MIRType::Int8x16: + case MIRType::Int16x8: case MIRType::Int32x4: case MIRType::Float32x4: + case MIRType::Bool8x16: + case MIRType::Bool16x8: + case MIRType::Bool32x4: if (floatRegIndex_ == NumFloatArgRegs) { stackOffset_ = AlignBytes(stackOffset_, SimdMemoryAlignment); current_ = ABIArg(stackOffset_); diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index 0d7c90c0ba2a..2c95d2ee30c5 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -2439,6 +2439,70 @@ class AssemblerX86Shared : public AssemblerShared MOZ_CRASH("unexpected operand kind"); } } + void vpaddb(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpaddb_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpaddb_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpaddb_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpsubb(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpsubb_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpsubb_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpsubb_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpaddw(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpaddw_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpaddw_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpaddw_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpsubw(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpsubw_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpsubw_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpsubw_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } void vpaddd(const Operand& src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); switch (src1.kind()) { diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index 3b8132a8cbc4..1280b0427490 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -583,6 +583,32 @@ public: m_formatter.twoByteOp(OP2_XADD_EvGv, offset, base, index, scale, srcdest); } + void vpaddb_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddb", VEX_PD, OP2_PADDB_VdqWdq, src1, src0, dst); + } + void vpaddb_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddb", VEX_PD, OP2_PADDB_VdqWdq, offset, base, src0, dst); + } + void vpaddb_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddb", VEX_PD, OP2_PADDB_VdqWdq, address, src0, dst); + } + + void vpaddw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddw", VEX_PD, OP2_PADDW_VdqWdq, src1, src0, dst); + } + void vpaddw_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddw", VEX_PD, OP2_PADDW_VdqWdq, offset, base, src0, dst); + } + void vpaddw_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddw", VEX_PD, OP2_PADDW_VdqWdq, address, src0, dst); + } + void vpaddd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpaddd", VEX_PD, OP2_PADDD_VdqWdq, src1, src0, dst); @@ -596,6 +622,32 @@ public: twoByteOpSimd("vpaddd", VEX_PD, OP2_PADDD_VdqWdq, address, src0, dst); } + void vpsubb_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubb", VEX_PD, OP2_PSUBB_VdqWdq, src1, src0, dst); + } + void vpsubb_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubb", VEX_PD, OP2_PSUBB_VdqWdq, offset, base, src0, dst); + } + void vpsubb_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubb", VEX_PD, OP2_PSUBB_VdqWdq, address, src0, dst); + } + + void vpsubw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubw", VEX_PD, OP2_PSUBW_VdqWdq, src1, src0, dst); + } + void vpsubw_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubw", VEX_PD, OP2_PSUBW_VdqWdq, offset, base, src0, dst); + } + void vpsubw_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubw", VEX_PD, OP2_PSUBW_VdqWdq, address, src0, dst); + } + void vpsubd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpsubd", VEX_PD, OP2_PSUBD_VdqWdq, src1, src0, dst); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index fe6edc21b278..f68f83725f7a 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2533,14 +2533,14 @@ CodeGeneratorX86Shared::visitFloat32x4ToUint32x4(LFloat32x4ToUint32x4* ins) // We can identify A-lanes by the sign bits in A: Any A-lanes will be // positive in A, and N, B, and V-lanes will be 0x80000000 in A. Compute a // mask of non-A-lanes into |tempF|. - masm.zeroFloat32x4(tempF); + masm.zeroSimd128Float(tempF); masm.packedGreaterThanInt32x4(Operand(out), tempF); // Clear the A-lanes in B. - masm.bitwiseAndX4(Operand(tempF), scratch); + masm.bitwiseAndSimd128(Operand(tempF), scratch); // Compute the final result: A for A-lanes, A|B for B-lanes. - masm.bitwiseOrX4(Operand(scratch), out); + masm.bitwiseOrSimd128(Operand(scratch), out); // We still need to filter out the V-lanes. They would show up as 0x80000000 // in both A and B. Since we cleared the valid A-lanes in B, the V-lanes are @@ -3315,7 +3315,7 @@ CodeGeneratorX86Shared::visitSimdBinaryCompIx4(LSimdBinaryCompIx4* ins) // if that's what it's used in. masm.loadConstantSimd128Int(allOnes, scratch); masm.packedEqualInt32x4(rhs, lhs); - masm.bitwiseXorX4(Operand(scratch), lhs); + masm.bitwiseXorSimd128(Operand(scratch), lhs); return; case MSimdBinaryComp::greaterThanOrEqual: // src := rhs @@ -3325,13 +3325,13 @@ CodeGeneratorX86Shared::visitSimdBinaryCompIx4(LSimdBinaryCompIx4* ins) masm.loadAlignedSimd128Int(rhs, scratch); masm.packedGreaterThanInt32x4(ToOperand(ins->lhs()), scratch); masm.loadConstantSimd128Int(allOnes, lhs); - masm.bitwiseXorX4(Operand(scratch), lhs); + masm.bitwiseXorSimd128(Operand(scratch), lhs); return; case MSimdBinaryComp::lessThanOrEqual: // lhs <= rhs is equivalent to !(rhs < lhs), which we compute here. masm.loadConstantSimd128Int(allOnes, scratch); masm.packedGreaterThanInt32x4(rhs, lhs); - masm.bitwiseXorX4(Operand(scratch), lhs); + masm.bitwiseXorSimd128(Operand(scratch), lhs); return; } MOZ_CRASH("unexpected SIMD op"); @@ -3534,6 +3534,58 @@ CodeGeneratorX86Shared::visitSimdBinaryArithFx4(LSimdBinaryArithFx4* ins) MOZ_CRASH("unexpected SIMD op"); } +void +CodeGeneratorX86Shared::visitSimdUnaryArithIx16(LSimdUnaryArithIx16* ins) +{ + Operand in = ToOperand(ins->input()); + FloatRegister out = ToFloatRegister(ins->output()); + + static const SimdConstant allOnes = SimdConstant::SplatX16(-1); + + switch (ins->operation()) { + case MSimdUnaryArith::neg: + masm.zeroSimd128Int(out); + masm.packedSubInt8(in, out); + return; + case MSimdUnaryArith::not_: + masm.loadConstantSimd128Int(allOnes, out); + masm.bitwiseXorSimd128(in, out); + return; + case MSimdUnaryArith::abs: + case MSimdUnaryArith::reciprocalApproximation: + case MSimdUnaryArith::reciprocalSqrtApproximation: + case MSimdUnaryArith::sqrt: + break; + } + MOZ_CRASH("unexpected SIMD op"); +} + +void +CodeGeneratorX86Shared::visitSimdUnaryArithIx8(LSimdUnaryArithIx8* ins) +{ + Operand in = ToOperand(ins->input()); + FloatRegister out = ToFloatRegister(ins->output()); + + static const SimdConstant allOnes = SimdConstant::SplatX8(-1); + + switch (ins->operation()) { + case MSimdUnaryArith::neg: + masm.zeroSimd128Int(out); + masm.packedSubInt16(in, out); + return; + case MSimdUnaryArith::not_: + masm.loadConstantSimd128Int(allOnes, out); + masm.bitwiseXorSimd128(in, out); + return; + case MSimdUnaryArith::abs: + case MSimdUnaryArith::reciprocalApproximation: + case MSimdUnaryArith::reciprocalSqrtApproximation: + case MSimdUnaryArith::sqrt: + break; + } + MOZ_CRASH("unexpected SIMD op"); +} + void CodeGeneratorX86Shared::visitSimdUnaryArithIx4(LSimdUnaryArithIx4* ins) { @@ -3544,12 +3596,12 @@ CodeGeneratorX86Shared::visitSimdUnaryArithIx4(LSimdUnaryArithIx4* ins) switch (ins->operation()) { case MSimdUnaryArith::neg: - masm.zeroInt32x4(out); + masm.zeroSimd128Int(out); masm.packedSubInt32(in, out); return; case MSimdUnaryArith::not_: masm.loadConstantSimd128Int(allOnes, out); - masm.bitwiseXorX4(in, out); + masm.bitwiseXorSimd128(in, out); return; case MSimdUnaryArith::abs: case MSimdUnaryArith::reciprocalApproximation: @@ -3580,15 +3632,15 @@ CodeGeneratorX86Shared::visitSimdUnaryArithFx4(LSimdUnaryArithFx4* ins) switch (ins->operation()) { case MSimdUnaryArith::abs: masm.loadConstantSimd128Float(signMasks, out); - masm.bitwiseAndX4(in, out); + masm.bitwiseAndSimd128(in, out); return; case MSimdUnaryArith::neg: masm.loadConstantSimd128Float(minusZero, out); - masm.bitwiseXorX4(in, out); + masm.bitwiseXorSimd128(in, out); return; case MSimdUnaryArith::not_: masm.loadConstantSimd128Float(allOnes, out); - masm.bitwiseXorX4(in, out); + masm.bitwiseXorSimd128(in, out); return; case MSimdUnaryArith::reciprocalApproximation: masm.packedRcpApproximationFloat32x4(in, out); @@ -3709,9 +3761,9 @@ CodeGeneratorX86Shared::visitSimdSelect(LSimdSelect* ins) if (!mir->mask()->isSimdBinaryComp()) masm.packedRightShiftByScalar(Imm32(31), temp); - masm.bitwiseAndX4(Operand(temp), output); - masm.bitwiseAndNotX4(Operand(onFalse), temp); - masm.bitwiseOrX4(Operand(temp), output); + masm.bitwiseAndSimd128(Operand(temp), output); + masm.bitwiseAndNotSimd128(Operand(onFalse), temp); + masm.bitwiseOrSimd128(Operand(temp), output); } void diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index ad200f3470ce..7d0a888fe91c 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -307,6 +307,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void visitSimdSwizzleI(LSimdSwizzleI* lir); void visitSimdSwizzleF(LSimdSwizzleF* lir); void visitSimdShuffle(LSimdShuffle* lir); + void visitSimdUnaryArithIx16(LSimdUnaryArithIx16* lir); + void visitSimdUnaryArithIx8(LSimdUnaryArithIx8* lir); void visitSimdUnaryArithIx4(LSimdUnaryArithIx4* lir); void visitSimdUnaryArithFx4(LSimdUnaryArithFx4* lir); void visitSimdBinaryCompIx4(LSimdBinaryCompIx4* lir); diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h index 20e1f5943d34..a7cef422fcb4 100644 --- a/js/src/jit/x86-shared/Encoding-x86-shared.h +++ b/js/src/jit/x86-shared/Encoding-x86-shared.h @@ -259,7 +259,11 @@ enum TwoByteOpcodeID { OP2_PXORDQ_VdqWdq = 0xEF, OP2_PSLLD_VdqWdq = 0xF2, OP2_PMULUDQ_VdqWdq = 0xF4, + OP2_PSUBB_VdqWdq = 0xF8, + OP2_PSUBW_VdqWdq = 0xF9, OP2_PSUBD_VdqWdq = 0xFA, + OP2_PADDB_VdqWdq = 0xFC, + OP2_PADDW_VdqWdq = 0xFD, OP2_PADDD_VdqWdq = 0xFE }; diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h index 8fb70e0769e7..d9043bed848b 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h @@ -928,9 +928,9 @@ MacroAssembler::canonicalizeFloat32x4(FloatRegister reg, FloatRegister scratch) float nanf = float(JS::GenericNaN()); loadConstantSimd128Float(SimdConstant::SplatX4(nanf), ifFalse); - bitwiseAndX4(Operand(mask), reg); - bitwiseAndNotX4(Operand(ifFalse), mask); - bitwiseOrX4(Operand(mask), reg); + bitwiseAndSimd128(Operand(mask), reg); + bitwiseAndNotSimd128(Operand(ifFalse), mask); + bitwiseOrSimd128(Operand(mask), reg); } // ======================================================================== diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h index 673e10f83e57..bfa39c7dfafa 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h @@ -804,24 +804,24 @@ class MacroAssemblerX86Shared : public Assembler vcvtdq2ps(src, dest); } - void bitwiseAndX4(const Operand& src, FloatRegister dest) { + void bitwiseAndSimd128(const Operand& src, FloatRegister dest) { // TODO Using the "ps" variant for all types incurs a domain crossing // penalty for integer types and double. vandps(src, dest, dest); } - void bitwiseAndNotX4(const Operand& src, FloatRegister dest) { + void bitwiseAndNotSimd128(const Operand& src, FloatRegister dest) { vandnps(src, dest, dest); } - void bitwiseOrX4(const Operand& src, FloatRegister dest) { + void bitwiseOrSimd128(const Operand& src, FloatRegister dest) { vorps(src, dest, dest); } - void bitwiseXorX4(const Operand& src, FloatRegister dest) { + void bitwiseXorSimd128(const Operand& src, FloatRegister dest) { vxorps(src, dest, dest); } - void zeroFloat32x4(FloatRegister dest) { + void zeroSimd128Float(FloatRegister dest) { vxorps(dest, dest, dest); } - void zeroInt32x4(FloatRegister dest) { + void zeroSimd128Int(FloatRegister dest) { vpxor(dest, dest, dest); } @@ -939,6 +939,18 @@ class MacroAssemblerX86Shared : public Assembler void packedGreaterThanInt32x4(const Operand& src, FloatRegister dest) { vpcmpgtd(src, dest, dest); } + void packedAddInt8(const Operand& src, FloatRegister dest) { + vpaddb(src, dest, dest); + } + void packedSubInt8(const Operand& src, FloatRegister dest) { + vpsubb(src, dest, dest); + } + void packedAddInt16(const Operand& src, FloatRegister dest) { + vpaddw(src, dest, dest); + } + void packedSubInt16(const Operand& src, FloatRegister dest) { + vpsubw(src, dest, dest); + } void packedAddInt32(const Operand& src, FloatRegister dest) { vpaddd(src, dest, dest); } @@ -1197,7 +1209,7 @@ class MacroAssemblerX86Shared : public Assembler static const SimdConstant zero = SimdConstant::SplatX4(0); static const SimdConstant minusOne = SimdConstant::SplatX4(-1); if (v == zero) { - zeroInt32x4(dest); + zeroSimd128Int(dest); return true; } if (v == minusOne) { @@ -1211,7 +1223,7 @@ class MacroAssemblerX86Shared : public Assembler if (v == zero) { // This won't get inlined if the SimdConstant v contains -0 in any // lane, as operator== here does a memcmp. - zeroFloat32x4(dest); + zeroSimd128Float(dest); return true; } return false; diff --git a/js/src/jit/x86/Assembler-x86.cpp b/js/src/jit/x86/Assembler-x86.cpp index 2791823ce579..7ce7be4bbea6 100644 --- a/js/src/jit/x86/Assembler-x86.cpp +++ b/js/src/jit/x86/Assembler-x86.cpp @@ -30,8 +30,12 @@ ABIArgGenerator::next(MIRType type) current_ = ABIArg(stackOffset_); stackOffset_ += sizeof(uint64_t); break; + case MIRType::Int8x16: + case MIRType::Int16x8: case MIRType::Int32x4: case MIRType::Float32x4: + case MIRType::Bool8x16: + case MIRType::Bool16x8: case MIRType::Bool32x4: // SIMD values aren't passed in or out of C++, so we can make up // whatever internal ABI we like. visitAsmJSPassArg assumes From e06a11b5f57a77fd5176b22c95e0899541cd0a0e Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:18 -0700 Subject: [PATCH 045/199] Bug 1136226 - Binary functions for small integer SIMD types. r=bbouvier - Rename LSimdBinaryBitwiseX4 to LSimdBinaryBitwise and use it for all types. - Add pmullw to the assembler for 16x8 multiplies. Don't implement 8x16 multiplies just yet. There are no SSE instructions for that operation, so they need to be synthesied from 16x8 multiplies and shuffles. --- js/src/jit/Lowering.cpp | 14 +---- js/src/jit/arm/CodeGenerator-arm.h | 2 +- js/src/jit/arm64/CodeGenerator-arm64.h | 2 +- .../mips-shared/CodeGenerator-mips-shared.h | 2 +- js/src/jit/shared/LIR-shared.h | 22 ++++++- js/src/jit/shared/LOpcodes-shared.h | 4 +- js/src/jit/x86-shared/Assembler-x86-shared.h | 13 ++++ .../jit/x86-shared/BaseAssembler-x86-shared.h | 9 +++ .../x86-shared/CodeGenerator-x86-shared.cpp | 60 ++++++++++++++++++- .../jit/x86-shared/CodeGenerator-x86-shared.h | 4 +- js/src/jit/x86-shared/Encoding-x86-shared.h | 1 + js/src/jit/x86-shared/Lowering-x86-shared.cpp | 55 +++++++++++------ 12 files changed, 150 insertions(+), 38 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 9b2b54990190..09542a2a958c 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4537,18 +4537,8 @@ LIRGenerator::visitSimdBinaryBitwise(MSimdBinaryBitwise* ins) MDefinition* lhs = ins->lhs(); MDefinition* rhs = ins->rhs(); ReorderCommutative(&lhs, &rhs, ins); - - switch (ins->type()) { - case MIRType::Bool32x4: - case MIRType::Int32x4: - case MIRType::Float32x4: { - LSimdBinaryBitwiseX4* lir = new(alloc()) LSimdBinaryBitwiseX4; - lowerForFPU(lir, ins, lhs, rhs); - break; - } - default: - MOZ_CRASH("Unknown SIMD kind when doing bitwise operations"); - } + LSimdBinaryBitwise* lir = new(alloc()) LSimdBinaryBitwise; + lowerForFPU(lir, ins, lhs, rhs); } void diff --git a/js/src/jit/arm/CodeGenerator-arm.h b/js/src/jit/arm/CodeGenerator-arm.h index f674ca32a627..2f49dc93713c 100644 --- a/js/src/jit/arm/CodeGenerator-arm.h +++ b/js/src/jit/arm/CodeGenerator-arm.h @@ -259,7 +259,7 @@ class CodeGeneratorARM : public CodeGeneratorShared void visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir) { MOZ_CRASH("NYI"); } void visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir) { MOZ_CRASH("NYI"); } void visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryBitwise(LSimdBinaryBitwise* lir) { MOZ_CRASH("NYI"); } }; typedef CodeGeneratorARM CodeGeneratorSpecific; diff --git a/js/src/jit/arm64/CodeGenerator-arm64.h b/js/src/jit/arm64/CodeGenerator-arm64.h index 31635fc39246..58983b3ca1c5 100644 --- a/js/src/jit/arm64/CodeGenerator-arm64.h +++ b/js/src/jit/arm64/CodeGenerator-arm64.h @@ -233,7 +233,7 @@ class CodeGeneratorARM64 : public CodeGeneratorShared void visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir) { MOZ_CRASH("NYI"); } void visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir) { MOZ_CRASH("NYI"); } void visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryBitwise(LSimdBinaryBitwise* lir) { MOZ_CRASH("NYI"); } }; typedef CodeGeneratorARM64 CodeGeneratorSpecific; diff --git a/js/src/jit/mips-shared/CodeGenerator-mips-shared.h b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h index 1d4d4c6f0aaa..dbcc4ce2708d 100644 --- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.h +++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h @@ -243,7 +243,7 @@ class CodeGeneratorMIPSShared : public CodeGeneratorShared void visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir) { MOZ_CRASH("NYI"); } void visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir) { MOZ_CRASH("NYI"); } void visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryBitwise(LSimdBinaryBitwise* lir) { MOZ_CRASH("NYI"); } void visitSimdGeneralShuffleI(LSimdGeneralShuffleI* lir) { MOZ_CRASH("NYI"); } void visitSimdGeneralShuffleF(LSimdGeneralShuffleF* lir) { MOZ_CRASH("NYI"); } }; diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 2160adee5b76..dbee7c37ceab 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -497,6 +497,22 @@ class LSimdBinaryArith : public LInstructionHelper<1, 2, 1> } }; +// Binary SIMD arithmetic operation between two Int8x16 operands +class LSimdBinaryArithIx16 : public LSimdBinaryArith +{ + public: + LIR_HEADER(SimdBinaryArithIx16); + LSimdBinaryArithIx16() : LSimdBinaryArith() {} +}; + +// Binary SIMD arithmetic operation between two Int16x8 operands +class LSimdBinaryArithIx8 : public LSimdBinaryArith +{ + public: + LIR_HEADER(SimdBinaryArithIx8); + LSimdBinaryArithIx8() : LSimdBinaryArith() {} +}; + // Binary SIMD arithmetic operation between two Int32x4 operands class LSimdBinaryArithIx4 : public LSimdBinaryArith { @@ -557,11 +573,11 @@ class LSimdUnaryArithFx4 : public LSimdUnaryArith explicit LSimdUnaryArithFx4(const LAllocation& in) : LSimdUnaryArith(in) {} }; -// Binary SIMD bitwise operation between two int32x4 or float32x4 operands -class LSimdBinaryBitwiseX4 : public LInstructionHelper<1, 2, 0> +// Binary SIMD bitwise operation between two 128-bit operands. +class LSimdBinaryBitwise : public LInstructionHelper<1, 2, 0> { public: - LIR_HEADER(SimdBinaryBitwiseX4); + LIR_HEADER(SimdBinaryBitwise); const LAllocation* lhs() { return getOperand(0); } diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index f868a7a26c44..38b5bcb838c7 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -43,9 +43,11 @@ _(SimdUnaryArithFx4) \ _(SimdBinaryCompIx4) \ _(SimdBinaryCompFx4) \ + _(SimdBinaryArithIx16) \ + _(SimdBinaryArithIx8) \ _(SimdBinaryArithIx4) \ _(SimdBinaryArithFx4) \ - _(SimdBinaryBitwiseX4) \ + _(SimdBinaryBitwise) \ _(SimdShift) \ _(SimdSelect) \ _(Value) \ diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index 2c95d2ee30c5..c61700156721 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -2552,6 +2552,19 @@ class AssemblerX86Shared : public AssemblerShared MOZ_CRASH("unexpected operand kind"); } } + void vpmullw(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpmullw_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpmullw_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } void vpmulld(const Operand& src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE41()); switch (src1.kind()) { diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index 1280b0427490..e12601e87377 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -670,6 +670,15 @@ public: twoByteOpSimd("vpmuludq", VEX_PD, OP2_PMULUDQ_VdqWdq, offset, base, src0, dst); } + void vpmullw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpmullw", VEX_PD, OP2_PMULLW_VdqWdq, src1, src0, dst); + } + void vpmullw_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpmullw", VEX_PD, OP2_PMULLW_VdqWdq, offset, base, src0, dst); + } + void vpmulld_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { threeByteOpSimd("vpmulld", VEX_PD, OP3_PMULLD_VdqWdq, ESCAPE_38, src1, src0, dst); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index f68f83725f7a..69151c6b03bc 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -3367,6 +3367,64 @@ CodeGeneratorX86Shared::visitSimdBinaryCompFx4(LSimdBinaryCompFx4* ins) MOZ_CRASH("unexpected SIMD op"); } +void +CodeGeneratorX86Shared::visitSimdBinaryArithIx16(LSimdBinaryArithIx16* ins) +{ + FloatRegister lhs = ToFloatRegister(ins->lhs()); + Operand rhs = ToOperand(ins->rhs()); + FloatRegister output = ToFloatRegister(ins->output()); + + MSimdBinaryArith::Operation op = ins->operation(); + switch (op) { + case MSimdBinaryArith::Op_add: + masm.vpaddb(rhs, lhs, output); + return; + case MSimdBinaryArith::Op_sub: + masm.vpsubb(rhs, lhs, output); + return; + case MSimdBinaryArith::Op_mul: + // 8x16 mul is a valid operation, but not supported in SSE or AVX. + // The operation is synthesized from 16x8 multiplies by + // MSimdBinaryArith::AddLegalized(). + break; + case MSimdBinaryArith::Op_div: + case MSimdBinaryArith::Op_max: + case MSimdBinaryArith::Op_min: + case MSimdBinaryArith::Op_minNum: + case MSimdBinaryArith::Op_maxNum: + break; + } + MOZ_CRASH("unexpected SIMD op"); +} + +void +CodeGeneratorX86Shared::visitSimdBinaryArithIx8(LSimdBinaryArithIx8* ins) +{ + FloatRegister lhs = ToFloatRegister(ins->lhs()); + Operand rhs = ToOperand(ins->rhs()); + FloatRegister output = ToFloatRegister(ins->output()); + + MSimdBinaryArith::Operation op = ins->operation(); + switch (op) { + case MSimdBinaryArith::Op_add: + masm.vpaddw(rhs, lhs, output); + return; + case MSimdBinaryArith::Op_sub: + masm.vpsubw(rhs, lhs, output); + return; + case MSimdBinaryArith::Op_mul: + masm.vpmullw(rhs, lhs, output); + return; + case MSimdBinaryArith::Op_div: + case MSimdBinaryArith::Op_max: + case MSimdBinaryArith::Op_min: + case MSimdBinaryArith::Op_minNum: + case MSimdBinaryArith::Op_maxNum: + break; + } + MOZ_CRASH("unexpected SIMD op"); +} + void CodeGeneratorX86Shared::visitSimdBinaryArithIx4(LSimdBinaryArithIx4* ins) { @@ -3656,7 +3714,7 @@ CodeGeneratorX86Shared::visitSimdUnaryArithFx4(LSimdUnaryArithFx4* ins) } void -CodeGeneratorX86Shared::visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* ins) +CodeGeneratorX86Shared::visitSimdBinaryBitwise(LSimdBinaryBitwise* ins) { FloatRegister lhs = ToFloatRegister(ins->lhs()); Operand rhs = ToOperand(ins->rhs()); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 7d0a888fe91c..411253edb7a1 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -313,9 +313,11 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void visitSimdUnaryArithFx4(LSimdUnaryArithFx4* lir); void visitSimdBinaryCompIx4(LSimdBinaryCompIx4* lir); void visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir); + void visitSimdBinaryArithIx16(LSimdBinaryArithIx16* lir); + void visitSimdBinaryArithIx8(LSimdBinaryArithIx8* lir); void visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir); void visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir); - void visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* lir); + void visitSimdBinaryBitwise(LSimdBinaryBitwise* lir); void visitSimdShift(LSimdShift* lir); void visitSimdSelect(LSimdSelect* ins); void visitSimdAllTrue(LSimdAllTrue* ins); diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h index a7cef422fcb4..92959d6a5c38 100644 --- a/js/src/jit/x86-shared/Encoding-x86-shared.h +++ b/js/src/jit/x86-shared/Encoding-x86-shared.h @@ -251,6 +251,7 @@ enum TwoByteOpcodeID { OP2_PEXTRW_GdUdIb = 0xC5, OP2_SHUFPS_VpsWpsIb = 0xC6, OP2_PSRLD_VdqWdq = 0xD2, + OP2_PMULLW_VdqWdq = 0xD5, OP2_MOVQ_WdVd = 0xD6, OP2_PANDDQ_VdqWdq = 0xDB, OP2_PANDNDQ_VdqWdq = 0xDF, diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index 229020e066fe..09ab0b48642a 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -732,24 +732,45 @@ LIRGeneratorX86Shared::visitSimdBinaryArith(MSimdBinaryArith* ins) if (ins->isCommutative()) ReorderCommutative(&lhs, &rhs, ins); - if (ins->type() == MIRType::Int32x4) { - LSimdBinaryArithIx4* lir = new(alloc()) LSimdBinaryArithIx4(); - bool needsTemp = ins->operation() == MSimdBinaryArith::Op_mul && !MacroAssembler::HasSSE41(); - lir->setTemp(0, needsTemp ? temp(LDefinition::SIMD128INT) : LDefinition::BogusTemp()); - lowerForFPU(lir, ins, lhs, rhs); - return; + switch (ins->type()) { + case MIRType::Int8x16: { + LSimdBinaryArithIx16* lir = new (alloc()) LSimdBinaryArithIx16(); + lir->setTemp(0, LDefinition::BogusTemp()); + lowerForFPU(lir, ins, lhs, rhs); + return; + } + + case MIRType::Int16x8: { + LSimdBinaryArithIx8* lir = new (alloc()) LSimdBinaryArithIx8(); + lir->setTemp(0, LDefinition::BogusTemp()); + lowerForFPU(lir, ins, lhs, rhs); + return; + } + + case MIRType::Int32x4: { + LSimdBinaryArithIx4* lir = new (alloc()) LSimdBinaryArithIx4(); + bool needsTemp = + ins->operation() == MSimdBinaryArith::Op_mul && !MacroAssembler::HasSSE41(); + lir->setTemp(0, needsTemp ? temp(LDefinition::SIMD128INT) : LDefinition::BogusTemp()); + lowerForFPU(lir, ins, lhs, rhs); + return; + } + + case MIRType::Float32x4: { + LSimdBinaryArithFx4* lir = new (alloc()) LSimdBinaryArithFx4(); + + bool needsTemp = ins->operation() == MSimdBinaryArith::Op_max || + ins->operation() == MSimdBinaryArith::Op_minNum || + ins->operation() == MSimdBinaryArith::Op_maxNum; + lir->setTemp(0, + needsTemp ? temp(LDefinition::SIMD128FLOAT) : LDefinition::BogusTemp()); + lowerForFPU(lir, ins, lhs, rhs); + return; + } + + default: + MOZ_CRASH("unknown simd type on binary arith operation"); } - - MOZ_ASSERT(ins->type() == MIRType::Float32x4, "unknown simd type on binary arith operation"); - - LSimdBinaryArithFx4* lir = new(alloc()) LSimdBinaryArithFx4(); - - bool needsTemp = ins->operation() == MSimdBinaryArith::Op_max || - ins->operation() == MSimdBinaryArith::Op_minNum || - ins->operation() == MSimdBinaryArith::Op_maxNum; - lir->setTemp(0, needsTemp ? temp(LDefinition::SIMD128FLOAT) : LDefinition::BogusTemp()); - - lowerForFPU(lir, ins, lhs, rhs); } void From 4e590a427bb024d5412f3ee1bfbf008f3b7ed832 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:18 -0700 Subject: [PATCH 046/199] Bug 1136226 - Implement 16x8 SIMD shift operators. r=bbouvier These all have corresponding SSE instructions. The 8x16 shifts don't have SSE instructions, so they will be added by the next commit. --- js/src/jit/Lowering.cpp | 4 +- js/src/jit/shared/LIR-shared.h | 3 + js/src/jit/x86-shared/Assembler-x86-shared.h | 25 ++++++ .../jit/x86-shared/BaseAssembler-x86-shared.h | 41 ++++++++- .../x86-shared/CodeGenerator-x86-shared.cpp | 90 ++++++++++++++----- js/src/jit/x86-shared/Encoding-x86-shared.h | 12 ++- .../x86-shared/MacroAssembler-x86-shared.h | 31 +++++-- 7 files changed, 166 insertions(+), 40 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 09542a2a958c..97a676e28f11 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4544,8 +4544,8 @@ LIRGenerator::visitSimdBinaryBitwise(MSimdBinaryBitwise* ins) void LIRGenerator::visitSimdShift(MSimdShift* ins) { - MOZ_ASSERT(ins->type() == MIRType::Int32x4); - MOZ_ASSERT(ins->lhs()->type() == MIRType::Int32x4); + MOZ_ASSERT(IsIntegerSimdType(ins->type())); + MOZ_ASSERT(ins->lhs()->type() == ins->type()); MOZ_ASSERT(ins->rhs()->type() == MIRType::Int32); LUse vector = useRegisterAtStart(ins->lhs()); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index dbee7c37ceab..22c539d3118f 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -625,6 +625,9 @@ class LSimdShift : public LInstructionHelper<1, 2, 1> MSimdShift* mir() const { return mir_->toSimdShift(); } + MIRType type() const { + return mir_->type(); + } }; // SIMD selection of lanes from two int32x4 or float32x4 arguments based on a diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index c61700156721..a78f26c40022 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -2167,6 +2167,31 @@ class AssemblerX86Shared : public AssemblerShared masm.vpsrld_ir(count.value, src0.encoding(), dest.encoding()); } + void vpsllw(FloatRegister src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + masm.vpsllw_rr(src1.encoding(), src0.encoding(), dest.encoding()); + } + void vpsllw(Imm32 count, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + masm.vpsllw_ir(count.value, src0.encoding(), dest.encoding()); + } + void vpsraw(FloatRegister src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + masm.vpsraw_rr(src1.encoding(), src0.encoding(), dest.encoding()); + } + void vpsraw(Imm32 count, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + masm.vpsraw_ir(count.value, src0.encoding(), dest.encoding()); + } + void vpsrlw(FloatRegister src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + masm.vpsrlw_rr(src1.encoding(), src0.encoding(), dest.encoding()); + } + void vpsrlw(Imm32 count, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + masm.vpsrlw_ir(count.value, src0.encoding(), dest.encoding()); + } + void vcvtsi2sd(const Operand& src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); switch (src1.kind()) { diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index e12601e87377..96d34078280d 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -2758,13 +2758,13 @@ public: void vpsllq_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) { MOZ_ASSERT(count < 64); - shiftOpImmSimd("vpsllq", OP2_PSRLDQ_Vd, ShiftID::vpsllq, count, src, dst); + shiftOpImmSimd("vpsllq", OP2_PSRLDQ_Vd, ShiftID::vpsllx, count, src, dst); } void vpsrlq_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) { MOZ_ASSERT(count < 64); - shiftOpImmSimd("vpsrlq", OP2_PSRLDQ_Vd, ShiftID::vpsrlq, count, src, dst); + shiftOpImmSimd("vpsrlq", OP2_PSRLDQ_Vd, ShiftID::vpsrlx, count, src, dst); } void vpslld_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) @@ -2775,7 +2775,7 @@ public: void vpslld_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) { MOZ_ASSERT(count < 32); - shiftOpImmSimd("vpslld", OP2_PSLLD_UdqIb, ShiftID::vpslld, count, src, dst); + shiftOpImmSimd("vpslld", OP2_PSLLD_UdqIb, ShiftID::vpsllx, count, src, dst); } void vpsrad_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) @@ -2797,7 +2797,40 @@ public: void vpsrld_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) { MOZ_ASSERT(count < 32); - shiftOpImmSimd("vpsrld", OP2_PSRLD_UdqIb, ShiftID::vpsrld, count, src, dst); + shiftOpImmSimd("vpsrld", OP2_PSRLD_UdqIb, ShiftID::vpsrlx, count, src, dst); + } + + void vpsllw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsllw", VEX_PD, OP2_PSLLW_VdqWdq, src1, src0, dst); + } + + void vpsllw_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) + { + MOZ_ASSERT(count < 16); + shiftOpImmSimd("vpsllw", OP2_PSLLW_UdqIb, ShiftID::vpsllx, count, src, dst); + } + + void vpsraw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsraw", VEX_PD, OP2_PSRAW_VdqWdq, src1, src0, dst); + } + + void vpsraw_ir(int32_t count, XMMRegisterID src, XMMRegisterID dst) + { + MOZ_ASSERT(count < 16); + shiftOpImmSimd("vpsraw", OP2_PSRAW_UdqIb, ShiftID::vpsrad, count, src, dst); + } + + void vpsrlw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsrlw", VEX_PD, OP2_PSRLW_VdqWdq, src1, src0, dst); + } + + void vpsrlw_ir(uint32_t count, XMMRegisterID src, XMMRegisterID dst) + { + MOZ_ASSERT(count < 16); + shiftOpImmSimd("vpsrlw", OP2_PSRLW_UdqIb, ShiftID::vpsrlx, count, src, dst); } void vmovmskpd_rr(XMMRegisterID src, RegisterID dst) diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 69151c6b03bc..7ae778cc95ce 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -3750,21 +3750,44 @@ CodeGeneratorX86Shared::visitSimdShift(LSimdShift* ins) FloatRegister out = ToFloatRegister(ins->output()); MOZ_ASSERT(ToFloatRegister(ins->vector()) == out); // defineReuseInput(0); - // If the shift count is out of range, only use the low 5 bits. + // The shift amount is masked to the number of bits in a lane. + uint32_t shiftmask = (128u / SimdTypeToLength(ins->type())) - 1; + + // Note that SSE doesn't have instructions for shifting 8x16 vectors. + // These shifts are synthesized by the MSimdShift::AddLegalized() function. const LAllocation* val = ins->value(); if (val->isConstant()) { MOZ_ASSERT(ins->temp()->isBogusTemp()); - Imm32 count(uint32_t(ToInt32(val)) % 32); - switch (ins->operation()) { - case MSimdShift::lsh: - masm.packedLeftShiftByScalar(count, out); - return; - case MSimdShift::rsh: - masm.packedRightShiftByScalar(count, out); - return; - case MSimdShift::ursh: - masm.packedUnsignedRightShiftByScalar(count, out); - return; + Imm32 count(uint32_t(ToInt32(val)) & shiftmask); + switch (ins->type()) { + case MIRType::Int16x8: + switch (ins->operation()) { + case MSimdShift::lsh: + masm.packedLeftShiftByScalarInt16x8(count, out); + return; + case MSimdShift::rsh: + masm.packedRightShiftByScalarInt16x8(count, out); + return; + case MSimdShift::ursh: + masm.packedUnsignedRightShiftByScalarInt16x8(count, out); + return; + } + break; + case MIRType::Int32x4: + switch (ins->operation()) { + case MSimdShift::lsh: + masm.packedLeftShiftByScalarInt32x4(count, out); + return; + case MSimdShift::rsh: + masm.packedRightShiftByScalarInt32x4(count, out); + return; + case MSimdShift::ursh: + masm.packedUnsignedRightShiftByScalarInt32x4(count, out); + return; + } + break; + default: + MOZ_CRASH("unsupported type for SIMD shifts"); } MOZ_CRASH("unexpected SIMD bitwise op"); } @@ -3773,20 +3796,39 @@ CodeGeneratorX86Shared::visitSimdShift(LSimdShift* ins) MOZ_ASSERT(val->isRegister()); Register count = ToRegister(ins->temp()); masm.mov(ToRegister(val), count); - masm.andl(Imm32(31), count); + masm.andl(Imm32(shiftmask), count); ScratchFloat32Scope scratch(masm); masm.vmovd(count, scratch); - switch (ins->operation()) { - case MSimdShift::lsh: - masm.packedLeftShiftByScalar(scratch, out); - return; - case MSimdShift::rsh: - masm.packedRightShiftByScalar(scratch, out); - return; - case MSimdShift::ursh: - masm.packedUnsignedRightShiftByScalar(scratch, out); - return; + switch (ins->type()) { + case MIRType::Int16x8: + switch (ins->operation()) { + case MSimdShift::lsh: + masm.packedLeftShiftByScalarInt16x8(scratch, out); + return; + case MSimdShift::rsh: + masm.packedRightShiftByScalarInt16x8(scratch, out); + return; + case MSimdShift::ursh: + masm.packedUnsignedRightShiftByScalarInt16x8(scratch, out); + return; + } + break; + case MIRType::Int32x4: + switch (ins->operation()) { + case MSimdShift::lsh: + masm.packedLeftShiftByScalarInt32x4(scratch, out); + return; + case MSimdShift::rsh: + masm.packedRightShiftByScalarInt32x4(scratch, out); + return; + case MSimdShift::ursh: + masm.packedUnsignedRightShiftByScalarInt32x4(scratch, out); + return; + } + break; + default: + MOZ_CRASH("unsupported type for SIMD shifts"); } MOZ_CRASH("unexpected SIMD bitwise op"); } @@ -3817,7 +3859,7 @@ CodeGeneratorX86Shared::visitSimdSelect(LSimdSelect* ins) // Propagate sign to all bits of mask vector, if necessary. if (!mir->mask()->isSimdBinaryComp()) - masm.packedRightShiftByScalar(Imm32(31), temp); + masm.packedRightShiftByScalarInt32x4(Imm32(31), temp); masm.bitwiseAndSimd128(Operand(temp), output); masm.bitwiseAndNotSimd128(Operand(onFalse), temp); diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h index 92959d6a5c38..59c16ac1d861 100644 --- a/js/src/jit/x86-shared/Encoding-x86-shared.h +++ b/js/src/jit/x86-shared/Encoding-x86-shared.h @@ -154,12 +154,10 @@ enum OneByteOpcodeID { }; enum class ShiftID { - vpsrld = 2, - vpsrlq = 2, + vpsrlx = 2, vpsrldq = 3, vpsrad = 4, - vpslld = 6, - vpsllq = 6 + vpsllx = 6 }; enum TwoByteOpcodeID { @@ -219,6 +217,9 @@ enum TwoByteOpcodeID { OP2_MOVDQ_VsdWsd = 0x6F, OP2_MOVDQ_VdqWdq = 0x6F, OP2_PSHUFD_VdqWdqIb = 0x70, + OP2_PSLLW_UdqIb = 0x71, + OP2_PSRAW_UdqIb = 0x71, + OP2_PSRLW_UdqIb = 0x71, OP2_PSLLD_UdqIb = 0x72, OP2_PSRAD_UdqIb = 0x72, OP2_PSRLD_UdqIb = 0x72, @@ -250,14 +251,17 @@ enum TwoByteOpcodeID { OP2_PINSRW = 0xC4, OP2_PEXTRW_GdUdIb = 0xC5, OP2_SHUFPS_VpsWpsIb = 0xC6, + OP2_PSRLW_VdqWdq = 0xD1, OP2_PSRLD_VdqWdq = 0xD2, OP2_PMULLW_VdqWdq = 0xD5, OP2_MOVQ_WdVd = 0xD6, OP2_PANDDQ_VdqWdq = 0xDB, OP2_PANDNDQ_VdqWdq = 0xDF, + OP2_PSRAW_VdqWdq = 0xE1, OP2_PSRAD_VdqWdq = 0xE2, OP2_PORDQ_VdqWdq = 0xEB, OP2_PXORDQ_VdqWdq = 0xEF, + OP2_PSLLW_VdqWdq = 0xF1, OP2_PSLLD_VdqWdq = 0xF2, OP2_PMULUDQ_VdqWdq = 0xF4, OP2_PSUBB_VdqWdq = 0xF8, diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h index bfa39c7dfafa..5c6d267e6af5 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h @@ -971,22 +971,41 @@ class MacroAssemblerX86Shared : public Assembler vsqrtps(src, dest); } - void packedLeftShiftByScalar(FloatRegister src, FloatRegister dest) { + void packedLeftShiftByScalarInt16x8(FloatRegister src, FloatRegister dest) { + vpsllw(src, dest, dest); + } + void packedLeftShiftByScalarInt16x8(Imm32 count, FloatRegister dest) { + vpsllw(count, dest, dest); + } + void packedRightShiftByScalarInt16x8(FloatRegister src, FloatRegister dest) { + vpsraw(src, dest, dest); + } + void packedRightShiftByScalarInt16x8(Imm32 count, FloatRegister dest) { + vpsraw(count, dest, dest); + } + void packedUnsignedRightShiftByScalarInt16x8(FloatRegister src, FloatRegister dest) { + vpsrlw(src, dest, dest); + } + void packedUnsignedRightShiftByScalarInt16x8(Imm32 count, FloatRegister dest) { + vpsrlw(count, dest, dest); + } + + void packedLeftShiftByScalarInt32x4(FloatRegister src, FloatRegister dest) { vpslld(src, dest, dest); } - void packedLeftShiftByScalar(Imm32 count, FloatRegister dest) { + void packedLeftShiftByScalarInt32x4(Imm32 count, FloatRegister dest) { vpslld(count, dest, dest); } - void packedRightShiftByScalar(FloatRegister src, FloatRegister dest) { + void packedRightShiftByScalarInt32x4(FloatRegister src, FloatRegister dest) { vpsrad(src, dest, dest); } - void packedRightShiftByScalar(Imm32 count, FloatRegister dest) { + void packedRightShiftByScalarInt32x4(Imm32 count, FloatRegister dest) { vpsrad(count, dest, dest); } - void packedUnsignedRightShiftByScalar(FloatRegister src, FloatRegister dest) { + void packedUnsignedRightShiftByScalarInt32x4(FloatRegister src, FloatRegister dest) { vpsrld(src, dest, dest); } - void packedUnsignedRightShiftByScalar(Imm32 count, FloatRegister dest) { + void packedUnsignedRightShiftByScalarInt32x4(Imm32 count, FloatRegister dest) { vpsrld(count, dest, dest); } From 74578209dd2240d9090a39312fe2b1a338c814dd Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:18 -0700 Subject: [PATCH 047/199] Bug 1136226 - Implement 8x16 SIMD shift operators. r=bbouvier These operations don't have SSE instructions, so express them in terms of 16x8 shifts in MSimdShift::AddLegalized. --- js/src/asmjs/WasmIonCompile.cpp | 9 +- js/src/jit/MCallOptimize.cpp | 2 +- js/src/jit/MIR.cpp | 91 ++++++++++++++++++- js/src/jit/MIR.h | 7 ++ .../x86-shared/CodeGenerator-x86-shared.cpp | 10 +- 5 files changed, 102 insertions(+), 17 deletions(-) diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index 225cf674159b..97537ded958e 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -349,15 +349,12 @@ class FunctionCompiler return MSimdBinaryComp::AddLegalized(alloc(), curBlock_, lhs, rhs, op, sign); } - template - MDefinition* binarySimd(MDefinition* lhs, MDefinition* rhs, typename T::Operation op) + MDefinition* binarySimdShift(MDefinition* lhs, MDefinition* rhs, MSimdShift::Operation op) { if (inDeadCode()) return nullptr; - T* ins = T::New(alloc(), lhs, rhs, op); - curBlock_->add(ins); - return ins; + return MSimdShift::AddLegalized(alloc(), curBlock_, lhs, rhs, op); } MDefinition* swizzleSimd(MDefinition* vector, const uint8_t lanes[], MIRType type) @@ -2406,7 +2403,7 @@ EmitSimdShift(FunctionCompiler& f, ValType operandType, MSimdShift::Operation op if (!f.iter().readSimdShiftByScalar(operandType, &lhs, &rhs)) return false; - f.iter().setResult(f.binarySimd(lhs, rhs, op)); + f.iter().setResult(f.binarySimdShift(lhs, rhs, op)); return true; } diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index ff0dcc3176b8..276af8825da4 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -3574,7 +3574,7 @@ IonBuilder::inlineSimdShift(CallInfo& callInfo, JSNative native, MSimdShift::Ope MDefinition* vec = unboxSimd(callInfo.getArg(0), type); - MInstruction* ins = MSimdShift::New(alloc(), vec, callInfo.getArg(1), op); + MInstruction* ins = MSimdShift::AddLegalized(alloc(), current, vec, callInfo.getArg(1), op); return boxSimd(callInfo, ins, templateObj); } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 509eae372627..9f7b098d208d 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1267,8 +1267,7 @@ MSimdConvert::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition // Compute hi = obj >> 16 (lane-wise unsigned shift). MInstruction* c16 = MConstant::New(alloc, Int32Value(16)); addTo->add(c16); - MInstruction* hi = MSimdShift::New(alloc, obj, c16, MSimdShift::ursh); - addTo->add(hi); + MInstruction* hi = MSimdShift::AddLegalized(alloc, addTo, obj, c16, MSimdShift::ursh); // Compute lo = obj & 0xffff (lane-wise). MInstruction* m16 = @@ -1367,6 +1366,94 @@ MSimdBinaryComp::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinit return result; } +MInstruction* +MSimdShift::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left, + MDefinition* right, Operation op) +{ + MIRType opType = left->type(); + MOZ_ASSERT(IsIntegerSimdType(opType)); + + // SSE does not provide 8x16 shift instructions. + if (opType == MIRType::Int8x16) { + // Express the shift in terms of Int16x8 shifts by splitting into even + // and odd lanes, place 8-bit lanes into the high bits of Int16x8 + // vectors `even` and `odd`. Shift, mask, combine. + // + // wide = Int16x8.fromInt8x16Bits(left); + // shiftBy = right & 7 + // mask = Int16x8.splat(0xff00); + // + MInstruction* wide = MSimdReinterpretCast::New(alloc, left, MIRType::Int16x8); + addTo->add(wide); + + // wide = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx + + MInstruction* shiftMask = MConstant::New(alloc, Int32Value(7)); + addTo->add(shiftMask); + MBinaryBitwiseInstruction* shiftBy = MBitAnd::New(alloc, right, shiftMask); + shiftBy->setInt32Specialization(); + addTo->add(shiftBy); + + // Move the even 8x16 lanes into the high bits of the 16x8 lanes. + MInstruction* eight = MConstant::New(alloc, Int32Value(8)); + addTo->add(eight); + MInstruction* even = MSimdShift::AddLegalized(alloc, addTo, wide, eight, lsh); + + // Leave the odd lanes in place. + MInstruction* odd = wide; + + // even = xx00 xx00 xx00 xx00 xx00 xx00 xx00 xx00 + // odd = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx + + MInstruction* mask = + MSimdConstant::New(alloc, SimdConstant::SplatX8(int16_t(0xff00)), MIRType::Int16x8); + addTo->add(mask); + + // Left-shift: Clear the low bits in `odd` before shifting. + if (op == lsh) { + odd = MSimdBinaryBitwise::New(alloc, odd, mask, MSimdBinaryBitwise::and_); + addTo->add(odd); + // odd = yy00 yy00 yy00 yy00 yy00 yy00 yy00 yy00 + } + + // Do the real shift twice: once for the even lanes, once for the odd + // lanes. This is a recursive call, but with a different type. + even = MSimdShift::AddLegalized(alloc, addTo, even, shiftBy, op); + odd = MSimdShift::AddLegalized(alloc, addTo, odd, shiftBy, op); + + // even = XX~~ XX~~ XX~~ XX~~ XX~~ XX~~ XX~~ XX~~ + // odd = YY~~ YY~~ YY~~ YY~~ YY~~ YY~~ YY~~ YY~~ + + // Right-shift: Clear the low bits in `odd` after shifting. + if (op != lsh) { + odd = MSimdBinaryBitwise::New(alloc, odd, mask, MSimdBinaryBitwise::and_); + addTo->add(odd); + // odd = YY00 YY00 YY00 YY00 YY00 YY00 YY00 YY00 + } + + // Move the even lanes back to their original place. + even = MSimdShift::AddLegalized(alloc, addTo, even, eight, ursh); + + // Now, `odd` contains the odd lanes properly shifted, and `even` + // contains the even lanes properly shifted: + // + // even = 00XX 00XX 00XX 00XX 00XX 00XX 00XX 00XX + // odd = YY00 YY00 YY00 YY00 YY00 YY00 YY00 YY00 + // + // Combine: + MInstruction* result = MSimdBinaryBitwise::New(alloc, even, odd, MSimdBinaryBitwise::or_); + addTo->add(result); + result = MSimdReinterpretCast::New(alloc, result, opType); + addTo->add(result); + return result; + } + + // This is a legal operation already. Just create the instruction requested. + MInstruction* result = MSimdShift::New(alloc, left, right, op); + addTo->add(result); + return result; +} + template static void PrintOpcodeOperation(T* mir, GenericPrinter& out) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index bc0986517bfe..c0ae32ec154d 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2491,6 +2491,13 @@ class MSimdShift return new(alloc) MSimdShift(left, right, op); } + // Create an MSimdShift instruction and add it to the basic block. Possibly + // create and add an equivalent sequence of instructions instead if the + // current target doesn't support the requested shift operation directly. + // Return the inserted MInstruction that computes the shifted value. + static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left, + MDefinition* right, Operation op); + // Get the relevant right shift operation given the signedness of a type. static Operation rshForSign(SimdSign sign) { return sign == SimdSign::Unsigned ? ursh : rsh; diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 7ae778cc95ce..00088eb348aa 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2635,16 +2635,10 @@ CodeGeneratorX86Shared::visitSimdReinterpretCast(LSimdReinterpretCast* ins) if (input.aliases(output)) return; - switch (ins->mir()->type()) { - case MIRType::Int32x4: + if (IsIntegerSimdType(ins->mir()->type())) masm.vmovdqa(input, output); - break; - case MIRType::Float32x4: + else masm.vmovaps(input, output); - break; - default: - MOZ_CRASH("Unknown SIMD kind"); - } } // Extract an integer lane from the 32x4 vector register |input| and place it in From ba2db26708ba8bda9fa012930111954910b41ade Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:18 -0700 Subject: [PATCH 048/199] Bug 1136226 - Implement 8x16 SIMD multiplies. r=bbouvier There are no 8x16 SSE multiply instructions, so expand these multiplies in terms of 16x8 multiplies. --- js/src/asmjs/WasmIonCompile.cpp | 4 +- js/src/jit/MIR.cpp | 85 ++++++++++++++++++++++++++++----- js/src/jit/MIR.h | 6 +++ 3 files changed, 81 insertions(+), 14 deletions(-) diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index 97537ded958e..62569c04b2d3 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -322,9 +322,7 @@ class FunctionCompiler MOZ_ASSERT(IsSimdType(lhs->type()) && rhs->type() == lhs->type()); MOZ_ASSERT(lhs->type() == type); - auto* ins = MSimdBinaryArith::New(alloc(), lhs, rhs, op); - curBlock_->add(ins); - return ins; + return MSimdBinaryArith::AddLegalized(alloc(), curBlock_, lhs, rhs, op); } MDefinition* binarySimd(MDefinition* lhs, MDefinition* rhs, MSimdBinaryBitwise::Operation op, diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 9f7b098d208d..f4a60177d58c 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1301,15 +1301,11 @@ MSimdConvert::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition MSimdConstant::New(alloc, SimdConstant::SplatX4(BiasValue), MIRType::Float32x4); addTo->add(bias); MInstruction* fhi_debiased = - MSimdBinaryArith::New(alloc, fhi, bias, MSimdBinaryArith::Op_sub); - addTo->add(fhi_debiased); + MSimdBinaryArith::AddLegalized(alloc, addTo, fhi, bias, MSimdBinaryArith::Op_sub); // Compute the final result. - MInstruction* result = - MSimdBinaryArith::New(alloc, fhi_debiased, flo, MSimdBinaryArith::Op_add); - addTo->add(result); - - return result; + return MSimdBinaryArith::AddLegalized(alloc, addTo, fhi_debiased, flo, + MSimdBinaryArith::Op_add); } if (fromType == MIRType::Float32x4 && toType == MIRType::Int32x4) { @@ -1341,10 +1337,10 @@ MSimdBinaryComp::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinit addTo->add(bias); // Add the bias. - MInstruction* bleft = MSimdBinaryArith::New(alloc, left, bias, MSimdBinaryArith::Op_add); - addTo->add(bleft); - MInstruction* bright = MSimdBinaryArith::New(alloc, right, bias, MSimdBinaryArith::Op_add); - addTo->add(bright); + MInstruction* bleft = + MSimdBinaryArith::AddLegalized(alloc, addTo, left, bias, MSimdBinaryArith::Op_add); + MInstruction* bright = + MSimdBinaryArith::AddLegalized(alloc, addTo, right, bias, MSimdBinaryArith::Op_add); // Do the equivalent signed comparison. MInstruction* result = MSimdBinaryComp::New(alloc, bleft, bright, op, SimdSign::Signed); @@ -1366,6 +1362,73 @@ MSimdBinaryComp::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinit return result; } +MInstruction* +MSimdBinaryArith::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left, + MDefinition* right, Operation op) +{ + MOZ_ASSERT(left->type() == right->type()); + MIRType opType = left->type(); + MOZ_ASSERT(IsSimdType(opType)); + + // SSE does not have 8x16 multiply instructions. + if (opType == MIRType::Int8x16 && op == Op_mul) { + // Express the multiply in terms of Int16x8 multiplies by handling the + // even and odd lanes separately. + + MInstruction* wideL = MSimdReinterpretCast::New(alloc, left, MIRType::Int16x8); + addTo->add(wideL); + MInstruction* wideR = MSimdReinterpretCast::New(alloc, right, MIRType::Int16x8); + addTo->add(wideR); + + // wideL = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx + // wideR = bbaa bbaa bbaa bbaa bbaa bbaa bbaa bbaa + + // Shift the odd lanes down to the low bits of the 16x8 vectors. + MInstruction* eight = MConstant::New(alloc, Int32Value(8)); + addTo->add(eight); + MInstruction* evenL = wideL; + MInstruction* evenR = wideR; + MInstruction* oddL = + MSimdShift::AddLegalized(alloc, addTo, wideL, eight, MSimdShift::ursh); + MInstruction* oddR = + MSimdShift::AddLegalized(alloc, addTo, wideR, eight, MSimdShift::ursh); + + // evenL = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx + // evenR = bbaa bbaa bbaa bbaa bbaa bbaa bbaa bbaa + // oddL = 00yy 00yy 00yy 00yy 00yy 00yy 00yy 00yy + // oddR = 00bb 00bb 00bb 00bb 00bb 00bb 00bb 00bb + + // Now do two 16x8 multiplications. We can use the low bits of each. + MInstruction* even = MSimdBinaryArith::AddLegalized(alloc, addTo, evenL, evenR, Op_mul); + MInstruction* odd = MSimdBinaryArith::AddLegalized(alloc, addTo, oddL, oddR, Op_mul); + + // even = ~~PP ~~PP ~~PP ~~PP ~~PP ~~PP ~~PP ~~PP + // odd = ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ + + MInstruction* mask = + MSimdConstant::New(alloc, SimdConstant::SplatX8(int16_t(0x00ff)), MIRType::Int16x8); + addTo->add(mask); + even = MSimdBinaryBitwise::New(alloc, even, mask, MSimdBinaryBitwise::and_); + addTo->add(even); + odd = MSimdShift::AddLegalized(alloc, addTo, odd, eight, MSimdShift::lsh); + + // even = 00PP 00PP 00PP 00PP 00PP 00PP 00PP 00PP + // odd = QQ00 QQ00 QQ00 QQ00 QQ00 QQ00 QQ00 QQ00 + + // Combine: + MInstruction* result = MSimdBinaryBitwise::New(alloc, even, odd, MSimdBinaryBitwise::or_); + addTo->add(result); + result = MSimdReinterpretCast::New(alloc, result, opType); + addTo->add(result); + return result; + } + + // This is a legal operation already. Just create the instruction requested. + MInstruction* result = MSimdBinaryArith::New(alloc, left, right, op); + addTo->add(result); + return result; +} + MInstruction* MSimdShift::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left, MDefinition* right, Operation op) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index c0ae32ec154d..e1caa5dddab9 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2383,6 +2383,12 @@ class MSimdBinaryArith return new(alloc) MSimdBinaryArith(left, right, op); } + // Create an MSimdBinaryArith instruction and add it to the basic block. Possibly + // create and add an equivalent sequence of instructions instead if the + // current target doesn't support the requested shift operation directly. + static MInstruction* AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left, + MDefinition* right, Operation op); + AliasSet getAliasSet() const override { return AliasSet::None(); } From f85827cd00213fcd8dcc8d4280aafa39950d284c Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:19 -0700 Subject: [PATCH 049/199] Bug 1136226 - Implement SIMD saturating arithmetic. r=bbouvier --- js/src/asmjs/WasmIonCompile.cpp | 30 +++- js/src/jit/MIR.cpp | 5 + js/src/jit/MIR.h | 66 +++++++++ js/src/jit/MOpcodes.h | 1 + js/src/jit/none/Lowering-none.h | 1 + js/src/jit/shared/LIR-shared.h | 28 ++++ js/src/jit/shared/LOpcodes-shared.h | 1 + js/src/jit/shared/Lowering-shared.h | 1 + js/src/jit/x86-shared/Assembler-x86-shared.h | 128 ++++++++++++++++++ .../jit/x86-shared/BaseAssembler-x86-shared.h | 104 ++++++++++++++ .../x86-shared/CodeGenerator-x86-shared.cpp | 51 +++++++ .../jit/x86-shared/CodeGenerator-x86-shared.h | 1 + js/src/jit/x86-shared/Encoding-x86-shared.h | 8 ++ js/src/jit/x86-shared/Lowering-x86-shared.cpp | 17 +++ js/src/jit/x86-shared/Lowering-x86-shared.h | 1 + 15 files changed, 441 insertions(+), 2 deletions(-) diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index 62569c04b2d3..4bdbb805ed3d 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -347,6 +347,17 @@ class FunctionCompiler return MSimdBinaryComp::AddLegalized(alloc(), curBlock_, lhs, rhs, op, sign); } + MDefinition* binarySimdSaturating(MDefinition* lhs, MDefinition* rhs, + MSimdBinarySaturating::Operation op, SimdSign sign) + { + if (inDeadCode()) + return nullptr; + + auto* ins = MSimdBinarySaturating::New(alloc(), lhs, rhs, op, sign); + curBlock_->add(ins); + return ins; + } + MDefinition* binarySimdShift(MDefinition* lhs, MDefinition* rhs, MSimdShift::Operation op) { if (inDeadCode()) @@ -2393,6 +2404,19 @@ EmitSimdBinaryComp(FunctionCompiler& f, ValType operandType, MSimdBinaryComp::Op return true; } +static bool +EmitSimdBinarySaturating(FunctionCompiler& f, ValType type, MSimdBinarySaturating::Operation op, + SimdSign sign) +{ + MDefinition* lhs; + MDefinition* rhs; + if (!f.iter().readBinary(type, &lhs, &rhs)) + return false; + + f.iter().setResult(f.binarySimdSaturating(lhs, rhs, op, sign)); + return true; +} + static bool EmitSimdShift(FunctionCompiler& f, ValType operandType, MSimdShift::Operation op) { @@ -2788,6 +2812,10 @@ EmitSimdOp(FunctionCompiler& f, ValType type, SimdOperation op, SimdSign sign) FOREACH_NUMERIC_SIMD_BINOP(_CASE) FOREACH_FLOAT_SIMD_BINOP(_CASE) #undef _CASE + case SimdOperation::Fn_addSaturate: + return EmitSimdBinarySaturating(f, type, MSimdBinarySaturating::add, sign); + case SimdOperation::Fn_subSaturate: + return EmitSimdBinarySaturating(f, type, MSimdBinarySaturating::sub, sign); case SimdOperation::Fn_fromFloat32x4: return EmitSimdConvert(f, ValType::F32x4, type, sign); case SimdOperation::Fn_fromInt32x4: @@ -2804,8 +2832,6 @@ EmitSimdOp(FunctionCompiler& f, ValType type, SimdOperation op, SimdSign sign) case SimdOperation::Fn_fromUint8x16Bits: case SimdOperation::Fn_fromUint16x8Bits: case SimdOperation::Fn_fromFloat64x2Bits: - case SimdOperation::Fn_addSaturate: - case SimdOperation::Fn_subSaturate: MOZ_CRASH("NYI"); } MOZ_CRASH("unexpected opcode"); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index f4a60177d58c..eafd3b349ad3 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1531,6 +1531,11 @@ MSimdBinaryArith::printOpcode(GenericPrinter& out) const PrintOpcodeOperation(this, out); } void +MSimdBinarySaturating::printOpcode(GenericPrinter& out) const +{ + PrintOpcodeOperation(this, out); +} +void MSimdBinaryBitwise::printOpcode(GenericPrinter& out) const { PrintOpcodeOperation(this, out); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index e1caa5dddab9..e889d0357be9 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2406,6 +2406,72 @@ class MSimdBinaryArith ALLOW_CLONE(MSimdBinaryArith) }; +class MSimdBinarySaturating + : public MBinaryInstruction, + public MixPolicy, SimdSameAsReturnedTypePolicy<1>>::Data +{ + public: + enum Operation + { + add, + sub, + }; + + static const char* OperationName(Operation op) + { + switch (op) { + case add: + return "add"; + case sub: + return "sub"; + } + MOZ_CRASH("unexpected operation"); + } + + private: + Operation operation_; + SimdSign sign_; + + MSimdBinarySaturating(MDefinition* left, MDefinition* right, Operation op, SimdSign sign) + : MBinaryInstruction(left, right) + , operation_(op) + , sign_(sign) + { + MOZ_ASSERT(left->type() == right->type()); + MIRType type = left->type(); + MOZ_ASSERT(type == MIRType::Int8x16 || type == MIRType::Int16x8); + setResultType(type); + setMovable(); + if (op == add) + setCommutative(); + } + + public: + INSTRUCTION_HEADER(SimdBinarySaturating) + static MSimdBinarySaturating* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, + Operation op, SimdSign sign) + { + return new (alloc) MSimdBinarySaturating(left, right, op, sign); + } + + AliasSet getAliasSet() const override { return AliasSet::None(); } + + Operation operation() const { return operation_; } + SimdSign signedness() const { return sign_; } + + bool congruentTo(const MDefinition* ins) const override + { + if (!binaryCongruentTo(ins)) + return false; + return operation_ == ins->toSimdBinarySaturating()->operation() && + sign_ == ins->toSimdBinarySaturating()->signedness(); + } + + void printOpcode(GenericPrinter& out) const override; + + ALLOW_CLONE(MSimdBinarySaturating) +}; + class MSimdBinaryBitwise : public MBinaryInstruction, public MixPolicy, SimdSameAsReturnedTypePolicy<1> >::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 0ebe9f860725..a5991e1e4f98 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -27,6 +27,7 @@ namespace jit { _(SimdUnaryArith) \ _(SimdBinaryComp) \ _(SimdBinaryArith) \ + _(SimdBinarySaturating) \ _(SimdBinaryBitwise) \ _(SimdShift) \ _(SimdSelect) \ diff --git a/js/src/jit/none/Lowering-none.h b/js/src/jit/none/Lowering-none.h index 68cd00e23b1d..1529475a4d0c 100644 --- a/js/src/jit/none/Lowering-none.h +++ b/js/src/jit/none/Lowering-none.h @@ -94,6 +94,7 @@ class LIRGeneratorNone : public LIRGeneratorShared void visitSimdValueX4(MSimdValueX4* lir) { MOZ_CRASH(); } void visitSubstr(MSubstr*) { MOZ_CRASH(); } void visitSimdBinaryArith(js::jit::MSimdBinaryArith*) { MOZ_CRASH(); } + void visitSimdBinarySaturating(MSimdBinarySaturating* ins) { MOZ_CRASH(); } void visitRandom(js::jit::MRandom*) { MOZ_CRASH(); } void visitWasmTruncateToInt64(MWasmTruncateToInt64*) { MOZ_CRASH(); } void visitInt64ToFloatingPoint(MInt64ToFloatingPoint*) { MOZ_CRASH(); } diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 22c539d3118f..faa48ab55e79 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -529,6 +529,34 @@ class LSimdBinaryArithFx4 : public LSimdBinaryArith LSimdBinaryArithFx4() : LSimdBinaryArith() {} }; +// Binary SIMD saturating arithmetic operation between two SIMD operands +class LSimdBinarySaturating : public LInstructionHelper<1, 2, 0> +{ + public: + LIR_HEADER(SimdBinarySaturating); + LSimdBinarySaturating() {} + + const LAllocation* lhs() { + return this->getOperand(0); + } + const LAllocation* rhs() { + return this->getOperand(1); + } + + MSimdBinarySaturating::Operation operation() const { + return this->mir_->toSimdBinarySaturating()->operation(); + } + SimdSign signedness() const { + return this->mir_->toSimdBinarySaturating()->signedness(); + } + MIRType type() const { + return mir_->type(); + } + const char* extraName() const { + return MSimdBinarySaturating::OperationName(operation()); + } +}; + // Unary SIMD arithmetic operation on a SIMD operand class LSimdUnaryArith : public LInstructionHelper<1, 1, 0> { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 38b5bcb838c7..6a1d45edb285 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -47,6 +47,7 @@ _(SimdBinaryArithIx8) \ _(SimdBinaryArithIx4) \ _(SimdBinaryArithFx4) \ + _(SimdBinarySaturating) \ _(SimdBinaryBitwise) \ _(SimdShift) \ _(SimdSelect) \ diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index b75b696b1a30..ea31f4d2aa5f 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -277,6 +277,7 @@ class LIRGeneratorShared : public MDefinitionVisitor void visitSimdSelect(MSimdSelect*) override { MOZ_CRASH("NYI"); } void visitSimdSplat(MSimdSplat*) override { MOZ_CRASH("NYI"); } void visitSimdValueX4(MSimdValueX4*) override { MOZ_CRASH("NYI"); } + void visitSimdBinarySaturating(MSimdBinarySaturating*) override { MOZ_CRASH("NYI"); } }; } // namespace jit diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index a78f26c40022..f1bb123f02e0 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -2496,6 +2496,70 @@ class AssemblerX86Shared : public AssemblerShared MOZ_CRASH("unexpected operand kind"); } } + void vpaddsb(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpaddsb_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpaddsb_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpaddsb_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpaddusb(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpaddusb_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpaddusb_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpaddusb_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpsubsb(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpsubsb_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpsubsb_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpsubsb_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpsubusb(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpsubusb_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpsubusb_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpsubusb_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } void vpaddw(const Operand& src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); switch (src1.kind()) { @@ -2528,6 +2592,70 @@ class AssemblerX86Shared : public AssemblerShared MOZ_CRASH("unexpected operand kind"); } } + void vpaddsw(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpaddsw_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpaddsw_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpaddsw_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpaddusw(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpaddusw_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpaddusw_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpaddusw_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpsubsw(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpsubsw_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpsubsw_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpsubsw_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpsubusw(const Operand& src1, FloatRegister src0, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src1.kind()) { + case Operand::FPREG: + masm.vpsubusw_rr(src1.fpu(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpsubusw_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpsubusw_mr(src1.address(), src0.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } void vpaddd(const Operand& src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); switch (src1.kind()) { diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index 96d34078280d..da6fd56c00df 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -596,6 +596,32 @@ public: twoByteOpSimd("vpaddb", VEX_PD, OP2_PADDB_VdqWdq, address, src0, dst); } + void vpaddsb_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddsb", VEX_PD, OP2_PADDSB_VdqWdq, src1, src0, dst); + } + void vpaddsb_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddsb", VEX_PD, OP2_PADDSB_VdqWdq, offset, base, src0, dst); + } + void vpaddsb_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddsb", VEX_PD, OP2_PADDSB_VdqWdq, address, src0, dst); + } + + void vpaddusb_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddusb", VEX_PD, OP2_PADDUSB_VdqWdq, src1, src0, dst); + } + void vpaddusb_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddusb", VEX_PD, OP2_PADDUSB_VdqWdq, offset, base, src0, dst); + } + void vpaddusb_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddusb", VEX_PD, OP2_PADDUSB_VdqWdq, address, src0, dst); + } + void vpaddw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpaddw", VEX_PD, OP2_PADDW_VdqWdq, src1, src0, dst); @@ -609,6 +635,32 @@ public: twoByteOpSimd("vpaddw", VEX_PD, OP2_PADDW_VdqWdq, address, src0, dst); } + void vpaddsw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddsw", VEX_PD, OP2_PADDSW_VdqWdq, src1, src0, dst); + } + void vpaddsw_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddsw", VEX_PD, OP2_PADDSW_VdqWdq, offset, base, src0, dst); + } + void vpaddsw_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddsw", VEX_PD, OP2_PADDSW_VdqWdq, address, src0, dst); + } + + void vpaddusw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddusw", VEX_PD, OP2_PADDUSW_VdqWdq, src1, src0, dst); + } + void vpaddusw_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddusw", VEX_PD, OP2_PADDUSW_VdqWdq, offset, base, src0, dst); + } + void vpaddusw_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpaddusw", VEX_PD, OP2_PADDUSW_VdqWdq, address, src0, dst); + } + void vpaddd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpaddd", VEX_PD, OP2_PADDD_VdqWdq, src1, src0, dst); @@ -635,6 +687,32 @@ public: twoByteOpSimd("vpsubb", VEX_PD, OP2_PSUBB_VdqWdq, address, src0, dst); } + void vpsubsb_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubsb", VEX_PD, OP2_PSUBSB_VdqWdq, src1, src0, dst); + } + void vpsubsb_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubsb", VEX_PD, OP2_PSUBSB_VdqWdq, offset, base, src0, dst); + } + void vpsubsb_mr(const void* subress, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubsb", VEX_PD, OP2_PSUBSB_VdqWdq, subress, src0, dst); + } + + void vpsubusb_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubusb", VEX_PD, OP2_PSUBUSB_VdqWdq, src1, src0, dst); + } + void vpsubusb_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubusb", VEX_PD, OP2_PSUBUSB_VdqWdq, offset, base, src0, dst); + } + void vpsubusb_mr(const void* subress, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubusb", VEX_PD, OP2_PSUBUSB_VdqWdq, subress, src0, dst); + } + void vpsubw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpsubw", VEX_PD, OP2_PSUBW_VdqWdq, src1, src0, dst); @@ -648,6 +726,32 @@ public: twoByteOpSimd("vpsubw", VEX_PD, OP2_PSUBW_VdqWdq, address, src0, dst); } + void vpsubsw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubsw", VEX_PD, OP2_PSUBSW_VdqWdq, src1, src0, dst); + } + void vpsubsw_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubsw", VEX_PD, OP2_PSUBSW_VdqWdq, offset, base, src0, dst); + } + void vpsubsw_mr(const void* subress, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubsw", VEX_PD, OP2_PSUBSW_VdqWdq, subress, src0, dst); + } + + void vpsubusw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubusw", VEX_PD, OP2_PSUBUSW_VdqWdq, src1, src0, dst); + } + void vpsubusw_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubusw", VEX_PD, OP2_PSUBUSW_VdqWdq, offset, base, src0, dst); + } + void vpsubusw_mr(const void* subress, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpsubusw", VEX_PD, OP2_PSUBUSW_VdqWdq, subress, src0, dst); + } + void vpsubd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpsubd", VEX_PD, OP2_PSUBD_VdqWdq, src1, src0, dst); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 00088eb348aa..f81b7d96f924 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -3586,6 +3586,57 @@ CodeGeneratorX86Shared::visitSimdBinaryArithFx4(LSimdBinaryArithFx4* ins) MOZ_CRASH("unexpected SIMD op"); } +void +CodeGeneratorX86Shared::visitSimdBinarySaturating(LSimdBinarySaturating* ins) +{ + FloatRegister lhs = ToFloatRegister(ins->lhs()); + Operand rhs = ToOperand(ins->rhs()); + FloatRegister output = ToFloatRegister(ins->output()); + + SimdSign sign = ins->signedness(); + MOZ_ASSERT(sign != SimdSign::NotApplicable); + + switch (ins->type()) { + case MIRType::Int8x16: + switch (ins->operation()) { + case MSimdBinarySaturating::add: + if (sign == SimdSign::Signed) + masm.vpaddsb(rhs, lhs, output); + else + masm.vpaddusb(rhs, lhs, output); + return; + case MSimdBinarySaturating::sub: + if (sign == SimdSign::Signed) + masm.vpsubsb(rhs, lhs, output); + else + masm.vpsubusb(rhs, lhs, output); + return; + } + break; + + case MIRType::Int16x8: + switch (ins->operation()) { + case MSimdBinarySaturating::add: + if (sign == SimdSign::Signed) + masm.vpaddsw(rhs, lhs, output); + else + masm.vpaddusw(rhs, lhs, output); + return; + case MSimdBinarySaturating::sub: + if (sign == SimdSign::Signed) + masm.vpsubsw(rhs, lhs, output); + else + masm.vpsubusw(rhs, lhs, output); + return; + } + break; + + default: + break; + } + MOZ_CRASH("unsupported type for SIMD saturating arithmetic"); +} + void CodeGeneratorX86Shared::visitSimdUnaryArithIx16(LSimdUnaryArithIx16* ins) { diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 411253edb7a1..cae6adb32782 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -317,6 +317,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void visitSimdBinaryArithIx8(LSimdBinaryArithIx8* lir); void visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir); void visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir); + void visitSimdBinarySaturating(LSimdBinarySaturating* lir); void visitSimdBinaryBitwise(LSimdBinaryBitwise* lir); void visitSimdShift(LSimdShift* lir); void visitSimdSelect(LSimdSelect* ins); diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h index 59c16ac1d861..9cc7612cdb14 100644 --- a/js/src/jit/x86-shared/Encoding-x86-shared.h +++ b/js/src/jit/x86-shared/Encoding-x86-shared.h @@ -255,11 +255,19 @@ enum TwoByteOpcodeID { OP2_PSRLD_VdqWdq = 0xD2, OP2_PMULLW_VdqWdq = 0xD5, OP2_MOVQ_WdVd = 0xD6, + OP2_PSUBUSB_VdqWdq = 0xD8, + OP2_PSUBUSW_VdqWdq = 0xD9, OP2_PANDDQ_VdqWdq = 0xDB, + OP2_PADDUSB_VdqWdq = 0xDC, + OP2_PADDUSW_VdqWdq = 0xDD, OP2_PANDNDQ_VdqWdq = 0xDF, OP2_PSRAW_VdqWdq = 0xE1, OP2_PSRAD_VdqWdq = 0xE2, + OP2_PSUBSB_VdqWdq = 0xE8, + OP2_PSUBSW_VdqWdq = 0xE9, OP2_PORDQ_VdqWdq = 0xEB, + OP2_PADDSB_VdqWdq = 0xEC, + OP2_PADDSW_VdqWdq = 0xED, OP2_PXORDQ_VdqWdq = 0xEF, OP2_PSLLW_VdqWdq = 0xF1, OP2_PSLLD_VdqWdq = 0xF2, diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index 09ab0b48642a..293b35fea33c 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -773,6 +773,23 @@ LIRGeneratorX86Shared::visitSimdBinaryArith(MSimdBinaryArith* ins) } } +void +LIRGeneratorX86Shared::visitSimdBinarySaturating(MSimdBinarySaturating* ins) +{ + MOZ_ASSERT(IsSimdType(ins->lhs()->type())); + MOZ_ASSERT(IsSimdType(ins->rhs()->type())); + MOZ_ASSERT(IsSimdType(ins->type())); + + MDefinition* lhs = ins->lhs(); + MDefinition* rhs = ins->rhs(); + + if (ins->isCommutative()) + ReorderCommutative(&lhs, &rhs, ins); + + LSimdBinarySaturating* lir = new (alloc()) LSimdBinarySaturating(); + lowerForFPU(lir, ins, lhs, rhs); +} + void LIRGeneratorX86Shared::visitSimdSelect(MSimdSelect* ins) { diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.h b/js/src/jit/x86-shared/Lowering-x86-shared.h index 60e850d99a43..b52787bee443 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.h +++ b/js/src/jit/x86-shared/Lowering-x86-shared.h @@ -59,6 +59,7 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared void visitSimdInsertElement(MSimdInsertElement* ins); void visitSimdExtractElement(MSimdExtractElement* ins); void visitSimdBinaryArith(MSimdBinaryArith* ins); + void visitSimdBinarySaturating(MSimdBinarySaturating* ins); void visitSimdSelect(MSimdSelect* ins); void visitSimdSplat(MSimdSplat* ins); void visitSimdValueX4(MSimdValueX4* ins); From 9b1061c296a9b334a30361bb8c04eb6176b0536c Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:19 -0700 Subject: [PATCH 050/199] Bug 1136226 - Implement Bool8x16.splat and Bool16x8.splat. r=bbouvier The scalar argument to this operation is expanded into MIR as either -1 or 0 in an Int32, so the 4-lane splat produces the correct result for 8-lane and 16-lane splats too. Either an all-zeroes vector or an all-ones vector. --- js/src/jit/MIR.cpp | 10 ++++ js/src/jit/shared/LIR-shared.h | 30 +++++++++++ js/src/jit/shared/LOpcodes-shared.h | 2 + js/src/jit/x86-shared/Assembler-x86-shared.h | 14 +++++ .../jit/x86-shared/BaseAssembler-x86-shared.h | 15 ++++++ .../x86-shared/CodeGenerator-x86-shared.cpp | 51 ++++++++++++++----- .../jit/x86-shared/CodeGenerator-x86-shared.h | 2 + js/src/jit/x86-shared/Encoding-x86-shared.h | 3 ++ js/src/jit/x86-shared/Lowering-x86-shared.cpp | 26 ++++++---- 9 files changed, 131 insertions(+), 22 deletions(-) diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index eafd3b349ad3..9024dc3c660e 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1161,6 +1161,16 @@ MSimdSplat::foldsTo(TempAllocator& alloc) cst = SimdConstant::SplatX4(v); break; } + case MIRType::Int8x16: { + int32_t v = op->toConstant()->toInt32(); + cst = SimdConstant::SplatX16(v); + break; + } + case MIRType::Int16x8: { + int32_t v = op->toConstant()->toInt32(); + cst = SimdConstant::SplatX8(v); + break; + } case MIRType::Int32x4: { int32_t v = op->toConstant()->toInt32(); cst = SimdConstant::SplatX4(v); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index faa48ab55e79..54657fcefffa 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -195,6 +195,36 @@ class LSimdUnbox : public LInstructionHelper<1, 1, 1> } }; +// Constructs a SIMD value with 16 equal components (int8x16). +class LSimdSplatX16 : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(SimdSplatX16) + explicit LSimdSplatX16(const LAllocation& v) + { + setOperand(0, v); + } + + MSimdSplat* mir() const { + return mir_->toSimdSplat(); + } +}; + +// Constructs a SIMD value with 8 equal components (int16x8). +class LSimdSplatX8 : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(SimdSplatX8) + explicit LSimdSplatX8(const LAllocation& v) + { + setOperand(0, v); + } + + MSimdSplat* mir() const { + return mir_->toSimdSplat(); + } +}; + // Constructs a SIMD value with 4 equal components (e.g. int32x4, float32x4). class LSimdSplatX4 : public LInstructionHelper<1, 1, 0> { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 6a1d45edb285..2c5ebd53f215 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -20,6 +20,8 @@ _(Float32) \ _(SimdBox) \ _(SimdUnbox) \ + _(SimdSplatX16) \ + _(SimdSplatX8) \ _(SimdSplatX4) \ _(Simd128Int) \ _(Simd128Float) \ diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index f1bb123f02e0..a7f90429d00d 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -1085,6 +1085,7 @@ class AssemblerX86Shared : public AssemblerShared static bool HasSSE2() { return CPUInfo::IsSSE2Present(); } static bool HasSSE3() { return CPUInfo::IsSSE3Present(); } + static bool HasSSSE3() { return CPUInfo::IsSSSE3Present(); } static bool HasSSE41() { return CPUInfo::IsSSE41Present(); } static bool HasPOPCNT() { return CPUInfo::IsPOPCNTPresent(); } static bool SupportsFloatingPoint() { return CPUInfo::IsSSE2Present(); } @@ -2996,6 +2997,19 @@ class AssemblerX86Shared : public AssemblerShared MOZ_CRASH("unexpected operand kind"); } } + + void vpshuflw(uint32_t mask, FloatRegister src, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + masm.vpshuflw_irr(mask, src.encoding(), dest.encoding()); + } + void vpshufhw(uint32_t mask, FloatRegister src, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + masm.vpshufhw_irr(mask, src.encoding(), dest.encoding()); + } + void vpshufb(FloatRegister mask, FloatRegister src, FloatRegister dest) { + MOZ_ASSERT(HasSSSE3()); + masm.vpshufb_rr(mask.encoding(), src.encoding(), dest.encoding()); + } void vmovddup(FloatRegister src, FloatRegister dest) { MOZ_ASSERT(HasSSE3()); masm.vmovddup_rr(src.encoding(), dest.encoding()); diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index da6fd56c00df..67c7590e9fed 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -2825,6 +2825,21 @@ public: twoByteOpImmSimd("vpshufd", VEX_PD, OP2_PSHUFD_VdqWdqIb, mask, address, invalid_xmm, dst); } + void vpshuflw_irr(uint32_t mask, XMMRegisterID src, XMMRegisterID dst) + { + twoByteOpImmSimd("vpshuflw", VEX_SD, OP2_PSHUFLW_VdqWdqIb, mask, src, invalid_xmm, dst); + } + + void vpshufhw_irr(uint32_t mask, XMMRegisterID src, XMMRegisterID dst) + { + twoByteOpImmSimd("vpshufhw", VEX_SS, OP2_PSHUFHW_VdqWdqIb, mask, src, invalid_xmm, dst); + } + + void vpshufb_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + threeByteOpSimd("vpshufb", VEX_PD, OP3_PSHUFB_VdqWdq, ESCAPE_38, src1, src0, dst); + } + void vshufps_irr(uint32_t mask, XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpImmSimd("vshufps", VEX_PS, OP2_SHUFPS_VpsWpsIb, mask, src1, src0, dst); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index f81b7d96f924..ad29cb449380 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2598,6 +2598,39 @@ CodeGeneratorX86Shared::visitSimdValueFloat32x4(LSimdValueFloat32x4* ins) masm.vunpcklps(tmp, output, output); } +void +CodeGeneratorX86Shared::visitSimdSplatX16(LSimdSplatX16* ins) +{ + MOZ_ASSERT(SimdTypeToLength(ins->mir()->type()) == 16); + Register input = ToRegister(ins->getOperand(0)); + FloatRegister output = ToFloatRegister(ins->output()); + masm.vmovd(input, output); + if (AssemblerX86Shared::HasSSSE3()) { + masm.zeroSimd128Int(ScratchSimd128Reg); + masm.vpshufb(ScratchSimd128Reg, output, output); + } else { + // Use two shifts to duplicate the low 8 bits into the low 16 bits. + masm.vpsllw(Imm32(8), output, output); + masm.vmovdqa(output, ScratchSimd128Reg); + masm.vpsrlw(Imm32(8), ScratchSimd128Reg, ScratchSimd128Reg); + masm.vpor(ScratchSimd128Reg, output, output); + // Then do an X8 splat. + masm.vpshuflw(0, output, output); + masm.vpshufd(0, output, output); + } +} + +void +CodeGeneratorX86Shared::visitSimdSplatX8(LSimdSplatX8* ins) +{ + MOZ_ASSERT(SimdTypeToLength(ins->mir()->type()) == 8); + Register input = ToRegister(ins->getOperand(0)); + FloatRegister output = ToFloatRegister(ins->output()); + masm.vmovd(input, output); + masm.vpshuflw(0, output, output); + masm.vpshufd(0, output, output); +} + void CodeGeneratorX86Shared::visitSimdSplatX4(LSimdSplatX4* ins) { @@ -2607,22 +2640,14 @@ CodeGeneratorX86Shared::visitSimdSplatX4(LSimdSplatX4* ins) MOZ_ASSERT(IsSimdType(mir->type())); JS_STATIC_ASSERT(sizeof(float) == sizeof(int32_t)); - switch (mir->type()) { - case MIRType::Int32x4: - case MIRType::Bool32x4: { - Register r = ToRegister(ins->getOperand(0)); - masm.vmovd(r, output); - masm.vpshufd(0, output, output); - break; - } - case MIRType::Float32x4: { + if (mir->type() == MIRType::Float32x4) { FloatRegister r = ToFloatRegister(ins->getOperand(0)); FloatRegister rCopy = masm.reusedInputFloat32x4(r, output); masm.vshufps(0, rCopy, rCopy, output); - break; - } - default: - MOZ_CRASH("Unknown SIMD kind"); + } else { + Register r = ToRegister(ins->getOperand(0)); + masm.vmovd(r, output); + masm.vpshufd(0, output, output); } } diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index cae6adb32782..02fcaa074d8c 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -291,6 +291,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared // SIMD operators void visitSimdValueInt32x4(LSimdValueInt32x4* lir); void visitSimdValueFloat32x4(LSimdValueFloat32x4* lir); + void visitSimdSplatX16(LSimdSplatX16* lir); + void visitSimdSplatX8(LSimdSplatX8* lir); void visitSimdSplatX4(LSimdSplatX4* lir); void visitSimd128Int(LSimd128Int* ins); void visitSimd128Float(LSimd128Float* ins); diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h index 9cc7612cdb14..633b0e906528 100644 --- a/js/src/jit/x86-shared/Encoding-x86-shared.h +++ b/js/src/jit/x86-shared/Encoding-x86-shared.h @@ -217,6 +217,8 @@ enum TwoByteOpcodeID { OP2_MOVDQ_VsdWsd = 0x6F, OP2_MOVDQ_VdqWdq = 0x6F, OP2_PSHUFD_VdqWdqIb = 0x70, + OP2_PSHUFLW_VdqWdqIb = 0x70, + OP2_PSHUFHW_VdqWdqIb = 0x70, OP2_PSLLW_UdqIb = 0x71, OP2_PSRAW_UdqIb = 0x71, OP2_PSRLW_UdqIb = 0x71, @@ -281,6 +283,7 @@ enum TwoByteOpcodeID { }; enum ThreeByteOpcodeID { + OP3_PSHUFB_VdqWdq = 0x00, OP3_ROUNDSS_VsdWsd = 0x0A, OP3_ROUNDSD_VsdWsd = 0x0B, OP3_BLENDVPS_VdqWdq = 0x14, diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index 293b35fea33c..a8dfdf8622f9 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -814,19 +814,27 @@ void LIRGeneratorX86Shared::visitSimdSplat(MSimdSplat* ins) { LAllocation x = useRegisterAtStart(ins->getOperand(0)); - LSimdSplatX4* lir = new(alloc()) LSimdSplatX4(x); switch (ins->type()) { - case MIRType::Int32x4: - case MIRType::Bool32x4: - define(lir, ins); + case MIRType::Int8x16: + define(new (alloc()) LSimdSplatX16(x), ins); break; + case MIRType::Int16x8: + define(new (alloc()) LSimdSplatX8(x), ins); + break; + case MIRType::Int32x4: case MIRType::Float32x4: - // (Non-AVX) codegen actually wants the input and the output to be in - // the same register, but we can't currently use defineReuseInput - // because they have different types (scalar vs vector), so a spill slot - // for one may not be suitable for the other. - define(lir, ins); + case MIRType::Bool8x16: + case MIRType::Bool16x8: + case MIRType::Bool32x4: + // Use the SplatX4 instruction for all boolean splats. Since the input + // value is a 32-bit int that is either 0 or -1, the X4 splat gives + // the right result for all boolean geometries. + // For floats, (Non-AVX) codegen actually wants the input and the output + // to be in the same register, but we can't currently use + // defineReuseInput because they have different types (scalar vs + // vector), so a spill slot for one may not be suitable for the other. + define(new (alloc()) LSimdSplatX4(x), ins); break; default: MOZ_CRASH("Unknown SIMD kind"); From 006c4cf148eb781bc2b6dd8f71d3cd7472657a47 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:19 -0700 Subject: [PATCH 051/199] Bug 1136226 - Handle SIMD global variables for x86 / x64. r=bbouvier --- js/src/jit/x64/CodeGenerator-x64.cpp | 4 ++++ js/src/jit/x86/CodeGenerator-x86.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index c687e4857547..fe9cebf2e0df 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -1057,7 +1057,11 @@ CodeGeneratorX64::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins) break; // Aligned access: code is aligned on PageSize + there is padding // before the global data section. + case MIRType::Int8x16: + case MIRType::Int16x8: case MIRType::Int32x4: + case MIRType::Bool8x16: + case MIRType::Bool16x8: case MIRType::Bool32x4: label = masm.loadRipRelativeInt32x4(ToFloatRegister(ins->output())); break; diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 08bec2367e96..3d1eb0c40fbd 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -796,7 +796,11 @@ CodeGeneratorX86::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins) break; // Aligned access: code is aligned on PageSize + there is padding // before the global data section. + case MIRType::Int8x16: + case MIRType::Int16x8: case MIRType::Int32x4: + case MIRType::Bool8x16: + case MIRType::Bool16x8: case MIRType::Bool32x4: label = masm.vmovdqaWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output())); break; From ef09df040568980c01a6858e1b57bf8cd1f9b08c Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:19 -0700 Subject: [PATCH 052/199] Bug 1136226 - Implement select for 8x16 and 16x8 SIMD types. r=sunfish Remove a normalizing shift of the boolean selector. Boolean SIMD types are guaranteed to be represented as all-ones. --- js/src/asmjs/AsmJS.cpp | 16 +++++++--------- js/src/jit/TypePolicy.cpp | 4 ++-- .../jit/x86-shared/CodeGenerator-x86-shared.cpp | 8 +++----- js/src/jit/x86-shared/Lowering-x86-shared.cpp | 2 -- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index f63fbef42823..9b382d429adf 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -5005,22 +5005,20 @@ class CheckSimdScalarArgs class CheckSimdSelectArgs { Type formalType_; + Type maskType_; public: - explicit CheckSimdSelectArgs(SimdType t) : formalType_(t) {} + explicit CheckSimdSelectArgs(SimdType t) : formalType_(t), maskType_(GetBooleanSimdType(t)) {} bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType) const { - if (argIndex == 0) { - // First argument of select is a bool32x4 mask. - if (!(actualType <= Type::Bool32x4)) - return f.failf(arg, "%s is not a subtype of Bool32x4", actualType.toChars()); - return true; - } + // The first argument is the boolean selector, the next two are the + // values to choose from. + Type wantedType = argIndex == 0 ? maskType_ : formalType_; - if (!(actualType <= formalType_)) { + if (!(actualType <= wantedType)) { return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(), - formalType_.toChars()); + wantedType.toChars()); } return true; } diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp index 44bde5a9e1da..53019ea55900 100644 --- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -863,8 +863,8 @@ SimdShufflePolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) bool SimdSelectPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) { - // First input is the mask, which has to be a bool32x4. - MOZ_ASSERT(ins->getOperand(0)->type() == MIRType::Bool32x4); + // First input is the mask, which has to be a boolean. + MOZ_ASSERT(IsBooleanSimdType(ins->getOperand(0)->type())); // Next inputs are the two vectors of a particular type. for (unsigned i = 1; i < 3; i++) diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index ad29cb449380..b7041b7e894a 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -3918,8 +3918,10 @@ CodeGeneratorX86Shared::visitSimdSelect(LSimdSelect* ins) masm.vmovaps(mask, temp); MSimdSelect* mir = ins->mir(); + unsigned lanes = SimdTypeToLength(mir->type()); - if (AssemblerX86Shared::HasAVX()) { + if (AssemblerX86Shared::HasAVX() && lanes == 4) { + // TBD: Use vpblendvb for lanes > 4, HasAVX. masm.vblendvps(mask, onTrue, onFalse, output); return; } @@ -3927,10 +3929,6 @@ CodeGeneratorX86Shared::visitSimdSelect(LSimdSelect* ins) // SSE4.1 has plain blendvps which can do this, but it is awkward // to use because it requires the mask to be in xmm0. - // Propagate sign to all bits of mask vector, if necessary. - if (!mir->mask()->isSimdBinaryComp()) - masm.packedRightShiftByScalarInt32x4(Imm32(31), temp); - masm.bitwiseAndSimd128(Operand(temp), output); masm.bitwiseAndNotSimd128(Operand(onFalse), temp); masm.bitwiseOrSimd128(Operand(temp), output); diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index a8dfdf8622f9..80c7987fc492 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -794,8 +794,6 @@ void LIRGeneratorX86Shared::visitSimdSelect(MSimdSelect* ins) { MOZ_ASSERT(IsSimdType(ins->type())); - MOZ_ASSERT(ins->type() == MIRType::Int32x4 || ins->type() == MIRType::Float32x4, - "Unknown SIMD kind when doing bitwise operations"); LSimdSelect* lins = new(alloc()) LSimdSelect; MDefinition* r0 = ins->getOperand(0); From 0a4e75eeb55819d11f72098445e7fd5c85dcde92 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:19 -0700 Subject: [PATCH 053/199] Bug 1136226 - Implement swizzle for 8x16 and 16x8 SIMD types. r=sunfish When we have SSSE3 available, the pshufb instruction can perform any byte-wise swizzle. Without SSSE3, fall back to using byte-wise loads and stores to simulate the swizzle. This applies to CPUs from before 2006. --- js/src/asmjs/AsmJS.cpp | 33 +++++++------ js/src/jit/Lowering.cpp | 19 -------- js/src/jit/Lowering.h | 1 - js/src/jit/MIR.h | 4 ++ js/src/jit/none/Lowering-none.h | 1 + js/src/jit/shared/LIR-shared.h | 3 +- js/src/jit/shared/Lowering-shared.h | 1 + .../x86-shared/CodeGenerator-x86-shared.cpp | 48 ++++++++++++++++--- js/src/jit/x86-shared/Lowering-x86-shared.cpp | 31 ++++++++++++ js/src/jit/x86-shared/Lowering-x86-shared.h | 1 + 10 files changed, 101 insertions(+), 41 deletions(-) diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 9b382d429adf..012ea7e80320 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -5218,15 +5218,16 @@ CheckSimdCast(FunctionValidator& f, ParseNode* call, SimdType fromType, SimdType } // namespace static bool -CheckSimdShuffleSelectors(FunctionValidator& f, ParseNode* lane, int32_t lanes[4], uint32_t maxLane) +CheckSimdShuffleSelectors(FunctionValidator& f, ParseNode* lane, + mozilla::Array& lanes, unsigned numLanes, unsigned maxLane) { - for (unsigned i = 0; i < 4; i++, lane = NextNode(lane)) { + for (unsigned i = 0; i < numLanes; i++, lane = NextNode(lane)) { uint32_t u32; if (!IsLiteralInt(f.m(), lane, &u32)) return f.failf(lane, "lane selector should be a constant integer literal"); if (u32 >= maxLane) return f.failf(lane, "lane selector should be less than %u", maxLane); - lanes[i] = int32_t(u32); + lanes[i] = uint8_t(u32); } return true; } @@ -5234,9 +5235,11 @@ CheckSimdShuffleSelectors(FunctionValidator& f, ParseNode* lane, int32_t lanes[4 static bool CheckSimdSwizzle(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type) { + const unsigned numLanes = GetSimdLanes(opType); unsigned numArgs = CallArgListLength(call); - if (numArgs != 5) - return f.failf(call, "expected 5 arguments to SIMD swizzle, got %u", numArgs); + if (numArgs != 1 + numLanes) + return f.failf(call, "expected %u arguments to SIMD swizzle, got %u", 1 + numLanes, + numArgs); Type retType = opType; ParseNode* vec = CallArgList(call); @@ -5249,12 +5252,12 @@ CheckSimdSwizzle(FunctionValidator& f, ParseNode* call, SimdType opType, Type* t if (!f.writeSimdOp(opType, SimdOperation::Fn_swizzle)) return false; - int32_t lanes[4]; - if (!CheckSimdShuffleSelectors(f, NextNode(vec), lanes, 4)) + mozilla::Array lanes; + if (!CheckSimdShuffleSelectors(f, NextNode(vec), lanes, numLanes, numLanes)) return false; - for (unsigned i = 0; i < 4; i++) { - if (!f.encoder().writeFixedU8(uint8_t(lanes[i]))) + for (unsigned i = 0; i < numLanes; i++) { + if (!f.encoder().writeFixedU8(lanes[i])) return false; } @@ -5265,9 +5268,11 @@ CheckSimdSwizzle(FunctionValidator& f, ParseNode* call, SimdType opType, Type* t static bool CheckSimdShuffle(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type) { + const unsigned numLanes = GetSimdLanes(opType); unsigned numArgs = CallArgListLength(call); - if (numArgs != 6) - return f.failf(call, "expected 6 arguments to SIMD shuffle, got %u", numArgs); + if (numArgs != 2 + numLanes) + return f.failf(call, "expected %u arguments to SIMD shuffle, got %u", 2 + numLanes, + numArgs); Type retType = opType; ParseNode* arg = CallArgList(call); @@ -5282,11 +5287,11 @@ CheckSimdShuffle(FunctionValidator& f, ParseNode* call, SimdType opType, Type* t if (!f.writeSimdOp(opType, SimdOperation::Fn_shuffle)) return false; - int32_t lanes[4]; - if (!CheckSimdShuffleSelectors(f, arg, lanes, 8)) + mozilla::Array lanes; + if (!CheckSimdShuffleSelectors(f, arg, lanes, numLanes, 2 * numLanes)) return false; - for (unsigned i = 0; i < 4; i++) { + for (unsigned i = 0; i < numLanes; i++) { if (!f.encoder().writeFixedU8(uint8_t(lanes[i]))) return false; } diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 97a676e28f11..e5121df75257 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4405,25 +4405,6 @@ LIRGenerator::visitSimdAnyTrue(MSimdAnyTrue* ins) define(new(alloc()) LSimdAnyTrue(use), ins); } -void -LIRGenerator::visitSimdSwizzle(MSimdSwizzle* ins) -{ - MOZ_ASSERT(IsSimdType(ins->input()->type())); - MOZ_ASSERT(IsSimdType(ins->type())); - - if (ins->input()->type() == MIRType::Int32x4) { - LUse use = useRegisterAtStart(ins->input()); - LSimdSwizzleI* lir = new (alloc()) LSimdSwizzleI(use); - define(lir, ins); - } else if (ins->input()->type() == MIRType::Float32x4) { - LUse use = useRegisterAtStart(ins->input()); - LSimdSwizzleF* lir = new (alloc()) LSimdSwizzleF(use); - define(lir, ins); - } else { - MOZ_CRASH("Unknown SIMD kind when getting lane"); - } -} - void LIRGenerator::visitSimdGeneralShuffle(MSimdGeneralShuffle*ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 41f2a89e220b..4a7e4af6b8c3 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -294,7 +294,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitRecompileCheck(MRecompileCheck* ins); void visitSimdBox(MSimdBox* ins); void visitSimdUnbox(MSimdUnbox* ins); - void visitSimdSwizzle(MSimdSwizzle* ins); void visitSimdGeneralShuffle(MSimdGeneralShuffle* ins); void visitSimdShuffle(MSimdShuffle* ins); void visitSimdUnaryArith(MSimdUnaryArith* ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index e889d0357be9..5bf493d951a1 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1984,6 +1984,10 @@ class MSimdShuffleBase } public: + unsigned numLanes() const { + return arity_; + } + unsigned lane(unsigned i) const { MOZ_ASSERT(i < arity_); return lane_[i]; diff --git a/js/src/jit/none/Lowering-none.h b/js/src/jit/none/Lowering-none.h index 1529475a4d0c..a02680801b33 100644 --- a/js/src/jit/none/Lowering-none.h +++ b/js/src/jit/none/Lowering-none.h @@ -91,6 +91,7 @@ class LIRGeneratorNone : public LIRGeneratorShared LTableSwitchV* newLTableSwitchV(MTableSwitch*) { MOZ_CRASH(); } void visitSimdSelect(MSimdSelect* ins) { MOZ_CRASH(); } void visitSimdSplat(MSimdSplat* ins) { MOZ_CRASH(); } + void visitSimdSwizzle(MSimdSwizzle* ins) { MOZ_CRASH(); } void visitSimdValueX4(MSimdValueX4* lir) { MOZ_CRASH(); } void visitSubstr(MSubstr*) { MOZ_CRASH(); } void visitSimdBinaryArith(js::jit::MSimdBinaryArith*) { MOZ_CRASH(); } diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 54657fcefffa..0ff1d4935e90 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -365,7 +365,7 @@ class LSimdInsertElementF : public LSimdInsertElementBase }; // Base class for both int32x4 and float32x4 shuffle instructions. -class LSimdSwizzleBase : public LInstructionHelper<1, 1, 0> +class LSimdSwizzleBase : public LInstructionHelper<1, 1, 1> { public: explicit LSimdSwizzleBase(const LAllocation& base) @@ -377,6 +377,7 @@ class LSimdSwizzleBase : public LInstructionHelper<1, 1, 0> return getOperand(0); } + unsigned numLanes() const { return mir_->toSimdSwizzle()->numLanes(); } uint32_t lane(unsigned i) const { return mir_->toSimdSwizzle()->lane(i); } bool lanesMatch(uint32_t x, uint32_t y, uint32_t z, uint32_t w) const { diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index ea31f4d2aa5f..e3c4d7dc2d6e 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -278,6 +278,7 @@ class LIRGeneratorShared : public MDefinitionVisitor void visitSimdSplat(MSimdSplat*) override { MOZ_CRASH("NYI"); } void visitSimdValueX4(MSimdValueX4*) override { MOZ_CRASH("NYI"); } void visitSimdBinarySaturating(MSimdBinarySaturating*) override { MOZ_CRASH("NYI"); } + void visitSimdSwizzle(MSimdSwizzle*) override { MOZ_CRASH("NYI"); } }; } // namespace jit diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index b7041b7e894a..68559dc9355c 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2998,14 +2998,49 @@ CodeGeneratorX86Shared::visitSimdSwizzleI(LSimdSwizzleI* ins) { FloatRegister input = ToFloatRegister(ins->input()); FloatRegister output = ToFloatRegister(ins->output()); + const unsigned numLanes = ins->numLanes(); - uint32_t x = ins->lane(0); - uint32_t y = ins->lane(1); - uint32_t z = ins->lane(2); - uint32_t w = ins->lane(3); + switch (numLanes) { + case 4: { + uint32_t x = ins->lane(0); + uint32_t y = ins->lane(1); + uint32_t z = ins->lane(2); + uint32_t w = ins->lane(3); - uint32_t mask = MacroAssembler::ComputeShuffleMask(x, y, z, w); - masm.shuffleInt32(mask, input, output); + uint32_t mask = MacroAssembler::ComputeShuffleMask(x, y, z, w); + masm.shuffleInt32(mask, input, output); + return; + } + } + + // In the general case, use pshufb if it is available. Convert to a + // byte-wise swizzle. + const unsigned bytesPerLane = 16 / numLanes; + int8_t bLane[16]; + for (unsigned i = 0; i < numLanes; i++) { + for (unsigned b = 0; b < bytesPerLane; b++) { + bLane[i * bytesPerLane + b] = ins->lane(i) * bytesPerLane + b; + } + } + + if (AssemblerX86Shared::HasSSSE3()) { + ScratchSimd128Scope scratch(masm); + masm.loadConstantSimd128Int(SimdConstant::CreateX16(bLane), scratch); + FloatRegister inputCopy = masm.reusedInputInt32x4(input, output); + masm.vpshufb(scratch, inputCopy, output); + return; + } + + // Worst-case fallback for pre-SSSE3 machines. Bounce through memory. + Register temp = ToRegister(ins->getTemp(0)); + masm.reserveStack(2 * Simd128DataSize); + masm.storeAlignedSimd128Int(input, Address(StackPointer, Simd128DataSize)); + for (unsigned i = 0; i < 16; i++) { + masm.load8ZeroExtend(Address(StackPointer, Simd128DataSize + bLane[i]), temp); + masm.store8(temp, Address(StackPointer, i)); + } + masm.loadAlignedSimd128Int(Address(StackPointer, 0), output); + masm.freeStack(2 * Simd128DataSize); } void @@ -3013,6 +3048,7 @@ CodeGeneratorX86Shared::visitSimdSwizzleF(LSimdSwizzleF* ins) { FloatRegister input = ToFloatRegister(ins->input()); FloatRegister output = ToFloatRegister(ins->output()); + MOZ_ASSERT(ins->numLanes() == 4); uint32_t x = ins->lane(0); uint32_t y = ins->lane(1); diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index 80c7987fc492..cc860ab9976f 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -869,3 +869,34 @@ LIRGeneratorX86Shared::visitSimdValueX4(MSimdValueX4* ins) MOZ_CRASH("Unknown SIMD kind"); } } + +void +LIRGeneratorX86Shared::visitSimdSwizzle(MSimdSwizzle* ins) +{ + MOZ_ASSERT(IsSimdType(ins->input()->type())); + MOZ_ASSERT(IsSimdType(ins->type())); + + if (IsIntegerSimdType(ins->input()->type())) { + LUse use = useRegisterAtStart(ins->input()); + LSimdSwizzleI* lir = new (alloc()) LSimdSwizzleI(use); + define(lir, ins); + // We need a GPR temp register for pre-SSSE3 codegen (no vpshufb). + if (Assembler::HasSSSE3()) { + lir->setTemp(0, LDefinition::BogusTemp()); + } else { + // The temp must be a GPR usable with 8-bit loads and stores. +#if defined(JS_CODEGEN_X86) + lir->setTemp(0, tempFixed(ebx)); +#else + lir->setTemp(0, temp()); +#endif + } + } else if (ins->input()->type() == MIRType::Float32x4) { + LUse use = useRegisterAtStart(ins->input()); + LSimdSwizzleF* lir = new (alloc()) LSimdSwizzleF(use); + define(lir, ins); + lir->setTemp(0, LDefinition::BogusTemp()); + } else { + MOZ_CRASH("Unknown SIMD kind when getting lane"); + } +} diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.h b/js/src/jit/x86-shared/Lowering-x86-shared.h index b52787bee443..8292c858b937 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.h +++ b/js/src/jit/x86-shared/Lowering-x86-shared.h @@ -62,6 +62,7 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared void visitSimdBinarySaturating(MSimdBinarySaturating* ins); void visitSimdSelect(MSimdSelect* ins); void visitSimdSplat(MSimdSplat* ins); + void visitSimdSwizzle(MSimdSwizzle* ins); void visitSimdValueX4(MSimdValueX4* ins); void lowerCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins, bool useI386ByteRegisters); From aa35551e533b459ed7fc045868608721c572ac58 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:19 -0700 Subject: [PATCH 054/199] Bug 1136226 - Implement shuffle for 8x16 and 16x8 SIMD types. r=sunfish When SSSE3 is available, two pshufb instructions can be combined to form any shuffle. Old machines without SSSE3 bounce the two vectors through the stack. --- js/src/jit/Lowering.cpp | 20 ------- js/src/jit/Lowering.h | 1 - js/src/jit/none/Lowering-none.h | 1 + js/src/jit/shared/LIR-shared.h | 28 ++++++++- js/src/jit/shared/LOpcodes-shared.h | 1 + js/src/jit/shared/Lowering-shared.h | 1 + .../x86-shared/CodeGenerator-x86-shared.cpp | 58 +++++++++++++++++++ .../jit/x86-shared/CodeGenerator-x86-shared.h | 1 + js/src/jit/x86-shared/Lowering-x86-shared.cpp | 40 +++++++++++++ js/src/jit/x86-shared/Lowering-x86-shared.h | 1 + 10 files changed, 128 insertions(+), 24 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index e5121df75257..e6687c1eaf29 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4435,26 +4435,6 @@ LIRGenerator::visitSimdGeneralShuffle(MSimdGeneralShuffle*ins) define(lir, ins); } -void -LIRGenerator::visitSimdShuffle(MSimdShuffle* ins) -{ - MOZ_ASSERT(IsSimdType(ins->lhs()->type())); - MOZ_ASSERT(IsSimdType(ins->rhs()->type())); - MOZ_ASSERT(IsSimdType(ins->type())); - MOZ_ASSERT(ins->type() == MIRType::Int32x4 || ins->type() == MIRType::Float32x4); - - bool zFromLHS = ins->lane(2) < 4; - bool wFromLHS = ins->lane(3) < 4; - uint32_t lanesFromLHS = (ins->lane(0) < 4) + (ins->lane(1) < 4) + zFromLHS + wFromLHS; - - LSimdShuffle* lir = new (alloc()) LSimdShuffle(); - lowerForFPU(lir, ins, ins->lhs(), ins->rhs()); - - // See codegen for requirements details. - LDefinition temp = (lanesFromLHS == 3) ? tempCopy(ins->rhs(), 1) : LDefinition::BogusTemp(); - lir->setTemp(0, temp); -} - void LIRGenerator::visitSimdUnaryArith(MSimdUnaryArith* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 4a7e4af6b8c3..2359e37d31d5 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -295,7 +295,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitSimdBox(MSimdBox* ins); void visitSimdUnbox(MSimdUnbox* ins); void visitSimdGeneralShuffle(MSimdGeneralShuffle* ins); - void visitSimdShuffle(MSimdShuffle* ins); void visitSimdUnaryArith(MSimdUnaryArith* ins); void visitSimdBinaryComp(MSimdBinaryComp* ins); void visitSimdBinaryBitwise(MSimdBinaryBitwise* ins); diff --git a/js/src/jit/none/Lowering-none.h b/js/src/jit/none/Lowering-none.h index a02680801b33..87c7087c1c29 100644 --- a/js/src/jit/none/Lowering-none.h +++ b/js/src/jit/none/Lowering-none.h @@ -92,6 +92,7 @@ class LIRGeneratorNone : public LIRGeneratorShared void visitSimdSelect(MSimdSelect* ins) { MOZ_CRASH(); } void visitSimdSplat(MSimdSplat* ins) { MOZ_CRASH(); } void visitSimdSwizzle(MSimdSwizzle* ins) { MOZ_CRASH(); } + void visitSimdShuffle(MSimdShuffle* ins) { MOZ_CRASH(); } void visitSimdValueX4(MSimdValueX4* lir) { MOZ_CRASH(); } void visitSubstr(MSubstr*) { MOZ_CRASH(); } void visitSimdBinaryArith(js::jit::MSimdBinaryArith*) { MOZ_CRASH(); } diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 0ff1d4935e90..31fd2419a18f 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -443,11 +443,11 @@ class LSimdGeneralShuffleF : public LSimdGeneralShuffleBase }; // Base class for both int32x4 and float32x4 shuffle instructions. -class LSimdShuffle : public LInstructionHelper<1, 2, 1> +class LSimdShuffleX4 : public LInstructionHelper<1, 2, 1> { public: - LIR_HEADER(SimdShuffle); - LSimdShuffle() + LIR_HEADER(SimdShuffleX4); + LSimdShuffleX4() {} const LAllocation* lhs() { @@ -467,6 +467,28 @@ class LSimdShuffle : public LInstructionHelper<1, 2, 1> } }; +// Remaining shuffles (8x16, 16x8). +class LSimdShuffle : public LInstructionHelper<1, 2, 1> +{ + public: + LIR_HEADER(SimdShuffle); + LSimdShuffle() + {} + + const LAllocation* lhs() { + return getOperand(0); + } + const LAllocation* rhs() { + return getOperand(1); + } + const LDefinition* temp() { + return getTemp(0); + } + + unsigned numLanes() const { return mir_->toSimdShuffle()->numLanes(); } + unsigned lane(unsigned i) const { return mir_->toSimdShuffle()->lane(i); } +}; + // Binary SIMD comparison operation between two SIMD operands class LSimdBinaryComp: public LInstructionHelper<1, 2, 0> { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 2c5ebd53f215..20256eebb57f 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -39,6 +39,7 @@ _(SimdSwizzleI) \ _(SimdSwizzleF) \ _(SimdShuffle) \ + _(SimdShuffleX4) \ _(SimdUnaryArithIx16) \ _(SimdUnaryArithIx8) \ _(SimdUnaryArithIx4) \ diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index e3c4d7dc2d6e..d71042065cc0 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -279,6 +279,7 @@ class LIRGeneratorShared : public MDefinitionVisitor void visitSimdValueX4(MSimdValueX4*) override { MOZ_CRASH("NYI"); } void visitSimdBinarySaturating(MSimdBinarySaturating*) override { MOZ_CRASH("NYI"); } void visitSimdSwizzle(MSimdSwizzle*) override { MOZ_CRASH("NYI"); } + void visitSimdShuffle(MSimdShuffle*) override { MOZ_CRASH("NYI"); } }; } // namespace jit diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 68559dc9355c..cf3eb029ec53 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -3102,6 +3102,64 @@ CodeGeneratorX86Shared::visitSimdSwizzleF(LSimdSwizzleF* ins) void CodeGeneratorX86Shared::visitSimdShuffle(LSimdShuffle* ins) +{ + FloatRegister lhs = ToFloatRegister(ins->lhs()); + FloatRegister rhs = ToFloatRegister(ins->rhs()); + FloatRegister output = ToFloatRegister(ins->output()); + const unsigned numLanes = ins->numLanes(); + const unsigned bytesPerLane = 16 / numLanes; + + // Convert the shuffle to a byte-wise shuffle. + uint8_t bLane[16]; + for (unsigned i = 0; i < numLanes; i++) { + for (unsigned b = 0; b < bytesPerLane; b++) { + bLane[i * bytesPerLane + b] = ins->lane(i) * bytesPerLane + b; + } + } + + // Use pshufb if it is available. + if (AssemblerX86Shared::HasSSSE3()) { + FloatRegister scratch1 = ToFloatRegister(ins->temp()); + ScratchSimd128Scope scratch2(masm); + + // Use pshufb instructions to gather the lanes from each source vector. + // A negative index creates a zero lane, so the two vectors can be combined. + + // Set scratch2 = lanes from lhs. + int8_t idx[16]; + for (unsigned i = 0; i < 16; i++) + idx[i] = bLane[i] < 16 ? bLane[i] : -1; + masm.loadConstantSimd128Int(SimdConstant::CreateX16(idx), scratch1); + FloatRegister lhsCopy = masm.reusedInputInt32x4(lhs, scratch2); + masm.vpshufb(scratch1, lhsCopy, scratch2); + + // Set output = lanes from rhs. + for (unsigned i = 0; i < 16; i++) + idx[i] = bLane[i] >= 16 ? bLane[i] - 16 : -1; + masm.loadConstantSimd128Int(SimdConstant::CreateX16(idx), scratch1); + FloatRegister rhsCopy = masm.reusedInputInt32x4(rhs, output); + masm.vpshufb(scratch1, rhsCopy, output); + + // Combine. + masm.vpor(scratch2, output, output); + return; + } + + // Worst-case fallback for pre-SSE3 machines. Bounce through memory. + Register temp = ToRegister(ins->getTemp(0)); + masm.reserveStack(3 * Simd128DataSize); + masm.storeAlignedSimd128Int(lhs, Address(StackPointer, Simd128DataSize)); + masm.storeAlignedSimd128Int(rhs, Address(StackPointer, 2 * Simd128DataSize)); + for (unsigned i = 0; i < 16; i++) { + masm.load8ZeroExtend(Address(StackPointer, Simd128DataSize + bLane[i]), temp); + masm.store8(temp, Address(StackPointer, i)); + } + masm.loadAlignedSimd128Int(Address(StackPointer, 0), output); + masm.freeStack(3 * Simd128DataSize); +} + +void +CodeGeneratorX86Shared::visitSimdShuffleX4(LSimdShuffleX4* ins) { FloatRegister lhs = ToFloatRegister(ins->lhs()); Operand rhs = ToOperand(ins->rhs()); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 02fcaa074d8c..77140a401b9c 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -308,6 +308,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void visitSimdInsertElementF(LSimdInsertElementF* lir); void visitSimdSwizzleI(LSimdSwizzleI* lir); void visitSimdSwizzleF(LSimdSwizzleF* lir); + void visitSimdShuffleX4(LSimdShuffleX4* lir); void visitSimdShuffle(LSimdShuffle* lir); void visitSimdUnaryArithIx16(LSimdUnaryArithIx16* lir); void visitSimdUnaryArithIx8(LSimdUnaryArithIx8* lir); diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index cc860ab9976f..5929b8a87872 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -900,3 +900,43 @@ LIRGeneratorX86Shared::visitSimdSwizzle(MSimdSwizzle* ins) MOZ_CRASH("Unknown SIMD kind when getting lane"); } } + +void +LIRGeneratorX86Shared::visitSimdShuffle(MSimdShuffle* ins) +{ + MOZ_ASSERT(IsSimdType(ins->lhs()->type())); + MOZ_ASSERT(IsSimdType(ins->rhs()->type())); + MOZ_ASSERT(IsSimdType(ins->type())); + if (ins->type() == MIRType::Int32x4 || ins->type() == MIRType::Float32x4) { + bool zFromLHS = ins->lane(2) < 4; + bool wFromLHS = ins->lane(3) < 4; + uint32_t lanesFromLHS = (ins->lane(0) < 4) + (ins->lane(1) < 4) + zFromLHS + wFromLHS; + + LSimdShuffleX4* lir = new (alloc()) LSimdShuffleX4(); + lowerForFPU(lir, ins, ins->lhs(), ins->rhs()); + + // See codegen for requirements details. + LDefinition temp = + (lanesFromLHS == 3) ? tempCopy(ins->rhs(), 1) : LDefinition::BogusTemp(); + lir->setTemp(0, temp); + } else { + MOZ_ASSERT(ins->type() == MIRType::Int8x16 || ins->type() == MIRType::Int16x8); + LSimdShuffle* lir = new (alloc()) LSimdShuffle(); + lir->setOperand(0, useRegister(ins->lhs())); + lir->setOperand(1, useRegister(ins->rhs())); + define(lir, ins); + // We need a GPR temp register for pre-SSSE3 codegen, and an SSE temp + // when using pshufb. + if (Assembler::HasSSSE3()) { + lir->setTemp(0, temp(LDefinition::SIMD128INT)); + } else { + // The temp must be a GPR usable with 8-bit loads and stores. +#if defined(JS_CODEGEN_X86) + lir->setTemp(0, tempFixed(ebx)); +#else + lir->setTemp(0, temp()); +#endif + } + } +} + diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.h b/js/src/jit/x86-shared/Lowering-x86-shared.h index 8292c858b937..8e92bd07946e 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.h +++ b/js/src/jit/x86-shared/Lowering-x86-shared.h @@ -63,6 +63,7 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared void visitSimdSelect(MSimdSelect* ins); void visitSimdSplat(MSimdSplat* ins); void visitSimdSwizzle(MSimdSwizzle* ins); + void visitSimdShuffle(MSimdShuffle* ins); void visitSimdValueX4(MSimdValueX4* ins); void lowerCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins, bool useI386ByteRegisters); From b2053f35291f22a2afc8c4a4514029e408c202ca Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:19 -0700 Subject: [PATCH 055/199] Bug 1136226 - Add general shuffle support for 8x16 and 16x8 shuffles. r=bbouvier This instruction is used when the shuffle indexes are not compile time constants. Give the register allocator permission to spill the index arguments to GeneralShuffle instructions. There can be up to 16 of them, and they can't all be registers. Move visitSimdGeneralShuffle into the x86-specific lowering code since it has special register allocation requirements for 8-bit lanes. --- js/src/jit/Lowering.cpp | 30 --------- js/src/jit/Lowering.h | 1 - js/src/jit/shared/Lowering-shared.h | 1 + .../x86-shared/CodeGenerator-x86-shared.cpp | 16 ++++- js/src/jit/x86-shared/Lowering-x86-shared.cpp | 43 +++++++++++++ js/src/jit/x86-shared/Lowering-x86-shared.h | 1 + .../x86-shared/MacroAssembler-x86-shared.h | 62 +++++++++++++++---- 7 files changed, 109 insertions(+), 45 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index e6687c1eaf29..60b6498fd316 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4405,36 +4405,6 @@ LIRGenerator::visitSimdAnyTrue(MSimdAnyTrue* ins) define(new(alloc()) LSimdAnyTrue(use), ins); } -void -LIRGenerator::visitSimdGeneralShuffle(MSimdGeneralShuffle*ins) -{ - MOZ_ASSERT(IsSimdType(ins->type())); - - LSimdGeneralShuffleBase* lir; - if (ins->type() == MIRType::Int32x4) - lir = new (alloc()) LSimdGeneralShuffleI(temp()); - else if (ins->type() == MIRType::Float32x4) - lir = new (alloc()) LSimdGeneralShuffleF(temp()); - else - MOZ_CRASH("Unknown SIMD kind when doing a shuffle"); - - if (!lir->init(alloc(), ins->numVectors() + ins->numLanes())) - return; - - for (unsigned i = 0; i < ins->numVectors(); i++) { - MOZ_ASSERT(IsSimdType(ins->vector(i)->type())); - lir->setOperand(i, useRegister(ins->vector(i))); - } - - for (unsigned i = 0; i < ins->numLanes(); i++) { - MOZ_ASSERT(ins->lane(i)->type() == MIRType::Int32); - lir->setOperand(i + ins->numVectors(), useRegister(ins->lane(i))); - } - - assignSnapshot(lir, Bailout_BoundsCheck); - define(lir, ins); -} - void LIRGenerator::visitSimdUnaryArith(MSimdUnaryArith* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 2359e37d31d5..625b4fb961d5 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -294,7 +294,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitRecompileCheck(MRecompileCheck* ins); void visitSimdBox(MSimdBox* ins); void visitSimdUnbox(MSimdUnbox* ins); - void visitSimdGeneralShuffle(MSimdGeneralShuffle* ins); void visitSimdUnaryArith(MSimdUnaryArith* ins); void visitSimdBinaryComp(MSimdBinaryComp* ins); void visitSimdBinaryBitwise(MSimdBinaryBitwise* ins); diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index d71042065cc0..8affdf5ade62 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -280,6 +280,7 @@ class LIRGeneratorShared : public MDefinitionVisitor void visitSimdBinarySaturating(MSimdBinarySaturating*) override { MOZ_CRASH("NYI"); } void visitSimdSwizzle(MSimdSwizzle*) override { MOZ_CRASH("NYI"); } void visitSimdShuffle(MSimdShuffle*) override { MOZ_CRASH("NYI"); } + void visitSimdGeneralShuffle(MSimdGeneralShuffle*) override { MOZ_CRASH("NYI"); } }; } // namespace jit diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index cf3eb029ec53..62918ae87efc 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2946,6 +2946,7 @@ CodeGeneratorX86Shared::visitSimdGeneralShuffle(LSimdGeneralShuffleBase* ins, Re } Label bail; + const Scale laneScale = ScaleFromElemWidth(sizeof(T)); for (size_t i = 0; i < mir->numLanes(); i++) { Operand lane = ToOperand(ins->lane(i)); @@ -2954,11 +2955,11 @@ CodeGeneratorX86Shared::visitSimdGeneralShuffle(LSimdGeneralShuffleBase* ins, Re masm.j(Assembler::Above, &bail); if (lane.kind() == Operand::REG) { - masm.loadScalar(Operand(StackPointer, ToRegister(ins->lane(i)), TimesFour, Simd128DataSize), + masm.loadScalar(Operand(StackPointer, ToRegister(ins->lane(i)), laneScale, Simd128DataSize), tempRegister); } else { masm.load32(lane, laneTemp); - masm.loadScalar(Operand(StackPointer, laneTemp, TimesFour, Simd128DataSize), tempRegister); + masm.loadScalar(Operand(StackPointer, laneTemp, laneScale, Simd128DataSize), tempRegister); } masm.storeScalar(tempRegister, Address(StackPointer, i * sizeof(T))); @@ -2984,7 +2985,16 @@ CodeGeneratorX86Shared::visitSimdGeneralShuffle(LSimdGeneralShuffleBase* ins, Re void CodeGeneratorX86Shared::visitSimdGeneralShuffleI(LSimdGeneralShuffleI* ins) { - visitSimdGeneralShuffle(ins, ToRegister(ins->temp())); + switch (ins->mir()->type()) { + case MIRType::Int8x16: + return visitSimdGeneralShuffle(ins, ToRegister(ins->temp())); + case MIRType::Int16x8: + return visitSimdGeneralShuffle(ins, ToRegister(ins->temp())); + case MIRType::Int32x4: + return visitSimdGeneralShuffle(ins, ToRegister(ins->temp())); + default: + MOZ_CRASH("unsupported type for general shuffle"); + } } void CodeGeneratorX86Shared::visitSimdGeneralShuffleF(LSimdGeneralShuffleF* ins) diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index 5929b8a87872..ed5d3e68c2e6 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -940,3 +940,46 @@ LIRGeneratorX86Shared::visitSimdShuffle(MSimdShuffle* ins) } } +void +LIRGeneratorX86Shared::visitSimdGeneralShuffle(MSimdGeneralShuffle* ins) +{ + MOZ_ASSERT(IsSimdType(ins->type())); + + LSimdGeneralShuffleBase* lir; + if (IsIntegerSimdType(ins->type())) { +#if defined(JS_CODEGEN_X86) + // The temp register must be usable with 8-bit load and store + // instructions, so one of %eax-%edx. + LDefinition t; + if (ins->type() == MIRType::Int8x16) + t = tempFixed(ebx); + else + t = temp(); +#else + LDefinition t = temp(); +#endif + lir = new (alloc()) LSimdGeneralShuffleI(t); + } else if (ins->type() == MIRType::Float32x4) { + lir = new (alloc()) LSimdGeneralShuffleF(temp()); + } else { + MOZ_CRASH("Unknown SIMD kind when doing a shuffle"); + } + + if (!lir->init(alloc(), ins->numVectors() + ins->numLanes())) + return; + + for (unsigned i = 0; i < ins->numVectors(); i++) { + MOZ_ASSERT(IsSimdType(ins->vector(i)->type())); + lir->setOperand(i, useRegister(ins->vector(i))); + } + + for (unsigned i = 0; i < ins->numLanes(); i++) { + MOZ_ASSERT(ins->lane(i)->type() == MIRType::Int32); + // Note that there can be up to 16 lane arguments, so we can't assume + // that they all get an allocated register. + lir->setOperand(i + ins->numVectors(), use(ins->lane(i))); + } + + assignSnapshot(lir, Bailout_BoundsCheck); + define(lir, ins); +} diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.h b/js/src/jit/x86-shared/Lowering-x86-shared.h index 8e92bd07946e..ea04fc440f07 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.h +++ b/js/src/jit/x86-shared/Lowering-x86-shared.h @@ -64,6 +64,7 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared void visitSimdSplat(MSimdSplat* ins); void visitSimdSwizzle(MSimdSwizzle* ins); void visitSimdShuffle(MSimdShuffle* ins); + void visitSimdGeneralShuffle(MSimdGeneralShuffle* ins); void visitSimdValueX4(MSimdValueX4* ins); void lowerCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins, bool useI386ByteRegisters); diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h index 5c6d267e6af5..62e1663b6584 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h @@ -629,12 +629,18 @@ class MacroAssemblerX86Shared : public Assembler } }; + void load8ZeroExtend(const Operand& src, Register dest) { + movzbl(src, dest); + } void load8ZeroExtend(const Address& src, Register dest) { movzbl(Operand(src), dest); } void load8ZeroExtend(const BaseIndex& src, Register dest) { movzbl(Operand(src), dest); } + void load8SignExtend(const Operand& src, Register dest) { + movsbl(src, dest); + } void load8SignExtend(const Address& src, Register dest) { movsbl(Operand(src), dest); } @@ -682,6 +688,9 @@ class MacroAssemblerX86Shared : public Assembler xchgb(output, Operand(mem)); movsbl(output, output); } + void load16ZeroExtend(const Operand& src, Register dest) { + movzwl(src, dest); + } void load16ZeroExtend(const Address& src, Register dest) { movzwl(Operand(src), dest); } @@ -722,6 +731,9 @@ class MacroAssemblerX86Shared : public Assembler xchgw(output, Operand(mem)); movswl(output, output); } + void load16SignExtend(const Operand& src, Register dest) { + movswl(src, dest); + } void load16SignExtend(const Address& src, Register dest) { movswl(Operand(src), dest); } @@ -1332,24 +1344,44 @@ class MacroAssemblerX86Shared : public Assembler bool buildOOLFakeExitFrame(void* fakeReturnAddr); }; -template <> inline void -MacroAssemblerX86Shared::loadAlignedVector(const Address& src, FloatRegister dest) { - loadAlignedSimd128Int(src, dest); -} -template <> inline void -MacroAssemblerX86Shared::loadAlignedVector(const Address& src, FloatRegister dest) { +// Specialize for float to use movaps. Use movdqa for everything else. +template <> +inline void +MacroAssemblerX86Shared::loadAlignedVector(const Address& src, FloatRegister dest) +{ loadAlignedSimd128Float(src, dest); } -template <> inline void -MacroAssemblerX86Shared::storeAlignedVector(FloatRegister src, const Address& dest) { - storeAlignedSimd128Int(src, dest); +template +inline void +MacroAssemblerX86Shared::loadAlignedVector(const Address& src, FloatRegister dest) +{ + loadAlignedSimd128Int(src, dest); } -template <> inline void -MacroAssemblerX86Shared::storeAlignedVector(FloatRegister src, const Address& dest) { + +// Specialize for float to use movaps. Use movdqa for everything else. +template <> +inline void +MacroAssemblerX86Shared::storeAlignedVector(FloatRegister src, const Address& dest) +{ storeAlignedSimd128Float(src, dest); } +template +inline void +MacroAssemblerX86Shared::storeAlignedVector(FloatRegister src, const Address& dest) +{ + storeAlignedSimd128Int(src, dest); +} + +template <> inline void +MacroAssemblerX86Shared::loadScalar(const Operand& src, Register dest) { + load8ZeroExtend(src, dest); +} +template <> inline void +MacroAssemblerX86Shared::loadScalar(const Operand& src, Register dest) { + load16ZeroExtend(src, dest); +} template <> inline void MacroAssemblerX86Shared::loadScalar(const Operand& src, Register dest) { load32(src, dest); @@ -1359,6 +1391,14 @@ MacroAssemblerX86Shared::loadScalar(const Operand& src, FloatRegister des loadFloat32(src, dest); } +template <> inline void +MacroAssemblerX86Shared::storeScalar(Register src, const Address& dest) { + store8(src, dest); +} +template <> inline void +MacroAssemblerX86Shared::storeScalar(Register src, const Address& dest) { + store16(src, dest); +} template <> inline void MacroAssemblerX86Shared::storeScalar(Register src, const Address& dest) { store32(src, dest); From a917df3a54fa66c70916964c34c4ecfa0945eaca Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:19 -0700 Subject: [PATCH 056/199] Bug 1136226 - Implement compares for 8x16 and 16x8 SIMD types. r=sunfish Since SSE doesn't have unsigned comparisons, add a bias vector and use the signed comparisons instead, just like we do for the 32x4 unsigned vectors. Use 'defineReuseInput' even when SIMD input and output types differ. This is fine now since the register allocator uses a single Simd128 class for all SIMD registers. --- js/src/jit/Lowering.cpp | 35 +++-- js/src/jit/MIR.cpp | 44 ++++--- js/src/jit/arm/Assembler-arm.h | 4 +- js/src/jit/arm64/Assembler-arm64.h | 4 +- js/src/jit/mips32/Assembler-mips32.h | 4 +- js/src/jit/mips64/Assembler-mips64.h | 4 +- js/src/jit/none/Architecture-none.h | 4 +- js/src/jit/shared/LIR-shared.h | 18 ++- js/src/jit/shared/LOpcodes-shared.h | 2 + .../jit/x86-shared/Architecture-x86-shared.h | 4 +- js/src/jit/x86-shared/Assembler-x86-shared.h | 68 +++++++++- .../jit/x86-shared/BaseAssembler-x86-shared.h | 49 ++++++- .../x86-shared/CodeGenerator-x86-shared.cpp | 120 ++++++++++++++++++ .../jit/x86-shared/CodeGenerator-x86-shared.h | 2 + js/src/jit/x86-shared/Encoding-x86-shared.h | 5 +- js/src/jit/x86-shared/Lowering-x86-shared.cpp | 7 +- .../MacroAssembler-x86-shared-inl.h | 4 +- .../x86-shared/MacroAssembler-x86-shared.h | 2 +- 18 files changed, 336 insertions(+), 44 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 60b6498fd316..30a2590aeaae 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4445,15 +4445,32 @@ LIRGenerator::visitSimdBinaryComp(MSimdBinaryComp* ins) if (ShouldReorderCommutative(ins->lhs(), ins->rhs(), ins)) ins->reverse(); - if (ins->specialization() == MIRType::Int32x4) { - MOZ_ASSERT(ins->signedness() == SimdSign::Signed); - LSimdBinaryCompIx4* add = new(alloc()) LSimdBinaryCompIx4(); - lowerForCompIx4(add, ins, ins->lhs(), ins->rhs()); - } else if (ins->specialization() == MIRType::Float32x4) { - MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable); - LSimdBinaryCompFx4* add = new(alloc()) LSimdBinaryCompFx4(); - lowerForCompFx4(add, ins, ins->lhs(), ins->rhs()); - } else { + switch (ins->specialization()) { + case MIRType::Int8x16: { + MOZ_ASSERT(ins->signedness() == SimdSign::Signed); + LSimdBinaryCompIx16* add = new (alloc()) LSimdBinaryCompIx16(); + lowerForFPU(add, ins, ins->lhs(), ins->rhs()); + return; + } + case MIRType::Int16x8: { + MOZ_ASSERT(ins->signedness() == SimdSign::Signed); + LSimdBinaryCompIx8* add = new (alloc()) LSimdBinaryCompIx8(); + lowerForFPU(add, ins, ins->lhs(), ins->rhs()); + return; + } + case MIRType::Int32x4: { + MOZ_ASSERT(ins->signedness() == SimdSign::Signed); + LSimdBinaryCompIx4* add = new (alloc()) LSimdBinaryCompIx4(); + lowerForCompIx4(add, ins, ins->lhs(), ins->rhs()); + return; + } + case MIRType::Float32x4: { + MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable); + LSimdBinaryCompFx4* add = new (alloc()) LSimdBinaryCompFx4(); + lowerForCompFx4(add, ins, ins->lhs(), ins->rhs()); + return; + } + default: MOZ_CRASH("Unknown compare type when comparing values"); } } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 9024dc3c660e..10086beb490c 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1338,28 +1338,42 @@ MSimdBinaryComp::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinit MOZ_ASSERT(IsSimdType(opType)); bool IsEquality = op == equal || op == notEqual; - if (!SupportsUint32x4Compares && sign == SimdSign::Unsigned && !IsEquality) { - MOZ_ASSERT(opType == MIRType::Int32x4); + // Check if this is an unsupported unsigned compare that needs to be biased. + // If so, put the bias vector in `bias`. + if (sign == SimdSign::Unsigned && !IsEquality) { + MInstruction* bias = nullptr; + // This is an order comparison of Uint32x4 vectors which are not supported on this target. // Simply offset |left| and |right| by INT_MIN, then do a signed comparison. - MInstruction* bias = - MSimdConstant::New(alloc, SimdConstant::SplatX4(int32_t(0x80000000)), opType); - addTo->add(bias); + if (!SupportsUint32x4Compares && opType == MIRType::Int32x4) + bias = MSimdConstant::New(alloc, SimdConstant::SplatX4(int32_t(0x80000000)), opType); + else if (!SupportsUint16x8Compares && opType == MIRType::Int16x8) + bias = MSimdConstant::New(alloc, SimdConstant::SplatX8(int16_t(0x8000)), opType); + if (!SupportsUint8x16Compares && opType == MIRType::Int8x16) + bias = MSimdConstant::New(alloc, SimdConstant::SplatX16(int8_t(0x80)), opType); - // Add the bias. - MInstruction* bleft = - MSimdBinaryArith::AddLegalized(alloc, addTo, left, bias, MSimdBinaryArith::Op_add); - MInstruction* bright = - MSimdBinaryArith::AddLegalized(alloc, addTo, right, bias, MSimdBinaryArith::Op_add); + if (bias) { + addTo->add(bias); - // Do the equivalent signed comparison. - MInstruction* result = MSimdBinaryComp::New(alloc, bleft, bright, op, SimdSign::Signed); - addTo->add(result); + // Add the bias. + MInstruction* bleft = + MSimdBinaryArith::AddLegalized(alloc, addTo, left, bias, MSimdBinaryArith::Op_add); + MInstruction* bright = + MSimdBinaryArith::AddLegalized(alloc, addTo, right, bias, MSimdBinaryArith::Op_add); - return result; + // Do the equivalent signed comparison. + MInstruction* result = + MSimdBinaryComp::New(alloc, bleft, bright, op, SimdSign::Signed); + addTo->add(result); + + return result; + } } - if (!SupportsUint32x4Compares && sign == SimdSign::Unsigned && opType == MIRType::Int32x4) { + if (sign == SimdSign::Unsigned && + ((!SupportsUint32x4Compares && opType == MIRType::Int32x4) || + (!SupportsUint16x8Compares && opType == MIRType::Int16x8) || + (!SupportsUint8x16Compares && opType == MIRType::Int8x16))) { // The sign doesn't matter for equality tests. Flip it to make the // backend assertions happy. MOZ_ASSERT(IsEquality); diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index dafa7e209805..282d12602a59 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -227,7 +227,9 @@ static const uint32_t AsmJSStackAlignment = SimdMemoryAlignment; // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; -// Does this architecture support comparisons of unsigned 32x4 integer vectors? +// Does this architecture support comparisons of unsigned integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint8x16Compares = false; +static MOZ_CONSTEXPR_VAR bool SupportsUint16x8Compares = false; static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; static const Scale ScalePointer = TimesFour; diff --git a/js/src/jit/arm64/Assembler-arm64.h b/js/src/jit/arm64/Assembler-arm64.h index b99007e14355..89b19b55e8e5 100644 --- a/js/src/jit/arm64/Assembler-arm64.h +++ b/js/src/jit/arm64/Assembler-arm64.h @@ -178,7 +178,9 @@ static const int32_t AsmJSGlobalRegBias = 1024; // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; -// Does this architecture support comparisons of unsigned 32x4 integer vectors? +// Does this architecture support comparisons of unsigned integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint8x16Compares = false; +static MOZ_CONSTEXPR_VAR bool SupportsUint16x8Compares = false; static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; class Assembler : public vixl::Assembler diff --git a/js/src/jit/mips32/Assembler-mips32.h b/js/src/jit/mips32/Assembler-mips32.h index 09a3d90b9385..f66706d1574e 100644 --- a/js/src/jit/mips32/Assembler-mips32.h +++ b/js/src/jit/mips32/Assembler-mips32.h @@ -106,7 +106,9 @@ static MOZ_CONSTEXPR_VAR uint32_t AsmJSStackAlignment = SimdMemoryAlignment; // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; -// Does this architecture support comparisons of unsigned 32x4 integer vectors? +// Does this architecture support comparisons of unsigned integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint8x16Compares = false; +static MOZ_CONSTEXPR_VAR bool SupportsUint16x8Compares = false; static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; static MOZ_CONSTEXPR_VAR Scale ScalePointer = TimesFour; diff --git a/js/src/jit/mips64/Assembler-mips64.h b/js/src/jit/mips64/Assembler-mips64.h index 8b44b3583b21..038a4f385c6a 100644 --- a/js/src/jit/mips64/Assembler-mips64.h +++ b/js/src/jit/mips64/Assembler-mips64.h @@ -117,7 +117,9 @@ static MOZ_CONSTEXPR_VAR uint32_t AsmJSStackAlignment = SimdMemoryAlignment; // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; -// Does this architecture support comparisons of unsigned 32x4 integer vectors? +// Does this architecture support comparisons of unsigned integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint8x16Compares = false; +static MOZ_CONSTEXPR_VAR bool SupportsUint16x8Compares = false; static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; static MOZ_CONSTEXPR_VAR Scale ScalePointer = TimesEight; diff --git a/js/src/jit/none/Architecture-none.h b/js/src/jit/none/Architecture-none.h index f6118bead5a4..3d54a9dc303a 100644 --- a/js/src/jit/none/Architecture-none.h +++ b/js/src/jit/none/Architecture-none.h @@ -21,7 +21,9 @@ static const uint32_t AsmJSStackAlignment = 8; // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; -// Does this architecture support comparisons of unsigned 32x4 integer vectors? +// Does this architecture support comparisons of unsigned integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint8x16Compares = false; +static MOZ_CONSTEXPR_VAR bool SupportsUint16x8Compares = false; static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; class Registers diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 31fd2419a18f..7d6529945bd3 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -510,7 +510,23 @@ public: } }; -// Binary SIMD comparison operation between two Int32x4 operands +// Binary SIMD comparison operation between two Int8x16 operands. +class LSimdBinaryCompIx16 : public LSimdBinaryComp +{ + public: + LIR_HEADER(SimdBinaryCompIx16); + LSimdBinaryCompIx16() : LSimdBinaryComp() {} +}; + +// Binary SIMD comparison operation between two Int16x8 operands. +class LSimdBinaryCompIx8 : public LSimdBinaryComp +{ + public: + LIR_HEADER(SimdBinaryCompIx8); + LSimdBinaryCompIx8() : LSimdBinaryComp() {} +}; + +// Binary SIMD comparison operation between two Int32x4 operands. class LSimdBinaryCompIx4 : public LSimdBinaryComp { public: diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 20256eebb57f..69cecf15dc7c 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -44,6 +44,8 @@ _(SimdUnaryArithIx8) \ _(SimdUnaryArithIx4) \ _(SimdUnaryArithFx4) \ + _(SimdBinaryCompIx16) \ + _(SimdBinaryCompIx8) \ _(SimdBinaryCompIx4) \ _(SimdBinaryCompFx4) \ _(SimdBinaryArithIx16) \ diff --git a/js/src/jit/x86-shared/Architecture-x86-shared.h b/js/src/jit/x86-shared/Architecture-x86-shared.h index 8e58bd0131e6..a60d1952eafe 100644 --- a/js/src/jit/x86-shared/Architecture-x86-shared.h +++ b/js/src/jit/x86-shared/Architecture-x86-shared.h @@ -23,7 +23,9 @@ namespace jit { // Does this architecture support SIMD conversions between Uint32x4 and Float32x4? static MOZ_CONSTEXPR_VAR bool SupportsUint32x4FloatConversions = false; -// Does this architecture support comparisons of unsigned 32x4 integer vectors? +// Does this architecture support comparisons of unsigned integer vectors? +static MOZ_CONSTEXPR_VAR bool SupportsUint8x16Compares = false; +static MOZ_CONSTEXPR_VAR bool SupportsUint16x8Compares = false; static MOZ_CONSTEXPR_VAR bool SupportsUint32x4Compares = false; #if defined(JS_CODEGEN_X86) diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index a7f90429d00d..93bf1b319c45 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -2269,10 +2269,73 @@ class AssemblerX86Shared : public AssemblerShared MOZ_ASSERT(HasSSE2()); masm.vucomiss_rr(rhs.encoding(), lhs.encoding()); } - void vpcmpeqw(FloatRegister rhs, FloatRegister lhs, FloatRegister dst) { + + void vpcmpeqb(const Operand& rhs, FloatRegister lhs, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); - masm.vpcmpeqw_rr(rhs.encoding(), lhs.encoding(), dst.encoding()); + switch (rhs.kind()) { + case Operand::FPREG: + masm.vpcmpeqb_rr(rhs.fpu(), lhs.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpcmpeqb_mr(rhs.disp(), rhs.base(), lhs.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpcmpeqb_mr(rhs.address(), lhs.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } } + void vpcmpgtb(const Operand& rhs, FloatRegister lhs, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (rhs.kind()) { + case Operand::FPREG: + masm.vpcmpgtb_rr(rhs.fpu(), lhs.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpcmpgtb_mr(rhs.disp(), rhs.base(), lhs.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpcmpgtb_mr(rhs.address(), lhs.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + + void vpcmpeqw(const Operand& rhs, FloatRegister lhs, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (rhs.kind()) { + case Operand::FPREG: + masm.vpcmpeqw_rr(rhs.fpu(), lhs.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpcmpeqw_mr(rhs.disp(), rhs.base(), lhs.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpcmpeqw_mr(rhs.address(), lhs.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpcmpgtw(const Operand& rhs, FloatRegister lhs, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (rhs.kind()) { + case Operand::FPREG: + masm.vpcmpgtw_rr(rhs.fpu(), lhs.encoding(), dest.encoding()); + break; + case Operand::MEM_REG_DISP: + masm.vpcmpgtw_mr(rhs.disp(), rhs.base(), lhs.encoding(), dest.encoding()); + break; + case Operand::MEM_ADDRESS32: + masm.vpcmpgtw_mr(rhs.address(), lhs.encoding(), dest.encoding()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vpcmpeqd(const Operand& rhs, FloatRegister lhs, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); switch (rhs.kind()) { @@ -2305,6 +2368,7 @@ class AssemblerX86Shared : public AssemblerShared MOZ_CRASH("unexpected operand kind"); } } + void vcmpps(uint8_t order, Operand src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); // :TODO: (Bug 1132894) See LIRGeneratorX86Shared::lowerForFPU diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index 67c7590e9fed..7200a8e89ee8 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -2574,9 +2574,56 @@ public: // SSE operations: + void vpcmpeqb_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpeqb", VEX_PD, OP2_PCMPEQB_VdqWdq, src1, src0, dst); + } + void vpcmpeqb_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpeqb", VEX_PD, OP2_PCMPEQB_VdqWdq, offset, base, src0, dst); + } + void vpcmpeqb_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpeqb", VEX_PD, OP2_PCMPEQB_VdqWdq, address, src0, dst); + } + + void vpcmpgtb_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpgtb", VEX_PD, OP2_PCMPGTB_VdqWdq, src1, src0, dst); + } + void vpcmpgtb_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpgtb", VEX_PD, OP2_PCMPGTB_VdqWdq, offset, base, src0, dst); + } + void vpcmpgtb_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpgtb", VEX_PD, OP2_PCMPGTB_VdqWdq, address, src0, dst); + } + void vpcmpeqw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { - twoByteOpSimd("vpcmpeqw", VEX_PD, OP2_PCMPEQW, src1, src0, dst); + twoByteOpSimd("vpcmpeqw", VEX_PD, OP2_PCMPEQW_VdqWdq, src1, src0, dst); + } + void vpcmpeqw_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpeqw", VEX_PD, OP2_PCMPEQW_VdqWdq, offset, base, src0, dst); + } + void vpcmpeqw_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpeqw", VEX_PD, OP2_PCMPEQW_VdqWdq, address, src0, dst); + } + + void vpcmpgtw_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpgtw", VEX_PD, OP2_PCMPGTW_VdqWdq, src1, src0, dst); + } + void vpcmpgtw_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpgtw", VEX_PD, OP2_PCMPGTW_VdqWdq, offset, base, src0, dst); + } + void vpcmpgtw_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + { + twoByteOpSimd("vpcmpgtw", VEX_PD, OP2_PCMPGTW_VdqWdq, address, src0, dst); } void vpcmpeqd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 62918ae87efc..a42f9c576033 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -3401,6 +3401,126 @@ CodeGeneratorX86Shared::visitSimdShuffleX4(LSimdShuffleX4* ins) masm.vshufps(mask, lhs, lhs, lhs); } +void +CodeGeneratorX86Shared::visitSimdBinaryCompIx16(LSimdBinaryCompIx16* ins) +{ + static const SimdConstant allOnes = SimdConstant::SplatX16(-1); + + FloatRegister lhs = ToFloatRegister(ins->lhs()); + Operand rhs = ToOperand(ins->rhs()); + FloatRegister output = ToFloatRegister(ins->output()); + MOZ_ASSERT_IF(!Assembler::HasAVX(), output == lhs); + + ScratchSimd128Scope scratch(masm); + + MSimdBinaryComp::Operation op = ins->operation(); + switch (op) { + case MSimdBinaryComp::greaterThan: + masm.vpcmpgtb(rhs, lhs, output); + return; + case MSimdBinaryComp::equal: + masm.vpcmpeqb(rhs, lhs, output); + return; + case MSimdBinaryComp::lessThan: + // src := rhs + if (rhs.kind() == Operand::FPREG) + masm.moveSimd128Int(ToFloatRegister(ins->rhs()), scratch); + else + masm.loadAlignedSimd128Int(rhs, scratch); + + // src := src > lhs (i.e. lhs < rhs) + // Improve by doing custom lowering (rhs is tied to the output register) + masm.vpcmpgtb(ToOperand(ins->lhs()), scratch, scratch); + masm.moveSimd128Int(scratch, output); + return; + case MSimdBinaryComp::notEqual: + // Ideally for notEqual, greaterThanOrEqual, and lessThanOrEqual, we + // should invert the comparison by, e.g. swapping the arms of a select + // if that's what it's used in. + masm.loadConstantSimd128Int(allOnes, scratch); + masm.vpcmpeqb(rhs, lhs, output); + masm.bitwiseXorSimd128(Operand(scratch), output); + return; + case MSimdBinaryComp::greaterThanOrEqual: + // src := rhs + if (rhs.kind() == Operand::FPREG) + masm.moveSimd128Int(ToFloatRegister(ins->rhs()), scratch); + else + masm.loadAlignedSimd128Int(rhs, scratch); + masm.vpcmpgtb(ToOperand(ins->lhs()), scratch, scratch); + masm.loadConstantSimd128Int(allOnes, output); + masm.bitwiseXorSimd128(Operand(scratch), output); + return; + case MSimdBinaryComp::lessThanOrEqual: + // lhs <= rhs is equivalent to !(rhs < lhs), which we compute here. + masm.loadConstantSimd128Int(allOnes, scratch); + masm.vpcmpgtb(rhs, lhs, output); + masm.bitwiseXorSimd128(Operand(scratch), output); + return; + } + MOZ_CRASH("unexpected SIMD op"); +} + +void +CodeGeneratorX86Shared::visitSimdBinaryCompIx8(LSimdBinaryCompIx8* ins) +{ + static const SimdConstant allOnes = SimdConstant::SplatX8(-1); + + FloatRegister lhs = ToFloatRegister(ins->lhs()); + Operand rhs = ToOperand(ins->rhs()); + FloatRegister output = ToFloatRegister(ins->output()); + MOZ_ASSERT_IF(!Assembler::HasAVX(), output == lhs); + + ScratchSimd128Scope scratch(masm); + + MSimdBinaryComp::Operation op = ins->operation(); + switch (op) { + case MSimdBinaryComp::greaterThan: + masm.vpcmpgtw(rhs, lhs, output); + return; + case MSimdBinaryComp::equal: + masm.vpcmpeqw(rhs, lhs, output); + return; + case MSimdBinaryComp::lessThan: + // src := rhs + if (rhs.kind() == Operand::FPREG) + masm.moveSimd128Int(ToFloatRegister(ins->rhs()), scratch); + else + masm.loadAlignedSimd128Int(rhs, scratch); + + // src := src > lhs (i.e. lhs < rhs) + // Improve by doing custom lowering (rhs is tied to the output register) + masm.vpcmpgtw(ToOperand(ins->lhs()), scratch, scratch); + masm.moveSimd128Int(scratch, output); + return; + case MSimdBinaryComp::notEqual: + // Ideally for notEqual, greaterThanOrEqual, and lessThanOrEqual, we + // should invert the comparison by, e.g. swapping the arms of a select + // if that's what it's used in. + masm.loadConstantSimd128Int(allOnes, scratch); + masm.vpcmpeqw(rhs, lhs, output); + masm.bitwiseXorSimd128(Operand(scratch), output); + return; + case MSimdBinaryComp::greaterThanOrEqual: + // src := rhs + if (rhs.kind() == Operand::FPREG) + masm.moveSimd128Int(ToFloatRegister(ins->rhs()), scratch); + else + masm.loadAlignedSimd128Int(rhs, scratch); + masm.vpcmpgtw(ToOperand(ins->lhs()), scratch, scratch); + masm.loadConstantSimd128Int(allOnes, output); + masm.bitwiseXorSimd128(Operand(scratch), output); + return; + case MSimdBinaryComp::lessThanOrEqual: + // lhs <= rhs is equivalent to !(rhs < lhs), which we compute here. + masm.loadConstantSimd128Int(allOnes, scratch); + masm.vpcmpgtw(rhs, lhs, output); + masm.bitwiseXorSimd128(Operand(scratch), output); + return; + } + MOZ_CRASH("unexpected SIMD op"); +} + void CodeGeneratorX86Shared::visitSimdBinaryCompIx4(LSimdBinaryCompIx4* ins) { diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 77140a401b9c..3bc94cd927da 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -314,6 +314,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void visitSimdUnaryArithIx8(LSimdUnaryArithIx8* lir); void visitSimdUnaryArithIx4(LSimdUnaryArithIx4* lir); void visitSimdUnaryArithFx4(LSimdUnaryArithFx4* lir); + void visitSimdBinaryCompIx16(LSimdBinaryCompIx16* lir); + void visitSimdBinaryCompIx8(LSimdBinaryCompIx8* lir); void visitSimdBinaryCompIx4(LSimdBinaryCompIx4* lir); void visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir); void visitSimdBinaryArithIx16(LSimdBinaryArithIx16* lir); diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h index 633b0e906528..8c75a6a23a04 100644 --- a/js/src/jit/x86-shared/Encoding-x86-shared.h +++ b/js/src/jit/x86-shared/Encoding-x86-shared.h @@ -212,6 +212,8 @@ enum TwoByteOpcodeID { OP2_ORPD_VpdWpd = 0x56, OP2_XORPD_VpdWpd = 0x57, OP2_PUNPCKLDQ = 0x62, + OP2_PCMPGTB_VdqWdq = 0x64, + OP2_PCMPGTW_VdqWdq = 0x65, OP2_PCMPGTD_VdqWdq = 0x66, OP2_MOVD_VdEd = 0x6E, OP2_MOVDQ_VsdWsd = 0x6F, @@ -226,7 +228,8 @@ enum TwoByteOpcodeID { OP2_PSRAD_UdqIb = 0x72, OP2_PSRLD_UdqIb = 0x72, OP2_PSRLDQ_Vd = 0x73, - OP2_PCMPEQW = 0x75, + OP2_PCMPEQB_VdqWdq = 0x74, + OP2_PCMPEQW_VdqWdq = 0x75, OP2_PCMPEQD_VdqWdq = 0x76, OP2_HADDPD = 0x7C, OP2_MOVD_EdVd = 0x7E, diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index ed5d3e68c2e6..64db14a6d0a0 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -137,12 +137,7 @@ LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefini { // Without AVX, we'll need to use the x86 encodings where one of the // inputs must be the same location as the output. - // - // :TODO: (Bug 1132894) Note, we might have to allocate a different - // registers if the MIRType of the reused operand differs from the MIRType - // of returned value, as MUST_REUSE_INPUT is not yet capable of reusing the - // same register but with a different register type. - if (!Assembler::HasAVX() && mir->type() == lhs->type()) { + if (!Assembler::HasAVX()) { ins->setOperand(0, useRegisterAtStart(lhs)); ins->setOperand(1, lhs != rhs ? use(rhs) : useAtStart(rhs)); defineReuseInput(ins, mir, 0); diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h index d9043bed848b..2a8af3100c1c 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h @@ -219,7 +219,7 @@ void MacroAssembler::negateFloat(FloatRegister reg) { ScratchFloat32Scope scratch(*this); - vpcmpeqw(scratch, scratch, scratch); + vpcmpeqw(Operand(scratch), scratch, scratch); vpsllq(Imm32(31), scratch, scratch); // XOR the float in a float register with -0.0. @@ -231,7 +231,7 @@ MacroAssembler::negateDouble(FloatRegister reg) { // From MacroAssemblerX86Shared::maybeInlineDouble ScratchDoubleScope scratch(*this); - vpcmpeqw(scratch, scratch, scratch); + vpcmpeqw(Operand(scratch), scratch, scratch); vpsllq(Imm32(63), scratch, scratch); // XOR the float in a float register with -0.0. diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h index 62e1663b6584..b5e50b5c1fdd 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h @@ -1244,7 +1244,7 @@ class MacroAssemblerX86Shared : public Assembler return true; } if (v == minusOne) { - vpcmpeqw(dest, dest, dest); + vpcmpeqw(Operand(dest), dest, dest); return true; } return false; From e0fb4b123e8cd1ffe32a07dfd13cdf44179d0f31 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:20 -0700 Subject: [PATCH 057/199] Bug 1136226 - Inline saturating arithmetic operations. r=sunfish Add support for inlining addSaturate and subSaturate SIMD operations when compiling SIMD.js. --- js/src/jit-test/lib/simd.js | 4 +- js/src/jit-test/tests/SIMD/binary-arith.js | 12 +++--- .../tests/SIMD/float32x4-binary-arith.js | 10 ++--- js/src/jit-test/tests/SIMD/saturate.js | 37 +++++++++++++++++++ js/src/jit/IonBuilder.h | 2 + js/src/jit/MCallOptimize.cpp | 22 ++++++++++- 6 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 js/src/jit-test/tests/SIMD/saturate.js diff --git a/js/src/jit-test/lib/simd.js b/js/src/jit-test/lib/simd.js index 7490a1ebba12..f275c6f08107 100644 --- a/js/src/jit-test/lib/simd.js +++ b/js/src/jit-test/lib/simd.js @@ -9,11 +9,11 @@ function booleanBinaryX4(op, v, w) { return arr; } -function binaryX4(op, v, w) { +function binaryX(op, v, w) { var arr = []; var [varr, warr] = [simdToArray(v), simdToArray(w)]; [varr, warr] = [varr.map(Math.fround), warr.map(Math.fround)]; - for (var i = 0; i < 4; i++) + for (var i = 0; i < varr.length; i++) arr[i] = op(varr[i], warr[i]); return arr.map(Math.fround); } diff --git a/js/src/jit-test/tests/SIMD/binary-arith.js b/js/src/jit-test/tests/SIMD/binary-arith.js index 3fe70f1b0c9e..a85d069edd8a 100644 --- a/js/src/jit-test/tests/SIMD/binary-arith.js +++ b/js/src/jit-test/tests/SIMD/binary-arith.js @@ -10,13 +10,13 @@ function f() { var f2 = SIMD.Float32x4(4, 3, 2, 1); for (var i = 0; i < 150; i++) { - assertEqX4(SIMD.Float32x4.add(f1, f2), binaryX4((x, y) => x + y, f1, f2)); - assertEqX4(SIMD.Float32x4.sub(f1, f2), binaryX4((x, y) => x - y, f1, f2)); - assertEqX4(SIMD.Float32x4.mul(f1, f2), binaryX4((x, y) => x * y, f1, f2)); + assertEqX4(SIMD.Float32x4.add(f1, f2), binaryX((x, y) => x + y, f1, f2)); + assertEqX4(SIMD.Float32x4.sub(f1, f2), binaryX((x, y) => x - y, f1, f2)); + assertEqX4(SIMD.Float32x4.mul(f1, f2), binaryX((x, y) => x * y, f1, f2)); - assertEqX4(SIMD.Int32x4.add(i1, i2), binaryX4((x, y) => x + y, i1, i2)); - assertEqX4(SIMD.Int32x4.sub(i1, i2), binaryX4((x, y) => x - y, i1, i2)); - assertEqX4(SIMD.Int32x4.mul(i1, i2), binaryX4((x, y) => x * y, i1, i2)); + assertEqX4(SIMD.Int32x4.add(i1, i2), binaryX((x, y) => x + y, i1, i2)); + assertEqX4(SIMD.Int32x4.sub(i1, i2), binaryX((x, y) => x - y, i1, i2)); + assertEqX4(SIMD.Int32x4.mul(i1, i2), binaryX((x, y) => x * y, i1, i2)); } } diff --git a/js/src/jit-test/tests/SIMD/float32x4-binary-arith.js b/js/src/jit-test/tests/SIMD/float32x4-binary-arith.js index e2febd314b52..63e9215d9fef 100644 --- a/js/src/jit-test/tests/SIMD/float32x4-binary-arith.js +++ b/js/src/jit-test/tests/SIMD/float32x4-binary-arith.js @@ -22,11 +22,11 @@ function f() { var f1 = SIMD.Float32x4(1, 2, 3, 4); var f2 = SIMD.Float32x4(4, 3, 2, 1); for (var i = 0; i < 150; i++) { - assertEqX4(SIMD.Float32x4.div(f1, f2), binaryX4((x, y) => x / y, f1, f2)); - assertEqX4(SIMD.Float32x4.min(f1, f2), binaryX4(Math.min, f1, f2)); - assertEqX4(SIMD.Float32x4.max(f1, f2), binaryX4(Math.max, f1, f2)); - assertEqX4(SIMD.Float32x4.minNum(f1, f2), binaryX4(minNum, f1, f2)); - assertEqX4(SIMD.Float32x4.maxNum(f1, f2), binaryX4(maxNum, f1, f2)); + assertEqX4(SIMD.Float32x4.div(f1, f2), binaryX((x, y) => x / y, f1, f2)); + assertEqX4(SIMD.Float32x4.min(f1, f2), binaryX(Math.min, f1, f2)); + assertEqX4(SIMD.Float32x4.max(f1, f2), binaryX(Math.max, f1, f2)); + assertEqX4(SIMD.Float32x4.minNum(f1, f2), binaryX(minNum, f1, f2)); + assertEqX4(SIMD.Float32x4.maxNum(f1, f2), binaryX(maxNum, f1, f2)); } } diff --git a/js/src/jit-test/tests/SIMD/saturate.js b/js/src/jit-test/tests/SIMD/saturate.js new file mode 100644 index 000000000000..a98cf7ad799f --- /dev/null +++ b/js/src/jit-test/tests/SIMD/saturate.js @@ -0,0 +1,37 @@ +load(libdir + 'simd.js'); + +setJitCompilerOption("ion.warmup.trigger", 50); + +const INT8_MIN = -128; +const INT8_MAX = 127; +const UINT8_MAX = 255; + +function sat8(x) { + if (x < INT8_MIN) return INT8_MIN; + if (x > INT8_MAX) return INT8_MAX; + return x; +} + +function usat8(x) { + if (x < 0) return 0; + if (x > UINT8_MAX) return UINT8_MAX; + return x; +} + +function f() { + var i1 = SIMD.Int8x16(1, 100, 3, 4); + var i2 = SIMD.Int8x16(4, 30, 2, 1); + + var u1 = SIMD.Uint8x16(1, 2, 3, 4); + var u2 = SIMD.Uint8x16(4, 3, 2, 1); + + for (var i = 0; i < 150; i++) { + assertEqX4(SIMD.Int8x16.addSaturate(i1, i2), binaryX((x, y) => sat8(x + y), i1, i2)); + assertEqX4(SIMD.Int8x16.subSaturate(i1, i2), binaryX((x, y) => sat8(x - y), i1, i2)); + + assertEqX4(SIMD.Uint8x16.addSaturate(u1, u2), binaryX((x, y) => usat8(x + y), u1, u2)); + assertEqX4(SIMD.Uint8x16.subSaturate(u1, u2), binaryX((x, y) => usat8(x - y), u1, u2)); + } +} + +f(); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index acb6ef43a391..da09ec8047ad 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -918,6 +918,8 @@ class IonBuilder template InliningStatus inlineSimdBinary(CallInfo& callInfo, JSNative native, typename T::Operation op, SimdType type); + InliningStatus inlineSimdBinarySaturating(CallInfo& callInfo, JSNative native, + MSimdBinarySaturating::Operation op, SimdType type); InliningStatus inlineSimdShift(CallInfo& callInfo, JSNative native, MSimdShift::Operation op, SimdType type); InliningStatus inlineSimdComp(CallInfo& callInfo, JSNative native, diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 276af8825da4..821dc6368284 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -3268,9 +3268,12 @@ IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type) case SimdOperation::Fn_minNum: return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_minNum, type); + + // Binary saturating. case SimdOperation::Fn_addSaturate: + return inlineSimdBinarySaturating(callInfo, native, MSimdBinarySaturating::add, type); case SimdOperation::Fn_subSaturate: - MOZ_CRASH("NYI"); + return inlineSimdBinarySaturating(callInfo, native, MSimdBinarySaturating::sub, type); // Binary bitwise. case SimdOperation::Fn_and: @@ -3563,6 +3566,23 @@ IonBuilder::inlineSimdBinary(CallInfo& callInfo, JSNative native, typename T::Op return boxSimd(callInfo, ins, templateObj); } +// Inline a binary SIMD operation where both arguments are SIMD types. +IonBuilder::InliningStatus +IonBuilder::inlineSimdBinarySaturating(CallInfo& callInfo, JSNative native, + MSimdBinarySaturating::Operation op, SimdType type) +{ + InlineTypedObject* templateObj = nullptr; + if (!canInlineSimd(callInfo, native, 2, &templateObj)) + return InliningStatus_NotInlined; + + MDefinition* lhs = unboxSimd(callInfo.getArg(0), type); + MDefinition* rhs = unboxSimd(callInfo.getArg(1), type); + + MSimdBinarySaturating* ins = + MSimdBinarySaturating::New(alloc(), lhs, rhs, op, GetSimdSign(type)); + return boxSimd(callInfo, ins, templateObj); +} + // Inline a SIMD shiftByScalar operation. IonBuilder::InliningStatus IonBuilder::inlineSimdShift(CallInfo& callInfo, JSNative native, MSimdShift::Operation op, From 2810a22e4a1e06a0569e7ec6a40f4afa62ce3794 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:20 -0700 Subject: [PATCH 058/199] Bug 1136226 - Test loads, stores, and bitcasts. r=sunfish Add a new asm.js test later which exercises all the possible bitcast operations as well as all possible load and store operations, except the partial ones. This commit prepares for that test case. Add new Scalar::I8x16, I16x8 enumerators. Remove the fromUintXXX opcodes from the *_ASMJS macros. This avoids gerating unwanted wasm opcodes. Include the relevant unsigned bit-casts explicitly where needed. --- js/src/asmjs/AsmJS.cpp | 51 +++++++++++++++++-- js/src/asmjs/WasmBinaryIterator.cpp | 11 +++- js/src/asmjs/WasmIonCompile.cpp | 12 +++-- js/src/builtin/SIMD.h | 19 +++++-- js/src/builtin/TypedObject.cpp | 4 ++ js/src/builtin/TypedObject.h | 4 ++ js/src/builtin/TypedObjectConstants.h | 4 +- js/src/jit/IonTypes.h | 8 +++ js/src/jit/Lowering.cpp | 2 + js/src/jit/MCallOptimize.cpp | 4 ++ js/src/jit/MacroAssembler.cpp | 16 ++++++ js/src/jit/RangeAnalysis.cpp | 2 + js/src/jit/shared/CodeGenerator-shared-inl.h | 2 + js/src/jit/x64/CodeGenerator-x64.cpp | 22 ++++++++ js/src/jit/x64/Lowering-x64.cpp | 2 + .../x86-shared/CodeGenerator-x86-shared.cpp | 2 + js/src/jit/x86/CodeGenerator-x86.cpp | 20 ++++++++ js/src/jit/x86/Lowering-x86.cpp | 13 +++-- js/src/jsfriendapi.h | 12 +++++ js/src/vm/TypedArrayCommon.h | 4 ++ js/src/vm/TypedArrayObject.cpp | 4 ++ js/src/vm/TypedArrayObject.h | 2 + 22 files changed, 200 insertions(+), 20 deletions(-) diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 012ea7e80320..a5f37fcd1d98 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -2706,7 +2706,7 @@ SimdToExpr(SimdType type, SimdOperation op) switch (type) { case SimdType::Uint8x16: // Handle the special unsigned opcodes, then fall through to Int8x16. - switch(op) { + switch (op) { case SimdOperation::Fn_addSaturate: return Expr::I8x16addSaturateU; case SimdOperation::Fn_subSaturate: return Expr::I8x16subSaturateU; case SimdOperation::Fn_extractLane: return Expr::I8x16extractLaneU; @@ -2721,7 +2721,12 @@ SimdToExpr(SimdType type, SimdOperation op) MOZ_FALLTHROUGH; case SimdType::Int8x16: // Bitcasts Uint8x16 <--> Int8x16 become noops. - if (op == SimdOperation::Fn_fromUint8x16Bits) return Expr::Limit; + switch (op) { + case SimdOperation::Fn_fromUint8x16Bits: return Expr::Limit; + case SimdOperation::Fn_fromUint16x8Bits: return Expr::I8x16fromInt16x8Bits; + case SimdOperation::Fn_fromUint32x4Bits: return Expr::I8x16fromInt32x4Bits; + default: break; + } ENUMERATE(I8x16, FORALL_INT8X16_ASMJS_OP, I8x16CASE) break; @@ -2742,7 +2747,12 @@ SimdToExpr(SimdType type, SimdOperation op) MOZ_FALLTHROUGH; case SimdType::Int16x8: // Bitcasts Uint16x8 <--> Int16x8 become noops. - if (op == SimdOperation::Fn_fromUint16x8Bits) return Expr::Limit; + switch (op) { + case SimdOperation::Fn_fromUint8x16Bits: return Expr::I16x8fromInt8x16Bits; + case SimdOperation::Fn_fromUint16x8Bits: return Expr::Limit; + case SimdOperation::Fn_fromUint32x4Bits: return Expr::I16x8fromInt32x4Bits; + default: break; + } ENUMERATE(I16x8, FORALL_INT16X8_ASMJS_OP, I16x8CASE) break; @@ -2761,11 +2771,22 @@ SimdToExpr(SimdType type, SimdOperation op) MOZ_FALLTHROUGH; case SimdType::Int32x4: // Bitcasts Uint32x4 <--> Int32x4 become noops. - if (op == SimdOperation::Fn_fromUint32x4Bits) return Expr::Limit; + switch (op) { + case SimdOperation::Fn_fromUint8x16Bits: return Expr::I32x4fromInt8x16Bits; + case SimdOperation::Fn_fromUint16x8Bits: return Expr::I32x4fromInt16x8Bits; + case SimdOperation::Fn_fromUint32x4Bits: return Expr::Limit; + default: break; + } ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32x4CASE) break; case SimdType::Float32x4: + switch (op) { + case SimdOperation::Fn_fromUint8x16Bits: return Expr::F32x4fromInt8x16Bits; + case SimdOperation::Fn_fromUint16x8Bits: return Expr::F32x4fromInt16x8Bits; + case SimdOperation::Fn_fromUint32x4Bits: return Expr::F32x4fromInt32x4Bits; + default: break; + } ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32x4CASE) break; @@ -3387,6 +3408,8 @@ IsSimdValidOperationType(SimdType type, SimdOperation op) case SimdType::Int32x4: switch (op) { case SimdOperation::Constructor: + case SimdOperation::Fn_fromUint8x16Bits: + case SimdOperation::Fn_fromUint16x8Bits: case SimdOperation::Fn_fromUint32x4Bits: FORALL_INT32X4_ASMJS_OP(CASE) return true; default: return false; @@ -3395,6 +3418,8 @@ IsSimdValidOperationType(SimdType type, SimdOperation op) case SimdType::Uint32x4: switch (op) { case SimdOperation::Constructor: + case SimdOperation::Fn_fromUint8x16Bits: + case SimdOperation::Fn_fromUint16x8Bits: case SimdOperation::Fn_fromInt32x4Bits: FORALL_INT32X4_ASMJS_OP(CASE) return true; default: return false; @@ -3403,6 +3428,9 @@ IsSimdValidOperationType(SimdType type, SimdOperation op) case SimdType::Float32x4: switch (op) { case SimdOperation::Constructor: + case SimdOperation::Fn_fromUint8x16Bits: + case SimdOperation::Fn_fromUint16x8Bits: + case SimdOperation::Fn_fromUint32x4Bits: FORALL_FLOAT32X4_ASMJS_OP(CASE) return true; default: return false; } @@ -7542,19 +7570,25 @@ ValidateSimdOperation(JSContext* cx, const AsmJSGlobal& global, HandleValue glob switch (global.simdOperation()) { FORALL_INT8X16_ASMJS_OP(SET_NATIVE_INT8X16) SET_NATIVE_INT8X16(fromUint8x16Bits) + SET_NATIVE_INT8X16(fromUint16x8Bits) + SET_NATIVE_INT8X16(fromUint32x4Bits) default: MOZ_CRASH("shouldn't have been validated in the first place"); } break; case SimdType::Int16x8: switch (global.simdOperation()) { FORALL_INT16X8_ASMJS_OP(SET_NATIVE_INT16X8) + SET_NATIVE_INT16X8(fromUint8x16Bits) SET_NATIVE_INT16X8(fromUint16x8Bits) + SET_NATIVE_INT16X8(fromUint32x4Bits) default: MOZ_CRASH("shouldn't have been validated in the first place"); } break; case SimdType::Int32x4: switch (global.simdOperation()) { FORALL_INT32X4_ASMJS_OP(SET_NATIVE_INT32X4) + SET_NATIVE_INT32X4(fromUint8x16Bits) + SET_NATIVE_INT32X4(fromUint16x8Bits) SET_NATIVE_INT32X4(fromUint32x4Bits) default: MOZ_CRASH("shouldn't have been validated in the first place"); } @@ -7563,19 +7597,25 @@ ValidateSimdOperation(JSContext* cx, const AsmJSGlobal& global, HandleValue glob switch (global.simdOperation()) { FORALL_INT8X16_ASMJS_OP(SET_NATIVE_UINT8X16) SET_NATIVE_UINT8X16(fromInt8x16Bits) + SET_NATIVE_UINT8X16(fromUint16x8Bits) + SET_NATIVE_UINT8X16(fromUint32x4Bits) default: MOZ_CRASH("shouldn't have been validated in the first place"); } break; case SimdType::Uint16x8: switch (global.simdOperation()) { FORALL_INT16X8_ASMJS_OP(SET_NATIVE_UINT16X8) + SET_NATIVE_UINT16X8(fromUint8x16Bits) SET_NATIVE_UINT16X8(fromInt16x8Bits) + SET_NATIVE_UINT16X8(fromUint32x4Bits) default: MOZ_CRASH("shouldn't have been validated in the first place"); } break; case SimdType::Uint32x4: switch (global.simdOperation()) { FORALL_INT32X4_ASMJS_OP(SET_NATIVE_UINT32X4) + SET_NATIVE_UINT32X4(fromUint8x16Bits) + SET_NATIVE_UINT32X4(fromUint16x8Bits) SET_NATIVE_UINT32X4(fromInt32x4Bits) default: MOZ_CRASH("shouldn't have been validated in the first place"); } @@ -7583,6 +7623,9 @@ ValidateSimdOperation(JSContext* cx, const AsmJSGlobal& global, HandleValue glob case SimdType::Float32x4: switch (global.simdOperation()) { FORALL_FLOAT32X4_ASMJS_OP(SET_NATIVE_FLOAT32X4) + SET_NATIVE_FLOAT32X4(fromUint8x16Bits) + SET_NATIVE_FLOAT32X4(fromUint16x8Bits) + SET_NATIVE_FLOAT32X4(fromUint32x4Bits) default: MOZ_CRASH("shouldn't have been validated in the first place"); } break; diff --git a/js/src/asmjs/WasmBinaryIterator.cpp b/js/src/asmjs/WasmBinaryIterator.cpp index f700f9ffd8d0..3fbefa4a584d 100644 --- a/js/src/asmjs/WasmBinaryIterator.cpp +++ b/js/src/asmjs/WasmBinaryIterator.cpp @@ -267,8 +267,17 @@ wasm::Classify(Expr expr) case Expr::F32x4fromInt32x4: case Expr::F32x4fromUint32x4: case Expr::I32x4fromFloat32x4Bits: + case Expr::I32x4fromInt8x16Bits: + case Expr::I32x4fromInt16x8Bits: + case Expr::I16x8fromInt8x16Bits: + case Expr::I16x8fromInt32x4Bits: + case Expr::I16x8fromFloat32x4Bits: + case Expr::I8x16fromInt16x8Bits: + case Expr::I8x16fromInt32x4Bits: + case Expr::I8x16fromFloat32x4Bits: + case Expr::F32x4fromInt8x16Bits: + case Expr::F32x4fromInt16x8Bits: case Expr::F32x4fromInt32x4Bits: - case Expr::F32x4fromUint32x4Bits: return ExprKind::Conversion; case Expr::I32Load8S: case Expr::I32Load8U: diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index 4bdbb805ed3d..d666611a0cd6 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -2538,6 +2538,8 @@ static inline Scalar::Type SimdExprTypeToViewType(ValType type, unsigned* defaultNumElems) { switch (type) { + case ValType::I8x16: *defaultNumElems = 16; return Scalar::Int8x16; + case ValType::I16x8: *defaultNumElems = 8; return Scalar::Int16x8; case ValType::I32x4: *defaultNumElems = 4; return Scalar::Int32x4; case ValType::F32x4: *defaultNumElems = 4; return Scalar::Float32x4; default: break; @@ -2822,15 +2824,17 @@ EmitSimdOp(FunctionCompiler& f, ValType type, SimdOperation op, SimdSign sign) return EmitSimdConvert(f, ValType::I32x4, type, SimdSign::Signed); case SimdOperation::Fn_fromUint32x4: return EmitSimdConvert(f, ValType::I32x4, type, SimdSign::Unsigned); + case SimdOperation::Fn_fromInt8x16Bits: + case SimdOperation::Fn_fromUint8x16Bits: + return EmitSimdBitcast(f, ValType::I8x16, type); + case SimdOperation::Fn_fromUint16x8Bits: + case SimdOperation::Fn_fromInt16x8Bits: + return EmitSimdBitcast(f, ValType::I16x8, type); case SimdOperation::Fn_fromInt32x4Bits: case SimdOperation::Fn_fromUint32x4Bits: return EmitSimdBitcast(f, ValType::I32x4, type); case SimdOperation::Fn_fromFloat32x4Bits: - case SimdOperation::Fn_fromInt8x16Bits: return EmitSimdBitcast(f, ValType::F32x4, type); - case SimdOperation::Fn_fromInt16x8Bits: - case SimdOperation::Fn_fromUint8x16Bits: - case SimdOperation::Fn_fromUint16x8Bits: case SimdOperation::Fn_fromFloat64x2Bits: MOZ_CRASH("NYI"); } diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index d237a003113f..4e545bc89747 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -746,14 +746,20 @@ // this list is shared between Int8x16 and Uint8x16. #define FORALL_INT8X16_ASMJS_OP(_) \ FORALL_INT_SIMD_OP(_) \ - FOREACH_SMINT_SIMD_BINOP(_) + FOREACH_SMINT_SIMD_BINOP(_) \ + _(fromInt16x8Bits) \ + _(fromInt32x4Bits) \ + _(fromFloat32x4Bits) // All operations on Int16x8 or Uint16x8 in the asm.js world. // Note: this does not include conversions and casts to/from Uint16x8 because // this list is shared between Int16x8 and Uint16x8. #define FORALL_INT16X8_ASMJS_OP(_) \ FORALL_INT_SIMD_OP(_) \ - FOREACH_SMINT_SIMD_BINOP(_) + FOREACH_SMINT_SIMD_BINOP(_) \ + _(fromInt8x16Bits) \ + _(fromInt32x4Bits) \ + _(fromFloat32x4Bits) // All operations on Int32x4 or Uint32x4 in the asm.js world. // Note: this does not include conversions and casts to/from Uint32x4 because @@ -761,6 +767,8 @@ #define FORALL_INT32X4_ASMJS_OP(_) \ FORALL_INT_SIMD_OP(_) \ FOREACH_MEMORY_X4_SIMD_OP(_) \ + _(fromInt8x16Bits) \ + _(fromInt16x8Bits) \ _(fromFloat32x4) \ _(fromFloat32x4Bits) @@ -768,10 +776,11 @@ #define FORALL_FLOAT32X4_ASMJS_OP(_) \ FORALL_FLOAT_SIMD_OP(_) \ FOREACH_MEMORY_X4_SIMD_OP(_) \ - _(fromInt32x4) \ + _(fromInt8x16Bits) \ + _(fromInt16x8Bits) \ _(fromInt32x4Bits) \ - _(fromUint32x4) \ - _(fromUint32x4Bits) + _(fromInt32x4) \ + _(fromUint32x4) namespace js { diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 63978eb54c9e..b7990d824284 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -254,6 +254,8 @@ ScalarTypeDescr::typeName(Type type) JS_FOR_EACH_SCALAR_TYPE_REPR(NUMERIC_TYPE_TO_STRING) #undef NUMERIC_TYPE_TO_STRING case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: case Scalar::MaxTypedArrayViewType: MOZ_CRASH(); @@ -292,6 +294,8 @@ ScalarTypeDescr::call(JSContext* cx, unsigned argc, Value* vp) JS_FOR_EACH_SCALAR_TYPE_REPR(SCALARTYPE_CALL) #undef SCALARTYPE_CALL case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: case Scalar::MaxTypedArrayViewType: MOZ_CRASH(); diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 3a1b9bf46296..b415055203d5 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -250,6 +250,10 @@ class ScalarTypeDescr : public SimpleTypeDescr "TypedObjectConstants.h must be consistent with Scalar::Type"); static_assert(Scalar::Float32x4 == JS_SCALARTYPEREPR_FLOAT32X4, "TypedObjectConstants.h must be consistent with Scalar::Type"); + static_assert(Scalar::Int8x16 == JS_SCALARTYPEREPR_INT8X16, + "TypedObjectConstants.h must be consistent with Scalar::Type"); + static_assert(Scalar::Int16x8 == JS_SCALARTYPEREPR_INT16X8, + "TypedObjectConstants.h must be consistent with Scalar::Type"); static_assert(Scalar::Int32x4 == JS_SCALARTYPEREPR_INT32X4, "TypedObjectConstants.h must be consistent with Scalar::Type"); diff --git a/js/src/builtin/TypedObjectConstants.h b/js/src/builtin/TypedObjectConstants.h index e1b6a0d029c4..d1337083ed9a 100644 --- a/js/src/builtin/TypedObjectConstants.h +++ b/js/src/builtin/TypedObjectConstants.h @@ -90,7 +90,9 @@ #define JS_SCALARTYPEREPR_FLOAT64 7 #define JS_SCALARTYPEREPR_UINT8_CLAMPED 8 #define JS_SCALARTYPEREPR_FLOAT32X4 10 -#define JS_SCALARTYPEREPR_INT32X4 11 +#define JS_SCALARTYPEREPR_INT8X16 11 +#define JS_SCALARTYPEREPR_INT16X8 12 +#define JS_SCALARTYPEREPR_INT32X4 13 // These constants are for use exclusively in JS code. In C++ code, // prefer ReferenceTypeRepresentation::TYPE_ANY etc, which allows diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index 1e209c62d03e..1871450f6bad 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -705,6 +705,10 @@ ScalarTypeToMIRType(Scalar::Type type) return MIRType::Double; case Scalar::Float32x4: return MIRType::Float32x4; + case Scalar::Int8x16: + return MIRType::Int8x16; + case Scalar::Int16x8: + return MIRType::Int16x8; case Scalar::Int32x4: return MIRType::Int32x4; case Scalar::MaxTypedArrayViewType: @@ -730,6 +734,10 @@ ScalarTypeToLength(Scalar::Type type) case Scalar::Float32x4: case Scalar::Int32x4: return 4; + case Scalar::Int16x8: + return 8; + case Scalar::Int8x16: + return 16; case Scalar::MaxTypedArrayViewType: break; } diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 30a2590aeaae..ac56d5a8ef85 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3441,6 +3441,8 @@ LIRGenerator::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins) if (ins->isSimdWrite()) { MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32x4, ins->value()->type() == MIRType::Float32x4); + MOZ_ASSERT_IF(ins->writeType() == Scalar::Int8x16, ins->value()->type() == MIRType::Int8x16); + MOZ_ASSERT_IF(ins->writeType() == Scalar::Int16x8, ins->value()->type() == MIRType::Int16x8); MOZ_ASSERT_IF(ins->writeType() == Scalar::Int32x4, ins->value()->type() == MIRType::Int32x4); } else if (ins->isFloatWrite()) { MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32, ins->value()->type() == MIRType::Float32); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 821dc6368284..69b0e4b22b5e 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -3812,6 +3812,10 @@ SimdTypeToArrayElementType(SimdType type) { switch (type) { case SimdType::Float32x4: return Scalar::Float32x4; + case SimdType::Int8x16: + case SimdType::Uint8x16: return Scalar::Int8x16; + case SimdType::Int16x8: + case SimdType::Uint16x8: return Scalar::Int16x8; case SimdType::Int32x4: case SimdType::Uint32x4: return Scalar::Int32x4; default: MOZ_CRASH("unexpected simd type"); diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index e9ddc6cbd009..f4c4923191fb 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -274,6 +274,14 @@ StoreToTypedFloatArray(MacroAssembler& masm, int arrayType, const S& value, cons default: MOZ_CRASH("unexpected number of elements in simd write"); } break; + case Scalar::Int8x16: + MOZ_ASSERT(numElems == 16, "unexpected partial store"); + masm.storeUnalignedSimd128Int(value, dest); + break; + case Scalar::Int16x8: + MOZ_ASSERT(numElems == 8, "unexpected partial store"); + masm.storeUnalignedSimd128Int(value, dest); + break; default: MOZ_CRASH("Invalid typed array type"); } @@ -370,6 +378,14 @@ MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegi default: MOZ_CRASH("unexpected number of elements in SIMD load"); } break; + case Scalar::Int8x16: + MOZ_ASSERT(numElems == 16, "unexpected partial load"); + loadUnalignedSimd128Int(src, dest.fpu()); + break; + case Scalar::Int16x8: + MOZ_ASSERT(numElems == 8, "unexpected partial load"); + loadUnalignedSimd128Int(src, dest.fpu()); + break; default: MOZ_CRASH("Invalid typed array type"); } diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 5a8c084f16ae..5833369ac972 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -1756,6 +1756,8 @@ GetTypedArrayRange(TempAllocator& alloc, Scalar::Type type) case Scalar::Float32: case Scalar::Float64: case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: case Scalar::MaxTypedArrayViewType: break; diff --git a/js/src/jit/shared/CodeGenerator-shared-inl.h b/js/src/jit/shared/CodeGenerator-shared-inl.h index 04d7d0b6f4d1..c238aae9e997 100644 --- a/js/src/jit/shared/CodeGenerator-shared-inl.h +++ b/js/src/jit/shared/CodeGenerator-shared-inl.h @@ -338,6 +338,8 @@ CodeGeneratorShared::verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, b case Scalar::Float32: case Scalar::Float64: case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: op = OtherOperand(ToFloatRegister(alloc).encoding()); break; diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index fe9cebf2e0df..af9b91c179d5 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -598,6 +598,14 @@ CodeGeneratorX64::loadSimd(Scalar::Type type, unsigned numElems, const Operand& } break; } + case Scalar::Int8x16: + MOZ_ASSERT(numElems = 16, "unexpected partial load"); + masm.loadUnalignedSimd128Int(srcAddr, out); + break; + case Scalar::Int16x8: + MOZ_ASSERT(numElems = 8, "unexpected partial load"); + masm.loadUnalignedSimd128Int(srcAddr, out); + break; case Scalar::Int8: case Scalar::Uint8: case Scalar::Int16: @@ -698,6 +706,8 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) case Scalar::Float32: masm.loadFloat32(srcAddr, ToFloatRegister(out)); break; case Scalar::Float64: masm.loadDouble(srcAddr, ToFloatRegister(out)); break; case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: MOZ_CRASH("SIMD loads should be handled in emitSimdLoad"); case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: @@ -744,6 +754,14 @@ CodeGeneratorX64::storeSimd(Scalar::Type type, unsigned numElems, FloatRegister } break; } + case Scalar::Int8x16: + MOZ_ASSERT(numElems == 16, "unexpected partial store"); + masm.storeUnalignedSimd128Int(in, dstAddr); + break; + case Scalar::Int16x8: + MOZ_ASSERT(numElems == 8, "unexpected partial store"); + masm.storeUnalignedSimd128Int(in, dstAddr); + break; case Scalar::Int8: case Scalar::Uint8: case Scalar::Int16: @@ -845,6 +863,8 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) case Scalar::Float32: case Scalar::Float64: case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: @@ -871,6 +891,8 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) masm.storeUncanonicalizedDouble(ToFloatRegister(value), dstAddr); break; case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: MOZ_CRASH("SIMD stores must be handled in emitSimdStore"); case Scalar::Uint8Clamped: diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp index 6006fc39e80e..0606d02306c4 100644 --- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -195,6 +195,8 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins) case Scalar::Float32: case Scalar::Float64: case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: lir = new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value())); break; diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index a42f9c576033..7032ea69e35f 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -375,6 +375,8 @@ CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTyp { switch (ool->viewType()) { case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 3d1eb0c40fbd..68ac57f25141 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -270,6 +270,8 @@ CodeGeneratorX86::load(Scalar::Type accessType, const Operand& srcAddr, const LD case Scalar::Float32: masm.vmovssWithPatch(srcAddr, ToFloatRegister(out)); break; case Scalar::Float64: masm.vmovsdWithPatch(srcAddr, ToFloatRegister(out)); break; case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: MOZ_CRASH("SIMD load should be handled in their own function"); case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected type"); } @@ -370,6 +372,14 @@ CodeGeneratorX86::loadSimd(Scalar::Type type, unsigned numElems, const Operand& } break; } + case Scalar::Int8x16: + MOZ_ASSERT(numElems == 16, "unexpected partial load"); + masm.vmovdquWithPatch(srcAddr, out); + break; + case Scalar::Int16x8: + MOZ_ASSERT(numElems == 8, "unexpected partial load"); + masm.vmovdquWithPatch(srcAddr, out); + break; case Scalar::Int8: case Scalar::Uint8: case Scalar::Int16: @@ -496,6 +506,8 @@ CodeGeneratorX86::store(Scalar::Type accessType, const LAllocation* value, const break; case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: MOZ_CRASH("SIMD stores should be handled in emitSimdStore"); @@ -558,6 +570,14 @@ CodeGeneratorX86::storeSimd(Scalar::Type type, unsigned numElems, FloatRegister } break; } + case Scalar::Int8x16: + MOZ_ASSERT(numElems == 16, "unexpected partial store"); + masm.vmovdquWithPatch(in, dstAddr); + break; + case Scalar::Int16x8: + MOZ_ASSERT(numElems == 8, "unexpected partial store"); + masm.vmovdquWithPatch(in, dstAddr); + break; case Scalar::Int8: case Scalar::Uint8: case Scalar::Int16: diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index e64dd2a70c3e..d75eb360378d 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -239,11 +239,14 @@ LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins) case Scalar::Int16: case Scalar::Uint16: case Scalar::Int32: case Scalar::Uint32: case Scalar::Float32: case Scalar::Float64: - case Scalar::Float32x4: case Scalar::Int32x4: - // For now, don't allow constant values. The immediate operand - // affects instruction layout which affects patching. - lir = new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value())); - break; + case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: + case Scalar::Int32x4: + // For now, don't allow constant values. The immediate operand + // affects instruction layout which affects patching. + lir = new (alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value())); + break; case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 0c90b9d8f943..6828963a81da 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1522,6 +1522,8 @@ enum Type { MaxTypedArrayViewType, Float32x4, + Int8x16, + Int16x8, Int32x4 }; @@ -1542,6 +1544,8 @@ byteSize(Type atype) return 4; case Float64: return 8; + case Int8x16: + case Int16x8: case Int32x4: case Float32x4: return 16; @@ -1556,6 +1560,8 @@ isSignedIntType(Type atype) { case Int8: case Int16: case Int32: + case Int8x16: + case Int16x8: case Int32x4: return true; case Uint8: @@ -1584,6 +1590,8 @@ isSimdType(Type atype) { case Float32: case Float64: return false; + case Int8x16: + case Int16x8: case Int32x4: case Float32x4: return true; @@ -1596,6 +1604,10 @@ isSimdType(Type atype) { static inline size_t scalarByteSize(Type atype) { switch (atype) { + case Int8x16: + return 1; + case Int16x8: + return 2; case Int32x4: case Float32x4: return 4; diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index d093c76d4027..73c313f2ce5e 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -886,6 +886,8 @@ class TypedArrayMethods return ElementSpecific::setFromTypedArray(cx, target, source, offset); return ElementSpecific::setFromTypedArray(cx, target, source, offset); case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: case Scalar::MaxTypedArrayViewType: break; @@ -940,6 +942,8 @@ class TypedArrayMethods return ElementSpecific::setFromNonTypedArray(cx, target, source, len, offset); return ElementSpecific::setFromNonTypedArray(cx, target, source, len, offset); case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: case Scalar::MaxTypedArrayViewType: break; diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index de03e8248b45..1f13024377c7 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -2032,6 +2032,8 @@ TypedArrayObject::getElement(uint32_t index) case Scalar::Uint8Clamped: return Uint8ClampedArray::getIndexValue(this, index); case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: case Scalar::MaxTypedArrayViewType: break; @@ -2074,6 +2076,8 @@ TypedArrayObject::setElement(TypedArrayObject& obj, uint32_t index, double d) Float64Array::setIndexValue(obj, index, d); return; case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: case Scalar::MaxTypedArrayViewType: break; diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index 97ac7b8b59fc..9e5b5c2d2fe6 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -353,6 +353,8 @@ TypedArrayShift(Scalar::Type viewType) case Scalar::Float64: return 3; case Scalar::Float32x4: + case Scalar::Int8x16: + case Scalar::Int16x8: case Scalar::Int32x4: return 4; default:; From dfd06d750520fbf7bf8a211adf7c614978b25eea Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:20 -0700 Subject: [PATCH 059/199] Bug 1136226 - Asm.js: Enable small integer types. r=bbouvier --- js/src/asmjs/AsmJS.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index a5f37fcd1d98..d8d9e04583d6 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -3405,6 +3405,26 @@ IsSimdValidOperationType(SimdType type, SimdOperation op) { #define CASE(op) case SimdOperation::Fn_##op: switch(type) { + case SimdType::Int8x16: + switch (op) { + case SimdOperation::Constructor: + case SimdOperation::Fn_fromUint8x16Bits: + case SimdOperation::Fn_fromUint16x8Bits: + case SimdOperation::Fn_fromUint32x4Bits: + FORALL_INT8X16_ASMJS_OP(CASE) return true; + default: return false; + } + break; + case SimdType::Int16x8: + switch (op) { + case SimdOperation::Constructor: + case SimdOperation::Fn_fromUint8x16Bits: + case SimdOperation::Fn_fromUint16x8Bits: + case SimdOperation::Fn_fromUint32x4Bits: + FORALL_INT16X8_ASMJS_OP(CASE) return true; + default: return false; + } + break; case SimdType::Int32x4: switch (op) { case SimdOperation::Constructor: @@ -3415,6 +3435,26 @@ IsSimdValidOperationType(SimdType type, SimdOperation op) default: return false; } break; + case SimdType::Uint8x16: + switch (op) { + case SimdOperation::Constructor: + case SimdOperation::Fn_fromInt8x16Bits: + case SimdOperation::Fn_fromUint16x8Bits: + case SimdOperation::Fn_fromUint32x4Bits: + FORALL_INT8X16_ASMJS_OP(CASE) return true; + default: return false; + } + break; + case SimdType::Uint16x8: + switch (op) { + case SimdOperation::Constructor: + case SimdOperation::Fn_fromUint8x16Bits: + case SimdOperation::Fn_fromInt16x8Bits: + case SimdOperation::Fn_fromUint32x4Bits: + FORALL_INT16X8_ASMJS_OP(CASE) return true; + default: return false; + } + break; case SimdType::Uint32x4: switch (op) { case SimdOperation::Constructor: @@ -3435,6 +3475,8 @@ IsSimdValidOperationType(SimdType type, SimdOperation op) default: return false; } break; + case SimdType::Bool8x16: + case SimdType::Bool16x8: case SimdType::Bool32x4: switch (op) { case SimdOperation::Constructor: From 693ad15d32421c0aff42de7b140a88fefc76824a Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:20 -0700 Subject: [PATCH 060/199] Bug 1136226 - Asm.js: Test cases for small integer SIMD types. r=bbouvier Tests for all of the new operations added by the previous commits. --- js/src/jit-test/tests/asm.js/testSIMD-16x8.js | 510 +++++++++++++++++ js/src/jit-test/tests/asm.js/testSIMD-8x16.js | 524 ++++++++++++++++++ .../tests/asm.js/testSIMD-bitcasts.js | 84 +++ 3 files changed, 1118 insertions(+) create mode 100644 js/src/jit-test/tests/asm.js/testSIMD-16x8.js create mode 100644 js/src/jit-test/tests/asm.js/testSIMD-8x16.js create mode 100644 js/src/jit-test/tests/asm.js/testSIMD-bitcasts.js diff --git a/js/src/jit-test/tests/asm.js/testSIMD-16x8.js b/js/src/jit-test/tests/asm.js/testSIMD-16x8.js new file mode 100644 index 000000000000..6f017892cef5 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testSIMD-16x8.js @@ -0,0 +1,510 @@ +load(libdir + "asm.js"); +load(libdir + "simd.js"); +load(libdir + "asserts.js"); + +// Set to true to see more JS debugging spew. +const DEBUG = false; + +if (!isSimdAvailable()) { + DEBUG && print("won't run tests as simd extensions aren't activated yet"); + quit(0); +} + +// Tests for 16x8 SIMD types: Int16x8, Uint16x8, Bool16x8. + +const I16x8 = 'var i16x8 = glob.SIMD.Int16x8;' +const I16x8CHK = 'var i16x8chk = i16x8.check;' +const I16x8EXT = 'var i16x8ext = i16x8.extractLane;' +const I16x8REP = 'var i16x8rep = i16x8.replaceLane;' +const I16x8U16x8 = 'var i16x8u16x8 = i16x8.fromUint16x8Bits;' + +const U16x8 = 'var u16x8 = glob.SIMD.Uint16x8;' +const U16x8CHK = 'var u16x8chk = u16x8.check;' +const U16x8EXT = 'var u16x8ext = u16x8.extractLane;' +const U16x8REP = 'var u16x8rep = u16x8.replaceLane;' +const U16x8I16x8 = 'var u16x8i16x8 = u16x8.fromInt16x8Bits;' + +const B16x8 = 'var b16x8 = glob.SIMD.Bool16x8;' +const B16x8CHK = 'var b16x8chk = b16x8.check;' +const B16x8EXT = 'var b16x8ext = b16x8.extractLane;' +const B16x8REP = 'var b16x8rep = b16x8.replaceLane;' + +const INT16_MAX = 0x7fff +const INT16_MIN = -0x10000 +const UINT16_MAX = 0xffff + +// Linking +assertEq(asmLink(asmCompile('glob', USE_ASM + I16x8 + "function f() {} return f"), {SIMD:{Int16x8: SIMD.Int16x8}})(), undefined); +assertEq(asmLink(asmCompile('glob', USE_ASM + U16x8 + "function f() {} return f"), {SIMD:{Uint16x8: SIMD.Uint16x8}})(), undefined); +assertEq(asmLink(asmCompile('glob', USE_ASM + B16x8 + "function f() {} return f"), {SIMD:{Bool16x8: SIMD.Bool16x8}})(), undefined); + +// Local variable of Int16x8 type. +assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Int16x8(1,2,3,4,5,6,7,8);} return f"); +assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8;} return f"); +assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8();} return f"); +assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1);} return f"); +assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4);} return f"); +assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4,5,6,7,8.0);} return f"); +assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4,5,6,7,8,9);} return f"); +assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4,5,6,7,8|0);} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4,5,6,7,8);} return f"), this)(), undefined); +assertEq(asmLink(asmCompile('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4,5,6,7," + (INT16_MAX + 1) + ");} return f"), this)(), undefined); + +// Local variable of Uint16x8 type. +assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Uint16x8(1,2,3,4,5,6,7,8);} return f"); +assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8;} return f"); +assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8();} return f"); +assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1);} return f"); +assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4);} return f"); +assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4,5,6,7,8.0);} return f"); +assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4,5,6,7,8,9);} return f"); +assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4,5,6,7,8|0);} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4,5,6,7,8);} return f"), this)(), undefined); +assertEq(asmLink(asmCompile('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4,5,6,7," + (UINT16_MAX + 1) + ");} return f"), this)(), undefined); + +// Local variable of Bool16x8 type. +assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Bool16x8(1,0,0,0, 0,0,0,0);} return f"); +assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8;} return f"); +assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8();} return f"); +assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1);} return f"); +assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1,0,0,0);} return f"); +assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1,0,0,0, 0,0,0,1.0);} return f"); +assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1,0,0,0, 0,0,0,0|0);} return f"); +assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1,0,0,0, 0,0,0,0, 1);} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1,0,0,0, 0,-1,-2,0);} return f"), this)(), undefined); + +// Only signed Int16x8 allowed as return value. +assertEqVecArr(asmLink(asmCompile('glob', USE_ASM + I16x8 + "function f() {return i16x8(1,2,3,4,5,6,7,8);} return f"), this)(), + [1, 2, 3, 4, 5, 6, 7, 8]); +assertEqVecArr(asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + "function f() {return i16x8chk(i16x8(1,2,3,32771,5,6,7,8));} return f"), this)(), + [1, 2, 3, -32765, 5, 6, 7, 8]); +assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {return u16x8(1,2,3,4,5,6,7,8);} return f"); +assertAsmTypeFail('glob', USE_ASM + U16x8 + U16x8CHK + "function f() {return u16x8chk(u16x8(1,2,3,4,5,6,7,8));} return f"); + +// Test splat. +function splat(x) { + let r = [] + for (let i = 0; i < 8; i++) + r.push(x); + return r +} + +splatB = asmLink(asmCompile('glob', USE_ASM + B16x8 + + 'var splat = b16x8.splat;' + + 'function f(x) { x = x|0; return splat(x); } return f'), this); +assertEqVecArr(splatB(true), splat(true)); +assertEqVecArr(splatB(false), splat(false)); + + +splatB0 = asmLink(asmCompile('glob', USE_ASM + B16x8 + + 'var splat = b16x8.splat;' + + 'function f() { var x = 0; return splat(x); } return f'), this); +assertEqVecArr(splatB0(), splat(false)); +splatB1 = asmLink(asmCompile('glob', USE_ASM + B16x8 + + 'var splat = b16x8.splat;' + + 'function f() { var x = 1; return splat(x); } return f'), this); +assertEqVecArr(splatB1(), splat(true)); + +splatI = asmLink(asmCompile('glob', USE_ASM + I16x8 + + 'var splat = i16x8.splat;' + + 'function f(x) { x = x|0; return splat(x); } return f'), this); +for (let x of [0, 1, -1, 0x12345, 0x1234, -1000, -1000000]) { + assertEqVecArr(splatI(x), splat(x << 16 >> 16)); +} + +splatIc = asmLink(asmCompile('glob', USE_ASM + I16x8 + + 'var splat = i16x8.splat;' + + 'function f() { var x = 100; return splat(x); } return f'), this); +assertEqVecArr(splatIc(), splat(100)) + +splatU = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8U16x8 + + 'var splat = u16x8.splat;' + + 'function f(x) { x = x|0; return i16x8u16x8(splat(x)); } return f'), this); +for (let x of [0, 1, -1, 0x12345, 0x1234, -1000, -1000000]) { + assertEqVecArr(SIMD.Uint16x8.fromInt16x8Bits(splatI(x)), splat(x << 16 >>> 16)); +} + +splatUc = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8U16x8 + + 'var splat = u16x8.splat;' + + 'function f() { var x = 200; return i16x8u16x8(splat(x)); } return f'), this); +assertEqVecArr(SIMD.Uint16x8.fromInt16x8Bits(splatUc()), splat(200)) + + +// Test extractLane. +// +// The lane index must be a literal int, and we generate different code for +// different lanes. +function extractI(a, i) { + return asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8EXT + + `function f() {var x=i16x8(${a.join(',')}); return i16x8ext(x, ${i})|0; } return f`), this)(); +} +a = [-1,2,-3,4,-5,6,-7,-8]; +for (var i = 0; i < 8; i++) + assertEq(extractI(a, i), a[i]); +a = a.map(x => -x); +for (var i = 0; i < 8; i++) + assertEq(extractI(a, i), a[i]); + +function extractU(a, i) { + return asmLink(asmCompile('glob', USE_ASM + U16x8 + U16x8EXT + + `function f() {var x=u16x8(${a.join(',')}); return u16x8ext(x, ${i})|0; } return f`), this)(); +} +a = [1,255,12,13,14,150,200,3]; +for (var i = 0; i < 8; i++) + assertEq(extractU(a, i), a[i]); +a = a.map(x => UINT16_MAX-x); +for (var i = 0; i < 8; i++) + assertEq(extractU(a, i), a[i]); + +function extractB(a, i) { + return asmLink(asmCompile('glob', USE_ASM + B16x8 + B16x8EXT + + `function f() {var x=b16x8(${a.join(',')}); return b16x8ext(x, ${i})|0; } return f`), this)(); +} +a = [1,1,0,1, 1,0,0,0]; +for (var i = 0; i < 8; i++) + assertEq(extractB(a, i), a[i]); +a = a.map(x => 1-x); +for (var i = 0; i < 8; i++) + assertEq(extractB(a, i), a[i]); + +// Test replaceLane. +function replaceI(a, i) { + return asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8REP + + `function f(v) {v=v|0; var x=i16x8(${a.join(',')}); return i16x8rep(x,${i},v); } return f`), this); +} +a = [-1,2,-3,4,-5,6,-7,-9]; +for (var i = 0; i < 8; i++) { + var f = replaceI(a, i); + var b = a.slice(0); + b[i] = -20; + assertEqVecArr(f(-20), b); +} + +function replaceU(a, i) { + return asmLink(asmCompile('glob', USE_ASM + U16x8 + U16x8REP + I16x8 + I16x8U16x8 + + `function f(v) {v=v|0; var x=u16x8(${a.join(',')}); return i16x8u16x8(u16x8rep(x,${i},v)); } return f`), this); +} +a = [65000-1,2,65000-3,4,65000-5,6,65000-7,65000-9]; +for (var i = 0; i < 8; i++) { + var rawf = replaceU(a, i); + var f = x => SIMD.Uint16x8.fromInt16x8Bits(rawf(x)) + var b = a.slice(0); + b[i] = 1000; + assertEqVecArr(f(1000), b); +} + +function replaceB(a, i) { + return asmLink(asmCompile('glob', USE_ASM + B16x8 + B16x8REP + + `function f(v) {v=v|0; var x=b16x8(${a.join(',')}); return b16x8rep(x,${i},v); } return f`), this); +} +a = [1,1,0,1,1,0,0,0]; +for (var i = 0; i < 8; i++) { + var f = replaceB(a, i); + var b = a.slice(0); + let v = 1 - a[i]; + b[i] = v; + assertEqVecArr(f(v), b.map(x => !!x)); +} + + +// Test select. +selectI = asmLink(asmCompile('glob', USE_ASM + I16x8 + B16x8 + B16x8CHK + + 'var select = i16x8.select;' + + 'var a = i16x8(-1,2,-3,4,-5, 6,-7, 8);' + + 'var b = i16x8( 5,6, 7,8, 9,10,11,12);' + + 'function f(x) { x = b16x8chk(x); return select(x, a, b); } return f'), this); +assertEqVecArr(selectI(SIMD.Bool16x8( 0,0, 1,0, 1,1, 1, 0)), + [ 5,6,-3,8,-5,6,-7,12]); + +selectU = asmLink(asmCompile('glob', USE_ASM + I16x8 + B16x8 + B16x8CHK + U16x8 + I16x8U16x8 + U16x8I16x8 + + 'var select = u16x8.select;' + + 'var a = i16x8(-1,2,-3,4,-5, 6,-7, 8);' + + 'var b = i16x8( 5,6, 7,8, 9,10,11,12);' + + 'function f(x) { x = b16x8chk(x); return i16x8u16x8(select(x, u16x8i16x8(a), u16x8i16x8(b))); } return f'), this); +assertEqVecArr(selectU(SIMD.Bool16x8( 0,0, 1,0, 1,1, 1, 0)), + [ 5,6,-3,8,-5,6,-7,12]); + +// Test swizzle. +function swizzle(vec, lanes) { + let r = []; + for (let i = 0; i < 8; i++) + r.push(vec[lanes[i]]); + return r; +} + +function swizzleI(lanes) { + let asm = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + + 'var swz = i16x8.swizzle;' + + `function f(a) { a = i16x8chk(a); return swz(a, ${lanes.join()}); } return f`), this); + let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16); + let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16); + let v1 = SIMD.Int16x8(...a1); + let v2 = SIMD.Int16x8(...a2); + assertEqVecArr(asm(v1), swizzle(a1, lanes)); + assertEqVecArr(asm(v2), swizzle(a2, lanes)); +} + +swizzleI([3, 4, 7, 1, 4, 3, 1, 2]); +swizzleI([0, 0, 0, 0, 0, 0, 0, 0]); +swizzleI([7, 7, 7, 7, 7, 7, 7, 7]); + +function swizzleU(lanes) { + let asm = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + U16x8 + U16x8I16x8 + I16x8U16x8 + + 'var swz = u16x8.swizzle;' + + `function f(a) { a = i16x8chk(a); return i16x8u16x8(swz(u16x8i16x8(a), ${lanes.join()})); } return f`), this); + let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16); + let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16); + let v1 = SIMD.Int16x8(...a1); + let v2 = SIMD.Int16x8(...a2); + assertEqVecArr(asm(v1), swizzle(a1, lanes)); + assertEqVecArr(asm(v2), swizzle(a2, lanes)); +} + +swizzleU([3, 4, 7, 1, 4, 3, 1, 2]); +swizzleU([0, 0, 0, 0, 0, 0, 0, 0]); +swizzleU([7, 7, 7, 7, 7, 7, 7, 7]); + +// Out-of-range lane indexes. +assertAsmTypeFail('glob', USE_ASM + I16x8 + 'var swz = i16x8.swizzle; ' + + 'function f() { var x=i16x8(0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8); } return f'); +assertAsmTypeFail('glob', USE_ASM + U16x8 + 'var swz = u16x8.swizzle; ' + + 'function f() { var x=u16x8(0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8); } return f'); +// Missing lane indexes. +assertAsmTypeFail('glob', USE_ASM + I16x8 + 'var swz = i16x8.swizzle; ' + + 'function f() { var x=i16x8(0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7); } return f'); +assertAsmTypeFail('glob', USE_ASM + U16x8 + 'var swz = u16x8.swizzle; ' + + 'function f() { var x=u16x8(0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7); } return f'); + + +// Test shuffle. +function shuffle(vec1, vec2, lanes) { + let r = []; + let vec = vec1.concat(vec2) + for (let i = 0; i < 8; i++) + r.push(vec[lanes[i]]); + return r; +} + +function shuffleI(lanes) { + let asm = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + + 'var shuf = i16x8.shuffle;' + + `function f(a1, a2) { a1 = i16x8chk(a1); a2 = i16x8chk(a2); return shuf(a1, a2, ${lanes.join()}); } return f`), this); + let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16); + let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16); + let v1 = SIMD.Int16x8(...a1); + let v2 = SIMD.Int16x8(...a2); + assertEqVecArr(asm(v1, v2), shuffle(a1, a2, lanes)); +} + +function shuffleU(lanes) { + let asm = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + U16x8 + U16x8I16x8 + I16x8U16x8 + + 'var shuf = u16x8.shuffle;' + + 'function f(a1, a2) { a1 = i16x8chk(a1); a2 = i16x8chk(a2); ' + + `return i16x8u16x8(shuf(u16x8i16x8(a1), u16x8i16x8(a2), ${lanes.join()})); } return f`), this); + let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16); + let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16); + let v1 = SIMD.Int16x8(...a1); + let v2 = SIMD.Int16x8(...a2); + assertEqVecArr(asm(v1, v2), shuffle(a1, a2, lanes)); +} + +shuffleI([0, 0, 0, 0, 0, 0, 0, 0]) +shuffleI([15, 15, 15, 15, 15, 15, 15, 15]) +shuffleI([6, 2, 0, 14, 6, 10, 11, 1]) + +shuffleU([7, 7, 7, 7, 7, 7, 7, 7]) +shuffleU([8, 15, 15, 15, 15, 15, 15, 15]) +shuffleU([6, 2, 0, 14, 6, 10, 11, 1]) + +// Test unary operators. +function unaryI(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + + `var fut = i16x8.${opname};` + + 'function f(v) { v = i16x8chk(v); return fut(v); } return f'), this); + let a = [65000-1,2,65000-3,4,65000-5,6,65000-7,65000-9]; + let v = SIMD.Int16x8(...a); + assertEqVecArr(simdfunc(v), a.map(lanefunc)); +} + +function unaryU(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8CHK + U16x8I16x8 + I16x8U16x8 + + `var fut = u16x8.${opname};` + + 'function f(v) { v = i16x8chk(v); return i16x8u16x8(fut(u16x8i16x8(v))); } return f'), this); + let a = [65000-1,2,65000-3,4,65000-5,6,65000-7,65000-9]; + let v = SIMD.Int16x8(...a); + assertEqVecArr(SIMD.Uint16x8.fromInt16x8Bits(simdfunc(v)), a.map(lanefunc)); +} + +function unaryB(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + B16x8 + B16x8CHK + + `var fut = b16x8.${opname};` + + 'function f(v) { v = b16x8chk(v); return fut(v); } return f'), this); + let a = [1,1,0,1,1,0,0,0]; + let v = SIMD.Bool16x8(...a); + assertEqVecArr(simdfunc(v), a.map(lanefunc)); +} + +unaryI('not', x => ~x << 16 >> 16); +unaryU('not', x => ~x << 16 >>> 16); +unaryB('not', x => !x); +unaryI('neg', x => -x << 16 >> 16); +unaryU('neg', x => -x << 16 >>> 16); + + +// Test binary operators. +function zipmap(a1, a2, f) { + assertEq(a1.length, a2.length); + let r = []; + for (var i = 0; i < a1.length; i++) + r.push(f(a1[i], a2[i])); + return r +} + +function binaryI(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + + `var fut = i16x8.${opname};` + + 'function f(v1, v2) { v1 = i16x8chk(v1); v2 = i16x8chk(v2); return fut(v1, v2); } return f'), this); + let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16); + let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16); + let ref = zipmap(a1, a2, lanefunc); + let v1 = SIMD.Int16x8(...a1); + let v2 = SIMD.Int16x8(...a2); + assertEqVecArr(simdfunc(v1, v2), ref); +} + +function binaryU(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8CHK + U16x8I16x8 + I16x8U16x8 + + `var fut = u16x8.${opname};` + + 'function f(v1, v2) { v1 = i16x8chk(v1); v2 = i16x8chk(v2); return i16x8u16x8(fut(u16x8i16x8(v1), u16x8i16x8(v2))); } return f'), this); + let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >>> 16); + let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >>> 16); + let ref = zipmap(a1, a2, lanefunc); + let v1 = SIMD.Int16x8(...a1); + let v2 = SIMD.Int16x8(...a2); + let res = SIMD.Uint16x8.fromInt16x8Bits(simdfunc(v1, v2)); + assertEqVecArr(res, ref); +} + +function binaryB(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + B16x8 + B16x8CHK + + `var fut = b16x8.${opname};` + + 'function f(v1, v2) { v1 = b16x8chk(v1); v2 = b16x8chk(v2); return fut(v1, v2); } return f'), this); + let a = [1,1,0,1,1,0,0,0]; + let v = SIMD.Bool16x8(...a); + assertEqVecArr(simdfunc(v), a.map(lanefunc)); +} + +binaryI('add', (x, y) => (x + y) << 16 >> 16); +binaryI('sub', (x, y) => (x - y) << 16 >> 16); +binaryI('mul', (x, y) => (x * y) << 16 >> 16); +binaryU('add', (x, y) => (x + y) << 16 >>> 16); +binaryU('sub', (x, y) => (x - y) << 16 >>> 16); +binaryU('mul', (x, y) => (x * y) << 16 >>> 16); + +binaryI('and', (x, y) => (x & y) << 16 >> 16); +binaryI('or', (x, y) => (x | y) << 16 >> 16); +binaryI('xor', (x, y) => (x ^ y) << 16 >> 16); +binaryU('and', (x, y) => (x & y) << 16 >>> 16); +binaryU('or', (x, y) => (x | y) << 16 >>> 16); +binaryU('xor', (x, y) => (x ^ y) << 16 >>> 16); + +function sat(x, lo, hi) { + if (x < lo) return lo; + if (x > hi) return hi; + return x +} +function isat(x) { return sat(x, -32768, 32767); } +function usat(x) { return sat(x, 0, 0xffff); } + +binaryI('addSaturate', (x, y) => isat(x + y)) +binaryI('subSaturate', (x, y) => isat(x - y)) +binaryU('addSaturate', (x, y) => usat(x + y)) +binaryU('subSaturate', (x, y) => usat(x - y)) + + +// Test shift operators. +function zip1map(a, s, f) { + return a.map(x => f(x, s)); +} + +function shiftI(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + + `var fut = i16x8.${opname};` + + 'function f(v, s) { v = i16x8chk(v); s = s|0; return fut(v, s); } return f'), this); + let a = [-1,2,-3,0x80,0x7f,6,0x8000,0x7fff]; + let v = SIMD.Int16x8(...a); + for (let s of [0, 1, 2, 6, 7, 8, 9, 10, 16, 255, -1, -8, -7, -1000]) { + let ref = zip1map(a, s, lanefunc); + // 1. Test dynamic shift amount. + assertEqVecArr(simdfunc(v, s), ref); + + // 2. Test constant shift amount. + let cstf = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + + `var fut = i16x8.${opname};` + + `function f(v) { v = i16x8chk(v); return fut(v, ${s}); } return f`), this); + assertEqVecArr(cstf(v, s), ref); + } +} + +function shiftU(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8CHK + U16x8I16x8 + I16x8U16x8 + + `var fut = u16x8.${opname};` + + 'function f(v, s) { v = i16x8chk(v); s = s|0; return i16x8u16x8(fut(u16x8i16x8(v), s)); } return f'), this); + let a = [-1,2,-3,0x80,0x7f,6,0x8000,0x7fff]; + let v = SIMD.Int16x8(...a); + for (let s of [0, 1, 2, 6, 7, 8, 9, 10, 16, 255, -1, -8, -7, -1000]) { + let ref = zip1map(a, s, lanefunc); + // 1. Test dynamic shift amount. + assertEqVecArr(SIMD.Uint16x8.fromInt16x8Bits(simdfunc(v, s)), ref); + + // 2. Test constant shift amount. + let cstf = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8CHK + U16x8I16x8 + I16x8U16x8 + + `var fut = u16x8.${opname};` + + `function f(v) { v = i16x8chk(v); return i16x8u16x8(fut(u16x8i16x8(v), ${s})); } return f`), this); + assertEqVecArr(SIMD.Uint16x8.fromInt16x8Bits(cstf(v, s)), ref); + } +} + +shiftI('shiftLeftByScalar', (x,s) => (x << (s & 15)) << 16 >> 16); +shiftU('shiftLeftByScalar', (x,s) => (x << (s & 15)) << 16 >>> 16); +shiftI('shiftRightByScalar', (x,s) => ((x << 16 >> 16) >> (s & 15)) << 16 >> 16); +shiftU('shiftRightByScalar', (x,s) => ((x << 16 >>> 16) >>> (s & 15)) << 16 >>> 16); + + +// Comparisons. +function compareI(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + + `var fut = i16x8.${opname};` + + 'function f(v1, v2) { v1 = i16x8chk(v1); v2 = i16x8chk(v2); return fut(v1, v2); } return f'), this); + let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16); + let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16); + let ref = zipmap(a1, a2, lanefunc); + let v1 = SIMD.Int16x8(...a1); + let v2 = SIMD.Int16x8(...a2); + assertEqVecArr(simdfunc(v1, v2), ref); +} + +function compareU(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + U16x8 + U16x8I16x8 + + `var fut = u16x8.${opname};` + + 'function f(v1, v2) { v1 = i16x8chk(v1); v2 = i16x8chk(v2); return fut(u16x8i16x8(v1), u16x8i16x8(v2)); } return f'), this); + let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >>> 16); + let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >>> 16); + let ref = zipmap(a1, a2, lanefunc); + let v1 = SIMD.Int16x8(...a1); + let v2 = SIMD.Int16x8(...a2); + assertEqVecArr(simdfunc(v1, v2), ref); +} + +compareI("equal", (x,y) => x == y); +compareU("equal", (x,y) => x == y); +compareI("notEqual", (x,y) => x != y); +compareU("notEqual", (x,y) => x != y); +compareI("lessThan", (x,y) => x < y); +compareU("lessThan", (x,y) => x < y); +compareI("lessThanOrEqual", (x,y) => x <= y); +compareU("lessThanOrEqual", (x,y) => x <= y); +compareI("greaterThan", (x,y) => x > y); +compareU("greaterThan", (x,y) => x > y); +compareI("greaterThanOrEqual", (x,y) => x >= y); +compareU("greaterThanOrEqual", (x,y) => x >= y); diff --git a/js/src/jit-test/tests/asm.js/testSIMD-8x16.js b/js/src/jit-test/tests/asm.js/testSIMD-8x16.js new file mode 100644 index 000000000000..160da82a8ef6 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testSIMD-8x16.js @@ -0,0 +1,524 @@ +load(libdir + "asm.js"); +load(libdir + "simd.js"); +load(libdir + "asserts.js"); + +// Set to true to see more JS debugging spew. +const DEBUG = false; + +if (!isSimdAvailable()) { + DEBUG && print("won't run tests as simd extensions aren't activated yet"); + quit(0); +} + +// Tests for 8x16 SIMD types: Int8x16, Uint8x16, Bool8x16. + +const I8x16 = 'var i8x16 = glob.SIMD.Int8x16;' +const I8x16CHK = 'var i8x16chk = i8x16.check;' +const I8x16EXT = 'var i8x16ext = i8x16.extractLane;' +const I8x16REP = 'var i8x16rep = i8x16.replaceLane;' +const I8x16U8x16 = 'var i8x16u8x16 = i8x16.fromUint8x16Bits;' + +const U8x16 = 'var u8x16 = glob.SIMD.Uint8x16;' +const U8x16CHK = 'var u8x16chk = u8x16.check;' +const U8x16EXT = 'var u8x16ext = u8x16.extractLane;' +const U8x16REP = 'var u8x16rep = u8x16.replaceLane;' +const U8x16I8x16 = 'var u8x16i8x16 = u8x16.fromInt8x16Bits;' + +const B8x16 = 'var b8x16 = glob.SIMD.Bool8x16;' +const B8x16CHK = 'var b8x16chk = b8x16.check;' +const B8x16EXT = 'var b8x16ext = b8x16.extractLane;' +const B8x16REP = 'var b8x16rep = b8x16.replaceLane;' + +const INT8_MAX = 127 +const INT8_MIN = -128 +const UINT8_MAX = 255 + +// Linking +assertEq(asmLink(asmCompile('glob', USE_ASM + I8x16 + "function f() {} return f"), {SIMD:{Int8x16: SIMD.Int8x16}})(), undefined); +assertEq(asmLink(asmCompile('glob', USE_ASM + U8x16 + "function f() {} return f"), {SIMD:{Uint8x16: SIMD.Uint8x16}})(), undefined); +assertEq(asmLink(asmCompile('glob', USE_ASM + B8x16 + "function f() {} return f"), {SIMD:{Bool8x16: SIMD.Bool8x16}})(), undefined); + +// Local variable of Int8x16 type. +assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Int8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f"); +assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16;} return f"); +assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16();} return f"); +assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1);} return f"); +assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4);} return f"); +assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16.0);} return f"); +assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17);} return f"); +assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16|0);} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f"), this)(), undefined); +assertEq(asmLink(asmCompile('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15," + (INT8_MAX + 1) + ");} return f"), this)(), undefined); + +// Local variable of Uint8x16 type. +assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Uint8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f"); +assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16;} return f"); +assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16();} return f"); +assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1);} return f"); +assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4);} return f"); +assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16.0);} return f"); +assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17);} return f"); +assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16|0);} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f"), this)(), undefined); +assertEq(asmLink(asmCompile('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15," + (UINT8_MAX + 1) + ");} return f"), this)(), undefined); + +// Local variable of Bool8x16 type. +assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Bool8x16(1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1);} return f"); +assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16;} return f"); +assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16();} return f"); +assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1);} return f"); +assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1,0,0,0);} return f"); +assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1.0);} return f"); +assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1|0);} return f"); +assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1);} return f"); +assertEq(asmLink(asmCompile('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1,0,0,0,0,0,0,0,0,1,-1,2,-2,1,1,1);} return f"), this)(), undefined); + +// Only signed Int8x16 allowed as return value. +assertEqVecArr(asmLink(asmCompile('glob', USE_ASM + I8x16 + "function f() {return i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f"), this)(), + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); +assertEqVecArr(asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + "function f() {return i8x16chk(i8x16(1,2,3,132,5,6,7,8,9,10,11,12,13,14,15,16));} return f"), this)(), + [1, 2, 3, -124, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); +assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {return u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f"); +assertAsmTypeFail('glob', USE_ASM + U8x16 + U8x16CHK + "function f() {return u8x16chk(u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16));} return f"); + +// Test splat. +function splat(x) { + let r = [] + for (let i = 0; i < 16; i++) + r.push(x); + return r +} + +splatB = asmLink(asmCompile('glob', USE_ASM + B8x16 + + 'var splat = b8x16.splat;' + + 'function f(x) { x = x|0; return splat(x); } return f'), this); +assertEqVecArr(splatB(true), splat(true)); +assertEqVecArr(splatB(false), splat(false)); + + +splatB0 = asmLink(asmCompile('glob', USE_ASM + B8x16 + + 'var splat = b8x16.splat;' + + 'function f() { var x = 0; return splat(x); } return f'), this); +assertEqVecArr(splatB0(), splat(false)); +splatB1 = asmLink(asmCompile('glob', USE_ASM + B8x16 + + 'var splat = b8x16.splat;' + + 'function f() { var x = 1; return splat(x); } return f'), this); +assertEqVecArr(splatB1(), splat(true)); + +splatI = asmLink(asmCompile('glob', USE_ASM + I8x16 + + 'var splat = i8x16.splat;' + + 'function f(x) { x = x|0; return splat(x); } return f'), this); +for (let x of [0, 1, -1, 0x1234, 0x12, 1000, -1000000]) { + assertEqVecArr(splatI(x), splat(x << 24 >> 24)); +} + +splatIc = asmLink(asmCompile('glob', USE_ASM + I8x16 + + 'var splat = i8x16.splat;' + + 'function f() { var x = 100; return splat(x); } return f'), this); +assertEqVecArr(splatIc(), splat(100)) + +splatU = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16U8x16 + + 'var splat = u8x16.splat;' + + 'function f(x) { x = x|0; return i8x16u8x16(splat(x)); } return f'), this); +for (let x of [0, 1, -1, 0x1234, 0x12, 1000, -1000000]) { + assertEqVecArr(SIMD.Uint8x16.fromInt8x16Bits(splatI(x)), splat(x << 24 >>> 24)); +} + +splatUc = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16U8x16 + + 'var splat = u8x16.splat;' + + 'function f() { var x = 200; return i8x16u8x16(splat(x)); } return f'), this); +assertEqVecArr(SIMD.Uint8x16.fromInt8x16Bits(splatUc()), splat(200)) + + +// Test extractLane. +// +// The lane index must be a literal int, and we generate different code for +// different lanes. +function extractI(a, i) { + return asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16EXT + + `function f() {var x=i8x16(${a.join(',')}); return i8x16ext(x, ${i})|0; } return f`), this)(); +} +a = [-1,2,-3,4,-5,6,-7,8,-9,10,-11,12,-13,-14,-15,-16]; +for (var i = 0; i < 16; i++) + assertEq(extractI(a, i), a[i]); +a = a.map(x => -x); +for (var i = 0; i < 16; i++) + assertEq(extractI(a, i), a[i]); + +function extractU(a, i) { + return asmLink(asmCompile('glob', USE_ASM + U8x16 + U8x16EXT + + `function f() {var x=u8x16(${a.join(',')}); return u8x16ext(x, ${i})|0; } return f`), this)(); +} +a = [1,255,12,13,14,150,200,3,4,5,6,7,8,9,10,16]; +for (var i = 0; i < 16; i++) + assertEq(extractU(a, i), a[i]); +a = a.map(x => 255-x); +for (var i = 0; i < 16; i++) + assertEq(extractU(a, i), a[i]); + +function extractB(a, i) { + return asmLink(asmCompile('glob', USE_ASM + B8x16 + B8x16EXT + + `function f() {var x=b8x16(${a.join(',')}); return b8x16ext(x, ${i})|0; } return f`), this)(); +} +a = [1,1,0,1,1,0,0,0,1,1,1,1,0,0,0,1]; +for (var i = 0; i < 16; i++) + assertEq(extractB(a, i), a[i]); +a = a.map(x => 1-x); +for (var i = 0; i < 16; i++) + assertEq(extractB(a, i), a[i]); + +// Test replaceLane. +function replaceI(a, i) { + return asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16REP + + `function f(v) {v=v|0; var x=i8x16(${a.join(',')}); return i8x16rep(x,${i},v); } return f`), this); +} +a = [-1,2,-3,4,-5,6,-7,8,-9,10,-11,12,-13,-14,-15,-16]; +for (var i = 0; i < 16; i++) { + var f = replaceI(a, i); + var b = a.slice(0); + b[i] = -20; + assertEqVecArr(f(-20), b); +} + +function replaceU(a, i) { + return asmLink(asmCompile('glob', USE_ASM + U8x16 + U8x16REP + I8x16 + I8x16U8x16 + + `function f(v) {v=v|0; var x=u8x16(${a.join(',')}); x=u8x16rep(x,${i},v); return i8x16u8x16(x); } return f`), this); +} +a = [256-1,2,256-3,4,256-5,6,256-7,8,256-9,10,256-11,12,256-13,256-14,256-15,256-16]; +for (var i = 0; i < 16; i++) { + // Result returned as Int8x16, convert back. + var rawf = replaceU(a, i); + var f = x => SIMD.Uint8x16.fromInt8x16Bits(rawf(x)); + var b = a.slice(0); + b[i] = 100; + assertEqVecArr(f(100), b); +} + +function replaceB(a, i) { + return asmLink(asmCompile('glob', USE_ASM + B8x16 + B8x16REP + + `function f(v) {v=v|0; var x=b8x16(${a.join(',')}); return b8x16rep(x,${i},v); } return f`), this); +} +a = [1,1,0,1,1,0,0,0,1,1,1,1,0,0,0,1]; +for (var i = 0; i < 16; i++) { + var f = replaceB(a, i); + var b = a.slice(0); + v = 1 - a[i]; + b[i] = v; + assertEqVecArr(f(v), b.map(x => !!x)); +} + + +// Test select. +selectI = asmLink(asmCompile('glob', USE_ASM + I8x16 + B8x16 + B8x16CHK + + 'var select = i8x16.select;' + + 'var a = i8x16(-1,2,-3,4,-5, 6,-7, 8,-9,10,-11,12,-13,-14,-15,-16);' + + 'var b = i8x16( 5,6, 7,8, 9,10,11,12,13,14, 15,16,-77, 45, 32, 0);' + + 'function f(x) { x = b8x16chk(x); return select(x, a, b); } return f'), this); +assertEqVecArr(selectI(SIMD.Bool8x16( 0,0, 1,0, 1,1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1)), + [ 5,6,-3,8,-5,6,-7,12,-9,10,15,16,-13,-14,32,-16]); + +selectU = asmLink(asmCompile('glob', USE_ASM + I8x16 + B8x16 + B8x16CHK + U8x16 + I8x16U8x16 + U8x16I8x16 + + 'var select = u8x16.select;' + + 'var a = i8x16(-1,2,-3,4,-5, 6,-7, 8,-9,10,-11,12,-13,-14,-15,-16);' + + 'var b = i8x16( 5,6, 7,8, 9,10,11,12,13,14, 15,16,-77, 45, 32, 0);' + + 'function f(x) { x = b8x16chk(x); return i8x16u8x16(select(x, u8x16i8x16(a), u8x16i8x16(b))); } return f'), this); +assertEqVecArr(selectU(SIMD.Bool8x16( 0,0, 1,0, 1,1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1)), + [ 5,6,-3,8,-5,6,-7,12,-9,10,15,16,-13,-14,32,-16]); + + +// Test swizzle. +function swizzle(vec, lanes) { + let r = []; + for (let i = 0; i < 16; i++) + r.push(vec[lanes[i]]); + return r; +} + +function swizzleI(lanes) { + let asm = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + + 'var swz = i8x16.swizzle;' + + `function f(a) { a = i8x16chk(a); return swz(a, ${lanes.join()}); } return f`), this); + let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16]; + let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1]; + let v1 = SIMD.Int8x16(...a1); + let v2 = SIMD.Int8x16(...a2); + assertEqVecArr(asm(v1), swizzle(a1, lanes)); + assertEqVecArr(asm(v2), swizzle(a2, lanes)); +} + +swizzleI([10, 1, 7, 5, 1, 2, 6, 8, 5, 13, 0, 6, 2, 8, 0, 9]); +swizzleI([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); +swizzleI([15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15]); + +function swizzleU(lanes) { + let asm = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + U8x16 + U8x16I8x16 + I8x16U8x16 + + 'var swz = u8x16.swizzle;' + + `function f(a) { a = i8x16chk(a); return i8x16u8x16(swz(u8x16i8x16(a), ${lanes.join()})); } return f`), this); + let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16]; + let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1]; + let v1 = SIMD.Int8x16(...a1); + let v2 = SIMD.Int8x16(...a2); + assertEqVecArr(asm(v1), swizzle(a1, lanes)); + assertEqVecArr(asm(v2), swizzle(a2, lanes)); +} + +swizzleU([10, 1, 7, 5, 1, 2, 6, 8, 5, 13, 0, 6, 2, 8, 0, 9]); +swizzleU([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); +swizzleU([15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15]); + +// Out-of-range lane indexes. +assertAsmTypeFail('glob', USE_ASM + I8x16 + 'var swz = i8x16.swizzle; ' + + 'function f() { var x=i8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16); } return f'); +assertAsmTypeFail('glob', USE_ASM + U8x16 + 'var swz = u8x16.swizzle; ' + + 'function f() { var x=u8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16); } return f'); +// Missing lane indexes. +assertAsmTypeFail('glob', USE_ASM + I8x16 + 'var swz = i8x16.swizzle; ' + + 'function f() { var x=i8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); } return f'); +assertAsmTypeFail('glob', USE_ASM + U8x16 + 'var swz = u8x16.swizzle; ' + + 'function f() { var x=u8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); } return f'); + + +// Test shuffle. +function shuffle(vec1, vec2, lanes) { + let r = []; + let vec = vec1.concat(vec2); + for (let i = 0; i < 16; i++) + r.push(vec[lanes[i]]); + return r; +} + +function shuffleI(lanes) { + let asm = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + + 'var shuf = i8x16.shuffle;' + + `function f(a1, a2) { a1 = i8x16chk(a1); a2 = i8x16chk(a2); return shuf(a1, a2, ${lanes.join()}); } return f`), this); + let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16]; + let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1]; + let v1 = SIMD.Int8x16(...a1); + let v2 = SIMD.Int8x16(...a2); + assertEqVecArr(asm(v1, v2), shuffle(a1, a2, lanes)); +} + +shuffleI([31, 9, 5, 4, 29, 12, 19, 10, 16, 22, 10, 9, 6, 18, 9, 8]); +shuffleI([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); +shuffleI([31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31]); + +function shuffleU(lanes) { + let asm = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + U8x16 + U8x16I8x16 + I8x16U8x16 + + 'var shuf = u8x16.shuffle;' + + 'function f(a1, a2) { a1 = i8x16chk(a1); a2 = i8x16chk(a2); ' + + `return i8x16u8x16(shuf(u8x16i8x16(a1), u8x16i8x16(a2), ${lanes.join()})); } return f`), this); + let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16]; + let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1]; + let v1 = SIMD.Int8x16(...a1); + let v2 = SIMD.Int8x16(...a2); + assertEqVecArr(asm(v1, v2), shuffle(a1, a2, lanes)); +} + +shuffleU([31, 9, 5, 4, 29, 12, 19, 10, 16, 22, 10, 9, 6, 18, 9, 8]); +shuffleU([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); +shuffleU([31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31]); + + +// Out-of-range lane indexes. +assertAsmTypeFail('glob', USE_ASM + I8x16 + 'var shuf = i8x16.shuffle; ' + + 'function f() { var x=i8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); shuf(x,x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,32); } return f'); +assertAsmTypeFail('glob', USE_ASM + U8x16 + 'var shuf = u8x16.shuffle; ' + + 'function f() { var x=u8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); shuf(x,x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,32); } return f'); +// Missing lane indexes. +assertAsmTypeFail('glob', USE_ASM + I8x16 + 'var shuf = i8x16.shuffle; ' + + 'function f() { var x=i8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); shuf(x,x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); } return f'); +assertAsmTypeFail('glob', USE_ASM + U8x16 + 'var shuf = u8x16.shuffle; ' + + 'function f() { var x=u8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); shuf(x,x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); } return f'); + + +// Test unary operators. +function unaryI(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + + `var fut = i8x16.${opname};` + + 'function f(v) { v = i8x16chk(v); return fut(v); } return f'), this); + let a = [-1,2,-3,4,-5,6,-7,8,-9,10,-11,12,-13,-14,-15,-16]; + let v = SIMD.Int8x16(...a); + assertEqVecArr(simdfunc(v), a.map(lanefunc)); +} + +function unaryU(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16CHK + U8x16I8x16 + I8x16U8x16 + + `var fut = u8x16.${opname};` + + 'function f(v) { v = i8x16chk(v); return i8x16u8x16(fut(u8x16i8x16(v))); } return f'), this); + let a = [256-1,2,256-3,4,256-5,6,256-7,8,256-9,10,256-11,12,256-13,256-14,256-15,256-16]; + let v = SIMD.Int8x16(...a); + assertEqVecArr(SIMD.Uint8x16.fromInt8x16Bits(simdfunc(v)), a.map(lanefunc)); +} + +function unaryB(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + B8x16 + B8x16CHK + + `var fut = b8x16.${opname};` + + 'function f(v) { v = b8x16chk(v); return fut(v); } return f'), this); + let a = [1,1,0,1,1,0,0,0,1,1,1,1,0,0,0,1]; + let v = SIMD.Bool8x16(...a); + assertEqVecArr(simdfunc(v), a.map(lanefunc)); +} + +unaryI('not', x => ~x << 24 >> 24); +unaryU('not', x => ~x << 24 >>> 24); +unaryB('not', x => !x); +unaryI('neg', x => -x << 24 >> 24); +unaryU('neg', x => -x << 24 >>> 24); + + +// Test binary operators. +function zipmap(a1, a2, f) { + assertEq(a1.length, a2.length); + let r = []; + for (var i = 0; i < a1.length; i++) + r.push(f(a1[i], a2[i])); + return r +} + +function binaryI(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + + `var fut = i8x16.${opname};` + + 'function f(v1, v2) { v1 = i8x16chk(v1); v2 = i8x16chk(v2); return fut(v1, v2); } return f'), this); + let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16]; + let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1]; + let ref = zipmap(a1, a2, lanefunc); + let v1 = SIMD.Int8x16(...a1); + let v2 = SIMD.Int8x16(...a2); + assertEqVecArr(simdfunc(v1, v2), ref); +} + +function binaryU(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16CHK + U8x16I8x16 + I8x16U8x16 + + `var fut = u8x16.${opname};` + + 'function f(v1, v2) { v1 = i8x16chk(v1); v2 = i8x16chk(v2); return i8x16u8x16(fut(u8x16i8x16(v1), u8x16i8x16(v2))); } return f'), this); + let a1 = [ -1,2, -3,0x80,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16].map(x => x & 0xff); + let a2 = [0x80,2,0x80,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16,0xff].map(x => x & 0xff); + let ref = zipmap(a1, a2, lanefunc); + let v1 = SIMD.Int8x16(...a1); + let v2 = SIMD.Int8x16(...a2); + let res = SIMD.Uint8x16.fromInt8x16Bits(simdfunc(v1, v2)); + assertEqVecArr(res, ref); +} + +function binaryB(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + B8x16 + B8x16CHK + + `var fut = b8x16.${opname};` + + 'function f(v1, v2) { v1 = b8x16chk(v1); v2 = b8x16chk(v2); return fut(v1, v2); } return f'), this); + let a = [1,1,0,1,1,0,0,0,1,1,1,1,0,0,0,1]; + let v = SIMD.Bool8x16(...a); + assertEqVecArr(simdfunc(v), a.map(lanefunc)); +} + +binaryI('add', (x, y) => (x + y) << 24 >> 24); +binaryI('sub', (x, y) => (x - y) << 24 >> 24); +binaryI('mul', (x, y) => (x * y) << 24 >> 24); +binaryU('add', (x, y) => (x + y) << 24 >>> 24); +binaryU('sub', (x, y) => (x - y) << 24 >>> 24); +binaryU('mul', (x, y) => (x * y) << 24 >>> 24); + +binaryI('and', (x, y) => (x & y) << 24 >> 24); +binaryI('or', (x, y) => (x | y) << 24 >> 24); +binaryI('xor', (x, y) => (x ^ y) << 24 >> 24); +binaryU('and', (x, y) => (x & y) << 24 >>> 24); +binaryU('or', (x, y) => (x | y) << 24 >>> 24); +binaryU('xor', (x, y) => (x ^ y) << 24 >>> 24); + +function sat(x, lo, hi) { + if (x < lo) return lo; + if (x > hi) return hi; + return x +} +function isat(x) { return sat(x, -128, 127); } +function usat(x) { return sat(x, 0, 255); } + +binaryI('addSaturate', (x, y) => isat(x + y)) +binaryI('subSaturate', (x, y) => isat(x - y)) +binaryU('addSaturate', (x, y) => usat(x + y)) +binaryU('subSaturate', (x, y) => usat(x - y)) + +// Test shift operators. +function zip1map(a, s, f) { + return a.map(x => f(x, s)); +} + +function shiftI(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + + `var fut = i8x16.${opname};` + + 'function f(v, s) { v = i8x16chk(v); s = s|0; return fut(v, s); } return f'), this); + let a = [0x80,2,0x80,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16,0xff]; + let v = SIMD.Int8x16(...a); + for (let s of [0, 1, 2, 6, 7, 8, 9, 10, 16, 255, -1, -8, -7, -1000]) { + let ref = zip1map(a, s, lanefunc); + // 1. Test dynamic shift amount. + assertEqVecArr(simdfunc(v, s), ref); + + // 2. Test constant shift amount. + let cstf = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + + `var fut = i8x16.${opname};` + + `function f(v) { v = i8x16chk(v); return fut(v, ${s}); } return f`), this); + assertEqVecArr(cstf(v, s), ref); + } +} + +function shiftU(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16CHK + U8x16I8x16 + I8x16U8x16 + + `var fut = u8x16.${opname};` + + 'function f(v, s) { v = i8x16chk(v); s = s|0; return i8x16u8x16(fut(u8x16i8x16(v), s)); } return f'), this); + let a = [0x80,2,0x80,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16,0xff]; + let v = SIMD.Int8x16(...a); + for (let s of [0, 1, 2, 6, 7, 8, 9, 10, 16, 255, -1, -8, -7, -1000]) { + let ref = zip1map(a, s, lanefunc); + // 1. Test dynamic shift amount. + assertEqVecArr(SIMD.Uint8x16.fromInt8x16Bits(simdfunc(v, s)), ref); + + // 2. Test constant shift amount. + let cstf = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16CHK + U8x16I8x16 + I8x16U8x16 + + `var fut = u8x16.${opname};` + + `function f(v) { v = i8x16chk(v); return i8x16u8x16(fut(u8x16i8x16(v), ${s})); } return f`), this); + assertEqVecArr(SIMD.Uint8x16.fromInt8x16Bits(cstf(v, s)), ref); + } +} + +shiftI('shiftLeftByScalar', (x,s) => (x << (s & 7)) << 24 >> 24); +shiftU('shiftLeftByScalar', (x,s) => (x << (s & 7)) << 24 >>> 24); +shiftI('shiftRightByScalar', (x,s) => ((x << 24 >> 24) >> (s & 7)) << 24 >> 24); +shiftU('shiftRightByScalar', (x,s) => ((x << 24 >>> 24) >>> (s & 7)) << 24 >>> 24); + + +// Comparisons. +function compareI(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + + `var fut = i8x16.${opname};` + + 'function f(v1, v2) { v1 = i8x16chk(v1); v2 = i8x16chk(v2); return fut(v1, v2); } return f'), this); + let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16]; + let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1]; + let ref = zipmap(a1, a2, lanefunc); + let v1 = SIMD.Int8x16(...a1); + let v2 = SIMD.Int8x16(...a2); + assertEqVecArr(simdfunc(v1, v2), ref); +} + +function compareU(opname, lanefunc) { + let simdfunc = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + U8x16 + U8x16I8x16 + + `var fut = u8x16.${opname};` + + 'function f(v1, v2) { v1 = i8x16chk(v1); v2 = i8x16chk(v2); return fut(u8x16i8x16(v1), u8x16i8x16(v2)); } return f'), this); + let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16].map(x => x << 24 >>> 24); + let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1].map(x => x << 24 >>> 24); + let ref = zipmap(a1, a2, lanefunc); + let v1 = SIMD.Int8x16(...a1); + let v2 = SIMD.Int8x16(...a2); + assertEqVecArr(simdfunc(v1, v2), ref); +} + +compareI("equal", (x,y) => x == y); +compareU("equal", (x,y) => x == y); +compareI("notEqual", (x,y) => x != y); +compareU("notEqual", (x,y) => x != y); +compareI("lessThan", (x,y) => x < y); +compareU("lessThan", (x,y) => x < y); +compareI("lessThanOrEqual", (x,y) => x <= y); +compareU("lessThanOrEqual", (x,y) => x <= y); +compareI("greaterThan", (x,y) => x > y); +compareU("greaterThan", (x,y) => x > y); +compareI("greaterThanOrEqual", (x,y) => x >= y); +compareU("greaterThanOrEqual", (x,y) => x >= y); diff --git a/js/src/jit-test/tests/asm.js/testSIMD-bitcasts.js b/js/src/jit-test/tests/asm.js/testSIMD-bitcasts.js new file mode 100644 index 000000000000..c5a5fb2bcada --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testSIMD-bitcasts.js @@ -0,0 +1,84 @@ +load(libdir + "asm.js"); +load(libdir + "simd.js"); +load(libdir + "asserts.js"); + +// Set to true to see more JS debugging spew. +const DEBUG = false; + +if (!isSimdAvailable()) { + DEBUG && print("won't run tests as simd extensions aren't activated yet"); + quit(0); +} + +// Test all bit-casts and normal loads and stores. +var heap = new ArrayBuffer(BUF_MIN); +var asU8 = new Uint8Array(heap); +var allTypes = [ + "Int8x16", + "Int16x8", + "Int32x4", + "Uint8x16", + "Uint16x8", + "Uint32x4", + "Float32x4" +]; + +// Generate a load bit-cast store test function that performs: +// +// function f(a, b) { +// vec = src.load(H, a); +// cast = dst.from«src»Bits(vec); +// store(H, b, cast); +// } +// +// Here, `H` is the heap provided by `heap`. +function test_func(src, dst) { + text = ` + "use asm"; + var src = glob.SIMD.${src}; + var dst = glob.SIMD.${dst}; + var ld = src.load; + var st = dst.store; + var bc = dst.from${src}Bits; + + var H = new glob.Uint8Array(heap); + + function f(a, b) { + a = a|0; + b = b|0; + + st(H, b, bc(ld(H, a))); + } + + return f; + `; + return asmLink(asmCompile('glob', 'ffi', 'heap', text), this, null, heap); +} + +function assertBuf16(a, b) { + for (let i=0; i < 16; i++) { + assertEq(asU8[a+i], asU8[b+i]); + } +} + +for (let src of allTypes) { + for (let dst of allTypes) { + // Skip identity conversions. + if (src == dst) continue; + + print(src, dst); + let f = test_func(src, dst); + // Initialize with pseudo-random data. + for (let i = 0; i < 64; i++) { + asU8[i] = (i + 17) * 97; + } + + // Aligned load/store. + f(0, 16); + assertBuf16(0, 16); + + // Unaligned access. + f(1, 27); + assertBuf16(1, 27); + } +} From 8c8b287e9865db1f70654d99156520e26f1560c3 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:20 -0700 Subject: [PATCH 061/199] Bug 1136226 - Make MIR New factories private where AddLegalized should be used. r=sunfish Some MIR instructions have an AddLegalized() static function which much be used when creating new instructions. This ensures that the proper expansions are generated. Make the New() factory method in those instructions private so it isn't used inadvertently. De-templatize the inlineSimdBinary<> function since it was only used for two MIR instructions which are now created differently. --- js/src/jit-test/tests/SIMD/binary-arith.js | 7 +++ js/src/jit/IonBuilder.h | 7 +-- js/src/jit/MCallOptimize.cpp | 58 +++++++++++----------- js/src/jit/MIR.h | 31 ++++++------ 4 files changed, 57 insertions(+), 46 deletions(-) diff --git a/js/src/jit-test/tests/SIMD/binary-arith.js b/js/src/jit-test/tests/SIMD/binary-arith.js index a85d069edd8a..74211d46db91 100644 --- a/js/src/jit-test/tests/SIMD/binary-arith.js +++ b/js/src/jit-test/tests/SIMD/binary-arith.js @@ -9,6 +9,9 @@ function f() { var f1 = SIMD.Float32x4(1, 2, 3, 4); var f2 = SIMD.Float32x4(4, 3, 2, 1); + var i8_1 = SIMD.Int8x16(1, 2, 3, 4, 20, 30, 40, 50, 100, 115, 120, 125); + var i8_2 = SIMD.Int8x16(4, 3, 2, 1, 8, 7, 6, 5, 12, 11, 10, 9); + for (var i = 0; i < 150; i++) { assertEqX4(SIMD.Float32x4.add(f1, f2), binaryX((x, y) => x + y, f1, f2)); assertEqX4(SIMD.Float32x4.sub(f1, f2), binaryX((x, y) => x - y, f1, f2)); @@ -17,6 +20,10 @@ function f() { assertEqX4(SIMD.Int32x4.add(i1, i2), binaryX((x, y) => x + y, i1, i2)); assertEqX4(SIMD.Int32x4.sub(i1, i2), binaryX((x, y) => x - y, i1, i2)); assertEqX4(SIMD.Int32x4.mul(i1, i2), binaryX((x, y) => x * y, i1, i2)); + + assertEqX4(SIMD.Int8x16.add(i8_1, i8_2), binaryX((x, y) => (x + y) << 24 >> 24, i8_1, i8_2)); + assertEqX4(SIMD.Int8x16.sub(i8_1, i8_2), binaryX((x, y) => (x - y) << 24 >> 24, i8_1, i8_2)); + assertEqX4(SIMD.Int8x16.mul(i8_1, i8_2), binaryX((x, y) => (x * y) << 24 >> 24, i8_1, i8_2)); } } diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index da09ec8047ad..65da67fc54c0 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -915,9 +915,10 @@ class IonBuilder InliningStatus inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type); - template - InliningStatus inlineSimdBinary(CallInfo& callInfo, JSNative native, - typename T::Operation op, SimdType type); + InliningStatus inlineSimdBinaryArith(CallInfo& callInfo, JSNative native, + MSimdBinaryArith::Operation op, SimdType type); + InliningStatus inlineSimdBinaryBitwise(CallInfo& callInfo, JSNative native, + MSimdBinaryBitwise::Operation op, SimdType type); InliningStatus inlineSimdBinarySaturating(CallInfo& callInfo, JSNative native, MSimdBinarySaturating::Operation op, SimdType type); InliningStatus inlineSimdShift(CallInfo& callInfo, JSNative native, MSimdShift::Operation op, diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 69b0e4b22b5e..26915b36fd8d 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -3245,29 +3245,21 @@ IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type) // Binary arithmetic. case SimdOperation::Fn_add: - return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_add, - type); + return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_add, type); case SimdOperation::Fn_sub: - return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_sub, - type); + return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_sub, type); case SimdOperation::Fn_mul: - return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_mul, - type); + return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_mul, type); case SimdOperation::Fn_div: - return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_div, - type); + return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_div, type); case SimdOperation::Fn_max: - return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_max, - type); + return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_max, type); case SimdOperation::Fn_min: - return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_min, - type); + return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_min, type); case SimdOperation::Fn_maxNum: - return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_maxNum, - type); + return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_maxNum, type); case SimdOperation::Fn_minNum: - return inlineSimdBinary(callInfo, native, MSimdBinaryArith::Op_minNum, - type); + return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_minNum, type); // Binary saturating. case SimdOperation::Fn_addSaturate: @@ -3277,16 +3269,13 @@ IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type) // Binary bitwise. case SimdOperation::Fn_and: - return inlineSimdBinary(callInfo, native, MSimdBinaryBitwise::and_, - type); + return inlineSimdBinaryBitwise(callInfo, native, MSimdBinaryBitwise::and_, type); case SimdOperation::Fn_or: - return inlineSimdBinary(callInfo, native, MSimdBinaryBitwise::or_, - type); + return inlineSimdBinaryBitwise(callInfo, native, MSimdBinaryBitwise::or_, type); case SimdOperation::Fn_xor: - return inlineSimdBinary(callInfo, native, MSimdBinaryBitwise::xor_, - type); + return inlineSimdBinaryBitwise(callInfo, native, MSimdBinaryBitwise::xor_, type); - // Shifts. + // Shifts. case SimdOperation::Fn_shiftLeftByScalar: return inlineSimdShift(callInfo, native, MSimdShift::lsh, type); case SimdOperation::Fn_shiftRightByScalar: @@ -3549,11 +3538,9 @@ IonBuilder::boxSimd(CallInfo& callInfo, MDefinition* ins, InlineTypedObject* tem return InliningStatus_Inlined; } -// Inline a binary SIMD operation where both arguments are SIMD types. -template IonBuilder::InliningStatus -IonBuilder::inlineSimdBinary(CallInfo& callInfo, JSNative native, typename T::Operation op, - SimdType type) +IonBuilder::inlineSimdBinaryArith(CallInfo& callInfo, JSNative native, + MSimdBinaryArith::Operation op, SimdType type) { InlineTypedObject* templateObj = nullptr; if (!canInlineSimd(callInfo, native, 2, &templateObj)) @@ -3562,7 +3549,22 @@ IonBuilder::inlineSimdBinary(CallInfo& callInfo, JSNative native, typename T::Op MDefinition* lhs = unboxSimd(callInfo.getArg(0), type); MDefinition* rhs = unboxSimd(callInfo.getArg(1), type); - T* ins = T::New(alloc(), lhs, rhs, op); + auto* ins = MSimdBinaryArith::AddLegalized(alloc(), current, lhs, rhs, op); + return boxSimd(callInfo, ins, templateObj); +} + +IonBuilder::InliningStatus +IonBuilder::inlineSimdBinaryBitwise(CallInfo& callInfo, JSNative native, + MSimdBinaryBitwise::Operation op, SimdType type) +{ + InlineTypedObject* templateObj = nullptr; + if (!canInlineSimd(callInfo, native, 2, &templateObj)) + return InliningStatus_NotInlined; + + MDefinition* lhs = unboxSimd(callInfo.getArg(0), type); + MDefinition* rhs = unboxSimd(callInfo.getArg(1), type); + + auto* ins = MSimdBinaryBitwise::New(alloc(), lhs, rhs, op); return boxSimd(callInfo, ins, templateObj); } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 5bf493d951a1..07e36f0d8f6c 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1705,14 +1705,14 @@ class MSimdConvert } } - public: - INSTRUCTION_HEADER(SimdConvert) - static MSimdConvert* New(TempAllocator& alloc, MDefinition* obj, MIRType toType, SimdSign sign) { - return new(alloc) MSimdConvert(obj, toType, sign); + return new (alloc) MSimdConvert(obj, toType, sign); } + public: + INSTRUCTION_HEADER(SimdConvert) + // Create a MSimdConvert instruction and add it to the basic block. // Possibly create and add an equivalent sequence of instructions instead if // the current target doesn't support the requested conversion directly. @@ -2288,15 +2288,15 @@ class MSimdBinaryComp setCommutative(); } - public: - INSTRUCTION_HEADER(SimdBinaryComp) - static MSimdBinaryComp* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, Operation op, SimdSign sign) { - return new(alloc) MSimdBinaryComp(left, right, op, sign); + return new (alloc) MSimdBinaryComp(left, right, op, sign); } + public: + INSTRUCTION_HEADER(SimdBinaryComp) + // Create a MSimdBinaryComp or an equivalent sequence of instructions // supported by the current target. // Add all instructions to the basic block |addTo|. @@ -2379,14 +2379,15 @@ class MSimdBinaryArith setCommutative(); } - public: - INSTRUCTION_HEADER(SimdBinaryArith) static MSimdBinaryArith* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, Operation op) { - return new(alloc) MSimdBinaryArith(left, right, op); + return new (alloc) MSimdBinaryArith(left, right, op); } + public: + INSTRUCTION_HEADER(SimdBinaryArith) + // Create an MSimdBinaryArith instruction and add it to the basic block. Possibly // create and add an equivalent sequence of instructions instead if the // current target doesn't support the requested shift operation directly. @@ -2558,15 +2559,15 @@ class MSimdShift setMovable(); } - public: - INSTRUCTION_HEADER(SimdShift) - static MSimdShift* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, Operation op) { - return new(alloc) MSimdShift(left, right, op); + return new (alloc) MSimdShift(left, right, op); } + public: + INSTRUCTION_HEADER(SimdShift) + // Create an MSimdShift instruction and add it to the basic block. Possibly // create and add an equivalent sequence of instructions instead if the // current target doesn't support the requested shift operation directly. From 61cdb6908fc26efd29d20004f5531f1bd3ad26be Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 31 May 2016 09:00:20 -0700 Subject: [PATCH 062/199] Bug 1136226 - Inline small int SIMD.js constructor calls. r=sunfish Use the same strategy as we do for asm.js: Start from an initial vector and insert one lane at a time. --- js/src/jit/MCallOptimize.cpp | 51 ++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 26915b36fd8d..efdc3c5da441 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -3393,12 +3393,6 @@ IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) return InliningStatus_NotInlined; } - // NYI, this will be removed by a bug 1136226 patch. - if (SimdTypeToLength(simdType) != 4) { - trackOptimizationOutcome(TrackedOutcome::SimdTypeNotOptimized); - return InliningStatus_NotInlined; - } - // Take the templateObject out of Baseline ICs, such that we can box // SIMD value type in the same kind of objects. MOZ_ASSERT(size_t(descr->size(descr->type())) < InlineTypedObject::MaximumSize); @@ -3418,7 +3412,8 @@ IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) // containing the coercion of 'undefined' to the right type. MConstant* defVal = nullptr; MIRType laneType = SimdTypeToLaneType(simdType); - if (callInfo.argc() < SimdTypeToLength(simdType)) { + unsigned lanes = SimdTypeToLength(simdType); + if (lanes != 4 || callInfo.argc() < lanes) { if (laneType == MIRType::Int32) { defVal = constant(Int32Value(0)); } else if (laneType == MIRType::Boolean) { @@ -3432,19 +3427,41 @@ IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) } } - MDefinition* lane[4]; - for (unsigned i = 0; i < 4; i++) - lane[i] = callInfo.getArgWithDefault(i, defVal); + MInstruction *values = nullptr; - // Convert boolean lanes into Int32 0 / -1. - if (laneType == MIRType::Boolean) { + // Use the MSimdValueX4 constructor for X4 vectors. + if (lanes == 4) { + MDefinition* lane[4]; for (unsigned i = 0; i < 4; i++) - lane[i] = convertToBooleanSimdLane(lane[i]); - } + lane[i] = callInfo.getArgWithDefault(i, defVal); - MSimdValueX4* values = - MSimdValueX4::New(alloc(), simdType, lane[0], lane[1], lane[2], lane[3]); - current->add(values); + // Convert boolean lanes into Int32 0 / -1. + if (laneType == MIRType::Boolean) { + for (unsigned i = 0; i < 4; i++) + lane[i] = convertToBooleanSimdLane(lane[i]); + } + + values = MSimdValueX4::New(alloc(), simdType, lane[0], lane[1], lane[2], lane[3]); + current->add(values); + } else { + // For general constructor calls, start from splat(defVal), insert one + // lane at a time. + values = MSimdSplat::New(alloc(), defVal, simdType); + current->add(values); + + // Stop early if constructor doesn't have enough arguments. These lanes + // then get the default value. + if (callInfo.argc() < lanes) + lanes = callInfo.argc(); + + for (unsigned i = 0; i < lanes; i++) { + MDefinition* lane = callInfo.getArg(i); + if (laneType == MIRType::Boolean) + lane = convertToBooleanSimdLane(lane); + values = MSimdInsertElement::New(alloc(), values, lane, i); + current->add(values); + } + } MSimdBox* obj = MSimdBox::New(alloc(), constraints(), values, inlineTypedObject, descr->type(), inlineTypedObject->group()->initialHeap(constraints())); From 6a4e9a45853d76db2a0d2e7e86be406fdd9e7b03 Mon Sep 17 00:00:00 2001 From: Jakob Olesen Date: Tue, 31 May 2016 09:00:20 -0700 Subject: [PATCH 063/199] Bug 1136226 - Enable inlining of 8x16 and 16x8 types. r=sunfish --- js/src/builtin/SIMD.cpp | 36 ++++++++++++++++++---- js/src/jit/BaselineIC.cpp | 6 ++++ js/src/jit/InlinableNatives.h | 6 ++++ js/src/jit/MCallOptimize.cpp | 24 ++++++++++++--- js/src/jit/Recover.cpp | 36 +++++++++++----------- js/src/jit/shared/CodeGenerator-shared.cpp | 6 +++- 6 files changed, 84 insertions(+), 30 deletions(-) diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 835622d27588..fa84b7fbab29 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -277,6 +277,22 @@ static_assert(uint64_t(SimdOperation::Last) <= UINT16_MAX, "SimdOperation must f FLOAT32X4_FUNCTION_LIST(TDEFN) #undef TDEFN +#define TDEFN(Name, Func, Operands) DEFN(Int8x16, Name) +INT8X16_FUNCTION_LIST(TDEFN) +#undef TDEFN + +#define TDEFN(Name, Func, Operands) DEFN(Uint8x16, Name) +UINT8X16_FUNCTION_LIST(TDEFN) +#undef TDEFN + +#define TDEFN(Name, Func, Operands) DEFN(Int16x8, Name) +INT16X8_FUNCTION_LIST(TDEFN) +#undef TDEFN + +#define TDEFN(Name, Func, Operands) DEFN(Uint16x8, Name) +UINT16X8_FUNCTION_LIST(TDEFN) +#undef TDEFN + #define TDEFN(Name, Func, Operands) DEFN(Int32x4, Name) INT32X4_FUNCTION_LIST(TDEFN) #undef TDEFN @@ -285,6 +301,14 @@ INT32X4_FUNCTION_LIST(TDEFN) UINT32X4_FUNCTION_LIST(TDEFN) #undef TDEFN +#define TDEFN(Name, Func, Operands) DEFN(Bool8x16, Name) +BOOL8X16_FUNCTION_LIST(TDEFN) +#undef TDEFN + +#define TDEFN(Name, Func, Operands) DEFN(Bool16x8, Name) +BOOL16X8_FUNCTION_LIST(TDEFN) +#undef TDEFN + #define TDEFN(Name, Func, Operands) DEFN(Bool32x4, Name) BOOL32X4_FUNCTION_LIST(TDEFN) #undef TDEFN @@ -310,7 +334,7 @@ const JSFunctionSpec Float64x2Defn::Methods[] = { const JSFunctionSpec Int8x16Defn::Methods[] = { #define SIMD_INT8X16_FUNCTION_ITEM(Name, Func, Operands) \ - JS_FN(#Name, js::simd_int8x16_##Name, Operands, 0), + JS_INLINABLE_FN(#Name, js::simd_int8x16_##Name, Operands, 0, SimdInt8x16_##Name), INT8X16_FUNCTION_LIST(SIMD_INT8X16_FUNCTION_ITEM) #undef SIMD_INT8X16_FUNCTION_ITEM JS_FS_END @@ -318,7 +342,7 @@ const JSFunctionSpec Int8x16Defn::Methods[] = { const JSFunctionSpec Int16x8Defn::Methods[] = { #define SIMD_INT16X8_FUNCTION_ITEM(Name, Func, Operands) \ - JS_FN(#Name, js::simd_int16x8_##Name, Operands, 0), + JS_INLINABLE_FN(#Name, js::simd_int16x8_##Name, Operands, 0, SimdInt16x8_##Name), INT16X8_FUNCTION_LIST(SIMD_INT16X8_FUNCTION_ITEM) #undef SIMD_INT16X8_FUNCTION_ITEM JS_FS_END @@ -334,7 +358,7 @@ const JSFunctionSpec Int32x4Defn::Methods[] = { const JSFunctionSpec Uint8x16Defn::Methods[] = { #define SIMD_UINT8X16_FUNCTION_ITEM(Name, Func, Operands) \ - JS_FN(#Name, js::simd_uint8x16_##Name, Operands, 0), + JS_INLINABLE_FN(#Name, js::simd_uint8x16_##Name, Operands, 0, SimdUint8x16_##Name), UINT8X16_FUNCTION_LIST(SIMD_UINT8X16_FUNCTION_ITEM) #undef SIMD_UINT8X16_FUNCTION_ITEM JS_FS_END @@ -342,7 +366,7 @@ const JSFunctionSpec Uint8x16Defn::Methods[] = { const JSFunctionSpec Uint16x8Defn::Methods[] = { #define SIMD_UINT16X8_FUNCTION_ITEM(Name, Func, Operands) \ - JS_FN(#Name, js::simd_uint16x8_##Name, Operands, 0), + JS_INLINABLE_FN(#Name, js::simd_uint16x8_##Name, Operands, 0, SimdUint16x8_##Name), UINT16X8_FUNCTION_LIST(SIMD_UINT16X8_FUNCTION_ITEM) #undef SIMD_UINT16X8_FUNCTION_ITEM JS_FS_END @@ -358,7 +382,7 @@ const JSFunctionSpec Uint32x4Defn::Methods[] = { const JSFunctionSpec Bool8x16Defn::Methods[] = { #define SIMD_BOOL8X16_FUNCTION_ITEM(Name, Func, Operands) \ - JS_FN(#Name, js::simd_bool8x16_##Name, Operands, 0), + JS_INLINABLE_FN(#Name, js::simd_bool8x16_##Name, Operands, 0, SimdBool8x16_##Name), BOOL8X16_FUNCTION_LIST(SIMD_BOOL8X16_FUNCTION_ITEM) #undef SIMD_BOOL8X16_FUNCTION_ITEM JS_FS_END @@ -366,7 +390,7 @@ const JSFunctionSpec Bool8x16Defn::Methods[] = { const JSFunctionSpec Bool16x8Defn::Methods[] = { #define SIMD_BOOL16X8_FUNCTION_ITEM(Name, Func, Operands) \ - JS_FN(#Name, js::simd_bool16x8_##Name, Operands, 0), + JS_INLINABLE_FN(#Name, js::simd_bool16x8_##Name, Operands, 0, SimdBool16x8_##Name), BOOL16X8_FUNCTION_LIST(SIMD_BOOL16X8_FUNCTION_ITEM) #undef SIMD_BOOL16X8_FUNCTION_ITEM JS_FS_END diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 687183dadbee..eb809a8151af 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -5423,9 +5423,15 @@ GetTemplateObjectForSimd(JSContext* cx, JSFunction* target, MutableHandleObject // Check if this is a native inlinable SIMD operation. SimdType ctrlType; switch (jitInfo->inlinableNative) { + case InlinableNative::SimdInt8x16: ctrlType = SimdType::Int8x16; break; + case InlinableNative::SimdUint8x16: ctrlType = SimdType::Uint8x16; break; + case InlinableNative::SimdInt16x8: ctrlType = SimdType::Int16x8; break; + case InlinableNative::SimdUint16x8: ctrlType = SimdType::Uint16x8; break; case InlinableNative::SimdInt32x4: ctrlType = SimdType::Int32x4; break; case InlinableNative::SimdUint32x4: ctrlType = SimdType::Uint32x4; break; case InlinableNative::SimdFloat32x4: ctrlType = SimdType::Float32x4; break; + case InlinableNative::SimdBool8x16: ctrlType = SimdType::Bool8x16; break; + case InlinableNative::SimdBool16x8: ctrlType = SimdType::Bool16x8; break; case InlinableNative::SimdBool32x4: ctrlType = SimdType::Bool32x4; break; // This is not an inlinable SIMD operation. default: return false; diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 3c62633ba88e..a4a87a3deaee 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -84,8 +84,14 @@ \ _(SimdInt32x4) \ _(SimdUint32x4) \ + _(SimdInt16x8) \ + _(SimdUint16x8) \ + _(SimdInt8x16) \ + _(SimdUint8x16) \ _(SimdFloat32x4) \ _(SimdBool32x4) \ + _(SimdBool16x8) \ + _(SimdBool8x16) \ \ _(TestBailout) \ _(TestAssertFloat32) \ diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index efdc3c5da441..f71d738cc4ac 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -216,10 +216,22 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineSimd(callInfo, target, SimdType::Int32x4); case InlinableNative::SimdUint32x4: return inlineSimd(callInfo, target, SimdType::Uint32x4); + case InlinableNative::SimdInt16x8: + return inlineSimd(callInfo, target, SimdType::Int16x8); + case InlinableNative::SimdUint16x8: + return inlineSimd(callInfo, target, SimdType::Uint16x8); + case InlinableNative::SimdInt8x16: + return inlineSimd(callInfo, target, SimdType::Int8x16); + case InlinableNative::SimdUint8x16: + return inlineSimd(callInfo, target, SimdType::Uint8x16); case InlinableNative::SimdFloat32x4: return inlineSimd(callInfo, target, SimdType::Float32x4); case InlinableNative::SimdBool32x4: return inlineSimd(callInfo, target, SimdType::Bool32x4); + case InlinableNative::SimdBool16x8: + return inlineSimd(callInfo, target, SimdType::Bool16x8); + case InlinableNative::SimdBool8x16: + return inlineSimd(callInfo, target, SimdType::Bool8x16); // Testing functions. case InlinableNative::TestBailout: @@ -3328,16 +3340,18 @@ IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type) return inlineSimdStore(callInfo, native, type, 3); // Bitcasts. One for each type with a memory representation. - case SimdOperation::Fn_fromInt8x16Bits: - case SimdOperation::Fn_fromInt16x8Bits: - return InliningStatus_NotInlined; case SimdOperation::Fn_fromInt32x4Bits: return inlineSimdConvert(callInfo, native, true, SimdType::Int32x4, type); case SimdOperation::Fn_fromUint32x4Bits: return inlineSimdConvert(callInfo, native, true, SimdType::Uint32x4, type); - case SimdOperation::Fn_fromUint8x16Bits: + case SimdOperation::Fn_fromInt16x8Bits: + return inlineSimdConvert(callInfo, native, true, SimdType::Int16x8, type); case SimdOperation::Fn_fromUint16x8Bits: - return InliningStatus_NotInlined; + return inlineSimdConvert(callInfo, native, true, SimdType::Uint16x8, type); + case SimdOperation::Fn_fromInt8x16Bits: + return inlineSimdConvert(callInfo, native, true, SimdType::Int8x16, type); + case SimdOperation::Fn_fromUint8x16Bits: + return inlineSimdConvert(callInfo, native, true, SimdType::Uint8x16, type); case SimdOperation::Fn_fromFloat32x4Bits: return inlineSimdConvert(callInfo, native, true, SimdType::Float32x4, type); case SimdOperation::Fn_fromFloat64x2Bits: diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index f29224cae682..1bcc739257f0 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -1354,6 +1354,24 @@ RSimdBox::recover(JSContext* cx, SnapshotIterator& iter) const MOZ_ASSERT_IF(a.mode() == RValueAllocation::ANY_FLOAT_REG, a.fpuReg().isSimd128()); const FloatRegisters::RegisterContent* raw = iter.floatAllocationPointer(a); switch (SimdType(type_)) { + case SimdType::Bool8x16: + resultObject = js::CreateSimd(cx, (const Bool8x16::Elem*) raw); + break; + case SimdType::Int8x16: + resultObject = js::CreateSimd(cx, (const Int8x16::Elem*) raw); + break; + case SimdType::Uint8x16: + resultObject = js::CreateSimd(cx, (const Uint8x16::Elem*) raw); + break; + case SimdType::Bool16x8: + resultObject = js::CreateSimd(cx, (const Bool16x8::Elem*) raw); + break; + case SimdType::Int16x8: + resultObject = js::CreateSimd(cx, (const Int16x8::Elem*) raw); + break; + case SimdType::Uint16x8: + resultObject = js::CreateSimd(cx, (const Uint16x8::Elem*) raw); + break; case SimdType::Bool32x4: resultObject = js::CreateSimd(cx, (const Bool32x4::Elem*) raw); break; @@ -1369,24 +1387,6 @@ RSimdBox::recover(JSContext* cx, SnapshotIterator& iter) const case SimdType::Float64x2: MOZ_CRASH("NYI, RSimdBox of Float64x2"); break; - case SimdType::Int8x16: - MOZ_CRASH("NYI, RSimdBox of Int8x16"); - break; - case SimdType::Int16x8: - MOZ_CRASH("NYI, RSimdBox of Int16x8"); - break; - case SimdType::Uint8x16: - MOZ_CRASH("NYI, RSimdBox of UInt8x16"); - break; - case SimdType::Uint16x8: - MOZ_CRASH("NYI, RSimdBox of UInt16x8"); - break; - case SimdType::Bool8x16: - MOZ_CRASH("NYI, RSimdBox of Bool8x16"); - break; - case SimdType::Bool16x8: - MOZ_CRASH("NYI, RSimdBox of Bool16x8"); - break; case SimdType::Bool64x2: MOZ_CRASH("NYI, RSimdBox of Bool64x2"); break; diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 2f074b5336e1..ec5298059659 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -458,9 +458,13 @@ CodeGeneratorShared::encodeAllocation(LSnapshot* snapshot, MDefinition* mir, break; } case MIRType::Float32: - case MIRType::Bool32x4: + case MIRType::Int8x16: + case MIRType::Int16x8: case MIRType::Int32x4: case MIRType::Float32x4: + case MIRType::Bool8x16: + case MIRType::Bool16x8: + case MIRType::Bool32x4: { LAllocation* payload = snapshot->payloadOfSlot(*allocIndex); if (payload->isConstant()) { From 6f18e1035ad26f9a07c3cfa57fc8a0a8cef18d6c Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Tue, 31 May 2016 18:03:51 +0200 Subject: [PATCH 064/199] Bug 1273854 - Document the locking patterns in TextureChild that don't play well with tsan. r=bas, jseward --- gfx/layers/client/TextureClient.cpp | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index b6b9584ab352..9ac1814eba4d 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -163,6 +163,69 @@ private: Release(); } + // This lock is used order to prevent several threads to access the + // TextureClient's data concurrently. In particular, it prevents shutdown + // code to destroy a texture while another thread is reading or writing into + // it. + // In most places, the lock is held in short and bounded scopes in which we + // don't block on any other resource. There are few exceptions to this, which + // are discussed below. + // + // The locking pattern of TextureClient may in some case upset deadlock detection + // tools such as TSan. + // Typically our tile rendering code will lock all of its tiles, render into them + // and unlock them all right after that, which looks something like: + // + // Lock tile A + // Lock tile B + // Lock tile C + // Apply drawing commands to tiles A, B and C + // Unlock tile A + // Unlock tile B + // Unlock tile C + // + // And later, we may end up rendering a tile buffer that has the same tiles, + // in a different order, for example: + // + // Lock tile B + // Lock tile A + // Lock tile D + // Apply drawing commands to tiles A, B and D + // Unlock tile B + // Unlock tile A + // Unlock tile D + // + // This is because textures being expensive to create, we recycle them as much + // as possible and they may reappear in the tile buffer in a different order. + // + // Unfortunately this is not very friendly to TSan's analysis, which will see + // that B was once locked while A was locked, and then A locked while B was + // locked. TSan identifies this as a potential dead-lock which would be the + // case if this kind of inconsistent and dependent locking order was happening + // concurrently. + // In the case of TextureClient, dependent locking only ever happens on the + // thread that draws into the texture (let's call it the producer thread). Other + // threads may call into a method that can lock the texture in a short and + // bounded scope inside of which it is not allowed to do anything that could + // cause the thread to block. A given texture can only have one producer thread. + // + // Another example of TSan-unfriendly locking pattern is when copying a texture + // into another, which also never happens outside of the producer thread. + // Copying A into B looks like this: + // + // Lock texture B + // Lock texture A + // Copy A into B + // Unlock A + // Unlock B + // + // In a given frame we may need to copy A into B and in another frame copy + // B into A. For example A and B can be the Front and Back buffers, alternating + // roles and the copy is needed to avoid the cost of re-drawing the valid + // region. + // + // The important rule is that all of the dependent locking must occur only + // in the texture's producer thread to avoid deadlocks. mutable gfx::CriticalSection mLock; RefPtr mForwarder; From 15c05c673a2f672f27b294142dd97cb3b61178c3 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Tue, 31 May 2016 18:04:58 +0200 Subject: [PATCH 065/199] Bug 1273854 - Temporarily disable the TextureChild's mutex if not using ImageBridge. r=bas --- gfx/layers/client/TextureClient.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index 9ac1814eba4d..0cf6c8e07f4a 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -142,9 +142,9 @@ public: bool IPCOpen() const { return mIPCOpen; } - void Lock() const { mLock.Enter(); } + void Lock() const { if (mForwarder->UsesImageBridge()) { mLock.Enter(); } } - void Unlock() const { mLock.Leave(); } + void Unlock() const { if (mForwarder->UsesImageBridge()) { mLock.Leave(); } } private: From ada7d3fd11fbdfca9689569c85ee99c4e8729daa Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Tue, 31 May 2016 09:09:03 -0700 Subject: [PATCH 066/199] Bug 1275672 - Remove accidental trailing /D from makecab.exe arguments; r=me This was causing the "path" argument to get swallowed because makecab.exe thought it was the argument to /D. Derp. MozReview-Commit-ID: 6Bd3l7ISsPj --HG-- extra : rebase_source : fbba4e8aff9a164aa10278de08de67c3f38cdf58 --- toolkit/crashreporter/tools/symbolstore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/crashreporter/tools/symbolstore.py b/toolkit/crashreporter/tools/symbolstore.py index 5b7098192ee4..d75134ee7614 100755 --- a/toolkit/crashreporter/tools/symbolstore.py +++ b/toolkit/crashreporter/tools/symbolstore.py @@ -785,7 +785,7 @@ class Dumper_Win32(Dumper): compressed_file = path[:-1] + '_' # ignore makecab's output success = subprocess.call(["makecab.exe", "/D", - "CompressionType=MSZIP", "/D", + "CompressionType=MSZIP", path, compressed_file], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) From b91ac5f5d1e8e3165134baada333a3912e37b700 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Tue, 31 May 2016 18:56:20 +0300 Subject: [PATCH 067/199] Bug 1276938 - Optimize string usage in setAttribute when dealing with HTML elements, r=baku --HG-- extra : rebase_source : dea4a4d25ff3cc7b2ca0ced76cafae8577d0f650 --- dom/base/Element.cpp | 40 ++++++++++++++++++++----------- dom/base/Element.h | 10 +++++--- dom/html/nsGenericHTMLElement.cpp | 12 ---------- dom/html/nsGenericHTMLElement.h | 2 -- 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 46fd4de85358..f33b3ad6e153 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1181,17 +1181,11 @@ Element::SetAttribute(const nsAString& aName, if (aError.Failed()) { return; } - const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); + + nsAutoString nameToUse; + const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse); if (!name) { - nsCOMPtr nameAtom; - if (IsHTMLElement() && IsInHTMLDocument()) { - nsAutoString lower; - nsContentUtils::ASCIIToLower(aName, lower); - nameAtom = NS_Atomize(lower); - } - else { - nameAtom = NS_Atomize(aName); - } + nsCOMPtr nameAtom = NS_Atomize(nameToUse); if (!nameAtom) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); return; @@ -1208,7 +1202,7 @@ Element::SetAttribute(const nsAString& aName, void Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError) { - const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); + const nsAttrName* name = InternalGetAttrNameFromQName(aName); if (!name) { // If there is no canonical nsAttrName for this attribute name, then the @@ -1991,7 +1985,7 @@ Element::FindAttributeDependence(const nsIAtom* aAttribute, already_AddRefed Element::GetExistingAttrNameFromQName(const nsAString& aStr) const { - const nsAttrName* name = InternalGetExistingAttrNameFromQName(aStr); + const nsAttrName* name = InternalGetAttrNameFromQName(aStr); if (!name) { return nullptr; } @@ -2167,9 +2161,27 @@ Element::SetEventHandler(nsIAtom* aEventName, //---------------------------------------------------------------------- const nsAttrName* -Element::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const +Element::InternalGetAttrNameFromQName(const nsAString& aStr, + nsAutoString* aNameToUse) const { - return mAttrsAndChildren.GetExistingAttrNameFromQName(aStr); + MOZ_ASSERT(!aNameToUse || aNameToUse->IsEmpty()); + const nsAttrName* val = nullptr; + if (IsHTMLElement() && IsInHTMLDocument()) { + nsAutoString lower; + nsAutoString& outStr = aNameToUse ? *aNameToUse : lower; + nsContentUtils::ASCIIToLower(aStr, outStr); + val = mAttrsAndChildren.GetExistingAttrNameFromQName(outStr); + if (val) { + outStr.Truncate(); + } + } else { + val = mAttrsAndChildren.GetExistingAttrNameFromQName(aStr); + if (!val && aNameToUse) { + *aNameToUse = aStr; + } + } + + return val; } bool diff --git a/dom/base/Element.h b/dom/base/Element.h index 4839bb9da3da..df474f64c996 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -660,7 +660,7 @@ public: ErrorResult& aError); bool HasAttribute(const nsAString& aName) const { - return InternalGetExistingAttrNameFromQName(aName) != nullptr; + return InternalGetAttrNameFromQName(aName) != nullptr; } bool HasAttributeNS(const nsAString& aNamespaceURI, const nsAString& aLocalName) const; @@ -1273,9 +1273,13 @@ protected: GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer); /** - * Internal hook for converting an attribute name-string to an atomized name + * Internal hook for converting an attribute name-string to nsAttrName in + * case there is such existing attribute. aNameToUse can be passed to get + * name which was used for looking for the attribute (lowercase in HTML). */ - virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const; + const nsAttrName* + InternalGetAttrNameFromQName(const nsAString& aStr, + nsAutoString* aNameToUse = nullptr) const; nsIFrame* GetStyledFrame(); diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 96a90609904d..f8f6d7948e4f 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -2802,18 +2802,6 @@ nsGenericHTMLElement::DispatchSimulatedClick(nsGenericHTMLElement* aElement, return EventDispatcher::Dispatch(ToSupports(aElement), aPresContext, &event); } -const nsAttrName* -nsGenericHTMLElement::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const -{ - if (IsInHTMLDocument()) { - nsAutoString lower; - nsContentUtils::ASCIIToLower(aStr, lower); - return mAttrsAndChildren.GetExistingAttrNameFromQName(lower); - } - - return mAttrsAndChildren.GetExistingAttrNameFromQName(aStr); -} - nsresult nsGenericHTMLElement::GetEditor(nsIEditor** aEditor) { diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 3a8fbb4e51ee..169c1304c307 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -937,8 +937,6 @@ protected: GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer) override; - virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const override; - /** * Dispatch a simulated mouse click by keyboard to the given element. */ From 7b3b073eecc16b40d66893f7feac262147248252 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 29 Sep 2015 13:39:38 -0700 Subject: [PATCH 068/199] Bug 1259850 - Switch to using a Map for visited points within a function, and heavily comment, r=terrence MozReview-Commit-ID: LvU4upaHt8b --HG-- extra : rebase_source : 552e4141e8d2cbba4846d98a055fea201077b735 --- js/src/devtools/rootAnalysis/analyzeRoots.js | 74 +++++++++++++------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index 09b4de127a62..d757c2319640 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -331,31 +331,57 @@ function edgeCanGC(edge) // // - 'gcInfo': a direct pointer to the GC call edge // -function findGCBeforeVariableUse(suppressed, variable, worklist) +function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) { // Scan through all edges preceding an unrooted variable use, using an // explicit worklist, looking for a GC call. A worklist contains an // incoming edge together with a description of where it or one of its // successors GC'd (if any). + var bodies_visited = new Map(); + + let worklist = [{body: start_body, ppoint: start_point, gcInfo: null, why: null}]; while (worklist.length) { + // Grab an entry off of the worklist, representing a point within the + // CFG identified by . If this point has a descendant + // later in the CFG that can GC, gcInfo will be set to the information + // about that GC call. + var entry = worklist.pop(); var { body, ppoint, gcInfo } = entry; - if (body.seen) { - if (ppoint in body.seen) { - var seenEntry = body.seen[ppoint]; - if (!gcInfo || seenEntry.gcInfo) - continue; - } - } else { - body.seen = []; - } - body.seen[ppoint] = {body: body, gcInfo: gcInfo}; + // Handle the case where there are multiple ways to reach this point + // (traversing backwards). + var visited = bodies_visited.get(body); + if (!visited) + bodies_visited.set(body, visited = new Map()); + if (visited.has(ppoint)) { + var seenEntry = visited.get(ppoint); + // This point already knows how to GC through some other path, so + // we have nothing new to learn. (The other path will consider the + // predecessors.) + if (seenEntry.gcInfo) + continue; + + // If this worklist's entry doesn't know of any way to GC, then + // there's no point in continuing the traversal through it. Perhaps + // another edge will be found that *can* GC; otherwise, the first + // route to the point will traverse through predecessors. + // + // Note that this means we may visit a point more than once, if the + // first time we visit we don't have a known reachable GC call and + // the second time we do. + if (!gcInfo) + continue; + } + visited.set(ppoint, {body: body, gcInfo: gcInfo}); + + // Check for hitting the entry point of the current body (which may be + // the outer function or a loop within it.) if (ppoint == body.Index[0]) { if (body.BlockId.Kind == "Loop") { - // propagate to parents that enter the loop body. + // Propagate to outer body parents that enter the loop body. if ("BlockPPoint" in body) { for (var parent of body.BlockPPoint) { var found = false; @@ -399,11 +425,10 @@ function findGCBeforeVariableUse(suppressed, variable, worklist) // to a use after the GC call that proves its live range // extends at least that far. if (gcInfo) - return {gcInfo: gcInfo, why: {body: body, ppoint: source, gcInfo: gcInfo, why: entry } } + return {gcInfo: gcInfo, why: {body: body, ppoint: source, gcInfo: gcInfo, why: entry } }; - // Otherwise, we want to continue searching for the true - // minimumUse, for use in reporting unnecessary rooting, but we - // truncate this particular branch of the search at this edge. + // Otherwise, keep searching through the graph, but truncate + // this particular branch of the search at this edge. continue; } @@ -415,7 +440,10 @@ function findGCBeforeVariableUse(suppressed, variable, worklist) if (edge_uses) { // The live range starts at least this far back, so we're done - // for the same reason as with edge_kills. + // for the same reason as with edge_kills. The only difference + // is that a GC on this edge indicates a hazard, whereas if + // we're killing a live range in the GC call then it's not live + // *across* the call. if (gcInfo) return {gcInfo:gcInfo, why:entry}; } @@ -446,13 +474,12 @@ function findGCBeforeVariableUse(suppressed, variable, worklist) function variableLiveAcrossGC(suppressed, variable) { - // A variable is live across a GC if (1) it is used by an edge, and (2) it - // is used after a GC in a successor edge. + // A variable is live across a GC if (1) it is used by an edge (as in, it + // was at least initialized), and (2) it is used after a GC in a successor + // edge. - for (var body of functionBodies) { - body.seen = null; + for (var body of functionBodies) body.minimumUse = 0; - } for (var body of functionBodies) { if (!("PEdge" in body)) @@ -467,8 +494,7 @@ function variableLiveAcrossGC(suppressed, variable) // if (usePoint && !edgeKillsVariable(edge, variable)) { // Found a use, possibly after a GC. - var worklist = [{body:body, ppoint:usePoint, gcInfo:null, why:null}]; - var call = findGCBeforeVariableUse(suppressed, variable, worklist); + var call = findGCBeforeVariableUse(body, usePoint, suppressed, variable); if (!call) continue; From 7d7a213b56e5aa3d8b315ff07d70bbd041f41490 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 29 Sep 2015 13:39:38 -0700 Subject: [PATCH 069/199] Bug 1259850 - Rename function, add a few comments, r=terrence MozReview-Commit-ID: Fyk1zzbWGnh --HG-- extra : rebase_source : ff201f130de9b05ac0d79ed08502e7a95b803961 --- js/src/devtools/rootAnalysis/analyzeRoots.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index d757c2319640..82132789d000 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -527,7 +527,9 @@ function unsafeVariableAddressTaken(suppressed, variable) return null; } -function computePrintedLines(functionName) +// Read out the brief (non-JSON, semi-human-readable) CFG description for the +// given function and store it. +function loadPrintedLines(functionName) { assert(!os.system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile)); var lines = snarf(tmpfile).split('\n'); @@ -583,7 +585,7 @@ function printEntryTrace(functionName, entry) var gcPoint = entry.gcInfo ? entry.gcInfo.ppoint : 0; if (!functionBodies[0].lines) - computePrintedLines(functionName); + loadPrintedLines(functionName); while (entry) { var ppoint = entry.ppoint; @@ -598,8 +600,8 @@ function printEntryTrace(functionName, entry) var table = {}; entry.body.edgeTable = table; for (var line of entry.body.lines) { - if (match = /\((\d+),(\d+),/.exec(line)) - table[match[1] + "," + match[2]] = line; // May be multiple? + if (match = /\((\d+,\d+),/.exec(line)) + table[match[1]] = line; // May be multiple? } } From 43e12cc6bc668cef43f29206d18037cd7fd0d5f5 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 15:12:31 -0700 Subject: [PATCH 070/199] Bug 1259850 - The explanations of why something is a hazard went up to just before the initial use of a variable. Extend them one further, r=terrence MozReview-Commit-ID: 9l8ftRv3yjS --HG-- extra : rebase_source : a8cd8faeb91a66d2818c7212c435847996832796 --- js/src/devtools/rootAnalysis/analyzeRoots.js | 74 +++++++++++++++----- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index 82132789d000..ff03912fabaf 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -340,7 +340,7 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) var bodies_visited = new Map(); - let worklist = [{body: start_body, ppoint: start_point, gcInfo: null, why: null}]; + let worklist = [{body: start_body, ppoint: start_point, preGCLive: false, gcInfo: null, why: null}]; while (worklist.length) { // Grab an entry off of the worklist, representing a point within the // CFG identified by . If this point has a descendant @@ -348,7 +348,7 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) // about that GC call. var entry = worklist.pop(); - var { body, ppoint, gcInfo } = entry; + var { body, ppoint, gcInfo, preGCLive } = entry; // Handle the case where there are multiple ways to reach this point // (traversing backwards). @@ -399,7 +399,13 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) } else if (variable.Kind == "Arg" && gcInfo) { // The scope of arguments starts at the beginning of the // function - return {gcInfo: gcInfo, why: entry}; + return entry; + } else if (entry.preGCLive) { + // We didn't find a "good" explanation beginning of the live + // range, but we do know the variable was live across the GC. + // This can happen if the live range started when a variable is + // used as a retparam. + return entry; } } @@ -425,17 +431,19 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) // to a use after the GC call that proves its live range // extends at least that far. if (gcInfo) - return {gcInfo: gcInfo, why: {body: body, ppoint: source, gcInfo: gcInfo, why: entry } }; + return {body: body, ppoint: source, gcInfo: gcInfo, why: entry }; // Otherwise, keep searching through the graph, but truncate // this particular branch of the search at this edge. continue; } + var src_gcInfo = gcInfo; + var src_preGCLive = preGCLive; if (!gcInfo && !(source in body.suppressed) && !suppressed) { var gcName = edgeCanGC(edge, body); if (gcName) - gcInfo = {name:gcName, body:body, ppoint:source}; + src_gcInfo = {name:gcName, body:body, ppoint:source}; } if (edge_uses) { @@ -444,8 +452,31 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) // is that a GC on this edge indicates a hazard, whereas if // we're killing a live range in the GC call then it's not live // *across* the call. - if (gcInfo) - return {gcInfo:gcInfo, why:entry}; + // + // However, we may want to generate a longer usage chain for + // the variable than is minimally necessary. For example, + // consider: + // + // Value v = f(); + // if (v.isUndefined()) + // return false; + // gc(); + // return v; + // + // The call to .isUndefined() is considered to be a use and + // therefore indicates that v must be live at that point. But + // it's more helpful to the user to continue the 'why' path to + // include the ancestor where the value was generated. So we + // will only return here if edge.Kind is Assign; otherwise, + // we'll pass a "preGCLive" value up through the worklist to + // remember that the variable *is* alive before the GC and so + // this function should be returning a true value. + + if (src_gcInfo) { + src_preGCLive = true; + if (edge.Kind == 'Assign') + return {body:body, ppoint:source, gcInfo:src_gcInfo, why:entry}; + } } if (edge.Kind == "Loop") { @@ -457,7 +488,8 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) assert(!found); found = true; worklist.push({body:xbody, ppoint:xbody.Index[1], - gcInfo:gcInfo, why:entry}); + preGCLive: src_preGCLive, gcInfo:src_gcInfo, + why:entry}); } } assert(found); @@ -465,7 +497,9 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) } // Propagate the search to the predecessors of this edge. - worklist.push({body:body, ppoint:source, gcInfo:gcInfo, why:entry}); + worklist.push({body:body, ppoint:source, + preGCLive: src_preGCLive, gcInfo:src_gcInfo, + why:entry}); } } @@ -564,13 +598,21 @@ function loadPrintedLines(functionName) } } -function findLocation(body, ppoint) +function findLocation(body, ppoint, opts={brief: false}) { var location = body.PPoint[ppoint - 1].Location; - var text = location.CacheString + ":" + location.Line; - if (text.indexOf(sourceRoot) == 0) - return text.substring(sourceRoot.length); - return text; + var file = location.CacheString; + + if (file.indexOf(sourceRoot) == 0) + file = file.substring(sourceRoot.length); + + if (opts.brief) { + var m = /.*\/(.*)/.exec(file); + if (m) + file = m[1]; + } + + return file + ":" + location.Line; } function locationLine(text) @@ -589,7 +631,7 @@ function printEntryTrace(functionName, entry) while (entry) { var ppoint = entry.ppoint; - var lineText = findLocation(entry.body, ppoint); + var lineText = findLocation(entry.body, ppoint, {"brief": true}); var edgeText = ""; if (entry.why && entry.why.body == entry.body) { @@ -687,7 +729,7 @@ function processBodies(functionName) " of type '" + typeDesc(variable.Type) + "'" + " live across GC call " + result.gcInfo.name + " at " + lineText); - printEntryTrace(functionName, result.why); + printEntryTrace(functionName, result); } result = unsafeVariableAddressTaken(suppressed, variable.Variable); if (result) { From 1b4f2c009a5af5a6d1cec000e167359a454e973e Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 15:18:57 -0700 Subject: [PATCH 071/199] Bug 1259850 - Convert memoizer to Map, r=terrence MozReview-Commit-ID: 2Ps8gJpztw2 --HG-- extra : rebase_source : 17832fcd628859f8fd62f6b254878633435cdfe9 --- js/src/devtools/rootAnalysis/computeCallgraph.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index 5aeae6af843d..d7962b669e8e 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -104,17 +104,17 @@ function findVirtualFunctions(initialCSU, field, suppressed) return functions; } -var memoized = {}; +var memoized = new Map(); var memoizedCount = 0; function memo(name) { - if (!(name in memoized)) { - memoizedCount++; - memoized[name] = "" + memoizedCount; - print("#" + memoizedCount + " " + name); + if (!memoized.has(name)) { + let id = memoized.size + 1; + memoized.set(name, "" + id); + print(`#${id} ${name}`); } - return memoized[name]; + return memoized.get(name); } var seenCallees = null; From 77be2fa2f9e668a1f3c26b0dd8a06789f7def1fd Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 15:24:57 -0700 Subject: [PATCH 072/199] Bug 1259850 - Refactoring: more Set/Map usage, r=terrence MozReview-Commit-ID: 2Ps8gJpztw2 --HG-- extra : rebase_source : 358eb3bc7ef626177b744301659795c0f1d73d8b --- js/src/devtools/rootAnalysis/annotations.js | 6 + .../devtools/rootAnalysis/computeCallgraph.js | 104 ++++++++---------- js/src/devtools/rootAnalysis/utility.js | 9 ++ 3 files changed, 59 insertions(+), 60 deletions(-) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index b9072dbfb8f2..fd4dac78e09e 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -146,6 +146,12 @@ function ignoreEdgeAddressTaken(edge) return false; } +// Return whether csu.method is one that we claim can never GC. +function isSuppressedVirtualMethod(csu, method) +{ + return csu == "nsISupports" && (method == "AddRef" || method == "Release"); +} + // Ignore calls of these functions (so ignore any stack containing these) var ignoreFunctions = { "ptio.c:pt_MapError" : true, diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index d7962b669e8e..02ede9af9e29 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -12,25 +12,17 @@ if (scriptArgs[0] == '--function') { scriptArgs = scriptArgs.slice(2); } -var subclasses = {}; -var superclasses = {}; -var classFunctions = {}; +var subclasses = new Map(); // Map from csu => set of immediate subclasses +var superclasses = new Map(); // Map from csu => set of immediate superclasses +var classFunctions = new Map(); // Map from "csu:name" => set of full method name -var fieldCallSeen = {}; +var virtualResolutionsSeen = new Set(); -function addClassEntry(index, name, other) +function addEntry(map, name, entry) { - if (!(name in index)) { - index[name] = [other]; - return; - } - - for (var entry of index[name]) { - if (entry == other) - return; - } - - index[name].push(other); + if (!map.has(name)) + map.set(name, new Set()); + map.get(name).add(entry); } // CSU is "Class/Struct/Union" @@ -43,16 +35,14 @@ function processCSU(csuName, csu) var superclass = field.Field[1].Type.Name; var subclass = field.Field[1].FieldCSU.Type.Name; assert(subclass == csuName); - addClassEntry(subclasses, superclass, subclass); - addClassEntry(superclasses, subclass, superclass); + addEntry(subclasses, superclass, subclass); + addEntry(superclasses, subclass, superclass); } if ("Variable" in field) { // Note: not dealing with overloading correctly. var name = field.Variable.Name[0]; var key = csuName + ":" + field.Field[0].Name[0]; - if (!(key in classFunctions)) - classFunctions[key] = []; - classFunctions[key].push(name); + addEntry(classFunctions, key, name); } } } @@ -60,7 +50,7 @@ function processCSU(csuName, csu) function findVirtualFunctions(initialCSU, field, suppressed) { var worklist = [initialCSU]; - var functions = []; + var functions = new Set(); // Virtual call targets on subclasses of nsISupports may be incomplete, // if the interface is scriptable. Just treat all indirect calls on @@ -68,21 +58,19 @@ function findVirtualFunctions(initialCSU, field, suppressed) // which should never enter the JS engine (even when calling dtors). while (worklist.length) { var csu = worklist.pop(); - if (csu == "nsISupports" && (field == "AddRef" || field == "Release")) { + if (isSuppressedVirtualMethod(csu, field)) { suppressed[0] = true; - return []; + return new Set(); } if (isOverridableField(initialCSU, csu, field)) { // We will still resolve the virtual function call, because it's // nice to have as complete a callgraph as possible for other uses. // But push a token saying that we can run arbitrary code. - functions.push(null); + functions.add(null); } - if (csu in superclasses) { - for (var superclass of superclasses[csu]) - worklist.push(superclass); - } + if (superclasses.has(csu)) + worklist.push(...superclasses.get(csu)); } worklist = [csu]; @@ -90,15 +78,11 @@ function findVirtualFunctions(initialCSU, field, suppressed) var csu = worklist.pop(); var key = csu + ":" + field; - if (key in classFunctions) { - for (var name of classFunctions[key]) - functions.push(name); - } + if (classFunctions.has(key)) + functions.update(classFunctions.get(key)); - if (csu in subclasses) { - for (var subclass of subclasses[csu]) - worklist.push(subclass); - } + if (subclasses.has(csu)) + worklist.push(...subclasses.get(csu)); } return functions; @@ -117,12 +101,11 @@ function memo(name) return memoized.get(name); } -var seenCallees = null; -var seenSuppressedCallees = null; - // Return a list of all callees that the given edge might be a call to. Each // one is represented by an object with a 'kind' field that is one of -// ('direct', 'field', 'indirect', 'unknown'). +// ('direct', 'field', 'resolved-field', 'indirect', 'unknown'), though note +// that 'resolved-field' is really a global record of virtual method +// resolutions, indepedent of this particular edge. function getCallees(edge) { if (edge.Kind != "Call") @@ -236,39 +219,45 @@ function processBody(functionName, body) for (var tag of getTags(functionName, body).values()) print("T " + memo(functionName) + " " + tag); + // Set of all callees that have been output so far, in order to suppress + // repeated callgraph edges from being recorded. Use a separate set for + // suppressed callees, since we don't want a suppressed edge (within one + // RAII scope) to prevent an unsuppressed edge from being recorded. The + // seen array is indexed by a boolean 'suppressed' variable. + var seen = [ new Set(), new Set() ]; + lastline = null; for (var edge of body.PEdge) { if (edge.Kind != "Call") continue; - var edgeSuppressed = false; - var seen = seenCallees; - if (edge.Index[0] in body.suppressed) { - edgeSuppressed = true; - seen = seenSuppressedCallees; - } + + // Whether this call is within the RAII scope of a GC suppression class + var edgeSuppressed = (edge.Index[0] in body.suppressed); + for (var callee of getCallees(edge)) { - var prologue = (edgeSuppressed || callee.suppressed) ? "SUPPRESS_GC " : ""; + var suppressed = Boolean(edgeSuppressed || callee.suppressed); + var prologue = suppressed ? "SUPPRESS_GC " : ""; prologue += memo(functionName) + " "; if (callee.kind == 'direct') { - if (!(callee.name in seen)) { - seen[callee.name] = true; + if (!seen[+suppressed].has(callee.name)) { + seen[+suppressed].add(callee.name); printOnce("D " + prologue + memo(callee.name)); } } else if (callee.kind == 'field') { var { csu, field } = callee; printOnce("F " + prologue + "CLASS " + csu + " FIELD " + field); } else if (callee.kind == 'resolved-field') { - // Fully-resolved field call (usually a virtual method). Record - // the callgraph edges. Do not consider suppression, since it - // is local to this callsite and we are writing out a global + // Fully-resolved field (virtual method) call. Record the + // callgraph edges. Do not consider suppression, since it is + // local to this callsite and we are writing out a global // record here. // // Any field call that does *not* have an R entry must be // assumed to call anything. var { csu, field, callees } = callee; var fullFieldName = csu + "." + field; - if (!(fullFieldName in fieldCallSeen)) { - fieldCallSeen[fullFieldName] = true; + if (!virtualResolutionsSeen.has(fullFieldName)) { + virtualResolutionsSeen.add(fullFieldName); for (var target of callees) printOnce("R " + memo(fullFieldName) + " " + memo(target.name)); } @@ -284,8 +273,6 @@ function processBody(functionName, body) } } -var callgraph = {}; - var xdb = xdbLibrary(); xdb.open("src_comp.xdb"); @@ -327,9 +314,6 @@ function process(functionName, functionBodies) pbody.suppressed[id] = true; } - seenCallees = {}; - seenSuppressedCallees = {}; - for (var body of functionBodies) processBody(functionName, body); diff --git a/js/src/devtools/rootAnalysis/utility.js b/js/src/devtools/rootAnalysis/utility.js index 7303c7b81fd9..e116a07cd8e3 100644 --- a/js/src/devtools/rootAnalysis/utility.js +++ b/js/src/devtools/rootAnalysis/utility.js @@ -6,6 +6,15 @@ // constructors/destructors. var internalMarker = " *INTERNAL* "; +if (! Set.prototype.hasOwnProperty("update")) { + Object.defineProperty(Set.prototype, "update", { + value: function (collection) { + for (let elt of collection) + this.add(elt); + } + }); +} + function assert(x, msg) { if (x) From 8afd1f9d6baeffbe8d6ac12fa87168bbd22137d4 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 15:25:46 -0700 Subject: [PATCH 073/199] Bug 1259850 - Start searching from the most specialized csu, r=terrence Previously, this mostly "worked" purely by chance -- it started gathering method definitions at the 'csu' variable, which JS helpfully hoisted up to the toplevel. It had the last value assigned to csu within the loop, which would have been the basest base class (don't think too hard about the case of multiple inheritance, it was wrong). Then we find all descendants, which was *too* much, but it ended up just making the analysis conservative. MozReview-Commit-ID: 2Ps8gJpztw2 --HG-- extra : rebase_source : b52ccf514e70fd00e5e73cdef59df379efb32487 --- .../devtools/rootAnalysis/computeCallgraph.js | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index 02ede9af9e29..c9fe0c9cd65b 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -47,6 +47,24 @@ function processCSU(csuName, csu) } } +// Return the nearest ancestor method definition, or all nearest definitions in +// the case of multiple inheritance. +function nearestAncestorMethods(csu, method) +{ + var key = csu + ":" + method; + + if (classFunctions.has(key)) + return new Set(classFunctions.get(key)); + + var functions = new Set(); + if (superclasses.has(csu)) { + for (var parent of superclasses.get(csu)) + functions.update(nearestAncestorMethods(parent, method)); + } + + return functions; +} + function findVirtualFunctions(initialCSU, field, suppressed) { var worklist = [initialCSU]; @@ -73,7 +91,15 @@ function findVirtualFunctions(initialCSU, field, suppressed) worklist.push(...superclasses.get(csu)); } - worklist = [csu]; + // Now return a list of all the instantiations of the method named 'field' + // that could execute on an instance of initialCSU or a descendant class. + + // Start with the class itself, or if it doesn't define the method, all + // nearest ancestor definitions. + functions.update(nearestAncestorMethods(initialCSU, field)); + + // Then recurse through all descendants to add in their definitions. + var worklist = [initialCSU]; while (worklist.length) { var csu = worklist.pop(); var key = csu + ":" + field; From 8100e1379d6ffb9427e68ee7ab0051dc180b087b Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 15:25:56 -0700 Subject: [PATCH 074/199] Bug 1259850 - Refactor findVirtualFunctions and improve comments, r=terrence No more passing a 2nd return value back through a mutable 1-element array. MozReview-Commit-ID: IUcyrq93KXT --HG-- extra : rebase_source : 8485a1816bd888aada033e10dff062ec32d4d4f1 --- .../devtools/rootAnalysis/computeCallgraph.js | 85 ++++++++++--------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index c9fe0c9cd65b..e72c67313c85 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -65,21 +65,29 @@ function nearestAncestorMethods(csu, method) return functions; } -function findVirtualFunctions(initialCSU, field, suppressed) +// Return [ instantations, suppressed ], where instantiations is a Set of all +// possible implementations of 'field' given static type 'initialCSU', plus +// null if arbitrary other implementations are possible, and suppressed is true +// if we the method is assumed to be non-GC'ing by annotation. +function findVirtualFunctions(initialCSU, field) { var worklist = [initialCSU]; var functions = new Set(); - // Virtual call targets on subclasses of nsISupports may be incomplete, - // if the interface is scriptable. Just treat all indirect calls on - // nsISupports objects as potentially GC'ing, except AddRef/Release - // which should never enter the JS engine (even when calling dtors). + // Loop through all methods of initialCSU (by looking at all methods of ancestor csus). + // + // If field is nsISupports::AddRef or ::Release, return an empty list and a + // boolean that says we assert that it cannot GC. + // + // If this is a method that is annotated to be dangerous (eg, it could be + // overridden with an implementation that could GC), then use null as a + // signal value that it should be considered to GC, even though we'll also + // collect all of the instantiations for other purposes. + while (worklist.length) { var csu = worklist.pop(); - if (isSuppressedVirtualMethod(csu, field)) { - suppressed[0] = true; - return new Set(); - } + if (isSuppressedVirtualMethod(csu, field)) + return [ new Set(), true ]; if (isOverridableField(initialCSU, csu, field)) { // We will still resolve the virtual function call, because it's // nice to have as complete a callgraph as possible for other uses. @@ -111,7 +119,7 @@ function findVirtualFunctions(initialCSU, field, suppressed) worklist.push(...subclasses.get(csu)); } - return functions; + return [ functions, false ]; } var memoized = new Map(); @@ -148,42 +156,42 @@ function getCallees(edge) var field = callee.Exp[0].Field; var fieldName = field.Name[0]; var csuName = field.FieldCSU.Type.Name; - var functions = null; + var functions; if ("FieldInstanceFunction" in field) { - var suppressed = [ false ]; - functions = findVirtualFunctions(csuName, fieldName, suppressed); - if (suppressed[0]) { + let suppressed; + [ functions, suppressed ] = findVirtualFunctions(csuName, fieldName, suppressed); + if (suppressed) { // Field call known to not GC; mark it as suppressed so // direct invocations will be ignored callees.push({'kind': "field", 'csu': csuName, 'field': fieldName, 'suppressed': true}); } - } - if (functions) { - // Known set of virtual call targets. Treat them as direct - // calls to all possible resolved types, but also record edges - // from this field call to each final callee. When the analysis - // is checking whether an edge can GC and it sees an unrooted - // pointer held live across this field call, it will know - // whether any of the direct callees can GC or not. - var targets = []; - var fullyResolved = true; - for (var name of functions) { - if (name === null) { - // virtual call on an nsISupports object - callees.push({'kind': "field", 'csu': csuName, 'field': fieldName}); - fullyResolved = false; - } else { - callees.push({'kind': "direct", 'name': name}); - targets.push({'kind': "direct", 'name': name}); - } - } - if (fullyResolved) - callees.push({'kind': "resolved-field", 'csu': csuName, 'field': fieldName, 'callees': targets}); } else { - // Unknown set of call targets. Non-virtual field call. - callees.push({'kind': "field", 'csu': csuName, 'field': fieldName}); + functions = new Set([null]); // field call } + + // Known set of virtual call targets. Treat them as direct calls to + // all possible resolved types, but also record edges from this + // field call to each final callee. When the analysis is checking + // whether an edge can GC and it sees an unrooted pointer held live + // across this field call, it will know whether any of the direct + // callees can GC or not. + var targets = []; + var fullyResolved = true; + for (var name of functions) { + if (name === null) { + // Unknown set of call targets, meaning either a function + // pointer call ("field call") or a virtual method that can + // be overridden in extensions. + callees.push({'kind': "field", 'csu': csuName, 'field': fieldName}); + fullyResolved = false; + } else { + callees.push({'kind': "direct", 'name': name}); + targets.push({'kind': "direct", 'name': name}); + } + } + if (fullyResolved) + callees.push({'kind': "resolved-field", 'csu': csuName, 'field': fieldName, 'callees': targets}); } else if (callee.Exp[0].Kind == "Var") { // indirect call through a variable. callees.push({'kind': "indirect", 'variable': callee.Exp[0].Variable.Name[0]}); @@ -228,7 +236,6 @@ function getTags(functionName, body) { var tags = new Set(); var annotations = getAnnotations(body); if (functionName in annotations) { - print("crawling through"); for (var [ annName, annValue ] of annotations[functionName]) { if (annName == 'Tag') tags.add(annValue); From 044fdd220431d51ea6bdcf183d92052a48772538 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 11:07:04 -0700 Subject: [PATCH 075/199] Bug 1259850 - Hide command output behind --verbose flag, r=terrence MozReview-Commit-ID: ERQmFqLoyGw --HG-- extra : rebase_source : 8e724d7eb6bc683c6600d330febbb035fe298d98 --- js/src/devtools/rootAnalysis/analyze.py | 34 +++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/js/src/devtools/rootAnalysis/analyze.py b/js/src/devtools/rootAnalysis/analyze.py index 3f2c34a44003..dca1461fc70d 100755 --- a/js/src/devtools/rootAnalysis/analyze.py +++ b/js/src/devtools/rootAnalysis/analyze.py @@ -77,7 +77,8 @@ def generate_hazards(config, outfilename): config) outfile = 'rootingHazards.%s' % (i+1,) output = open(outfile, 'w') - print_command(command, outfile=outfile, env=env(config)) + if config['verbose']: + print_command(command, outfile=outfile, env=env(config)) jobs.append((command, Popen(command, stdout=output, env=env(config)))) final_status = 0 @@ -91,7 +92,8 @@ def generate_hazards(config, outfilename): with open(outfilename, 'w') as output: command = ['cat'] + [ 'rootingHazards.%s' % (i+1,) for i in range(int(config['jobs'])) ] - print_command(command, outfile=outfilename) + if config['verbose']: + print_command(command, outfile=outfilename) subprocess.call(command, stdout=output) JOBS = { 'dbs': @@ -155,7 +157,8 @@ def run_job(name, config): if isinstance(outfiles, basestring): stdout_filename = '%s.tmp' % name temp_map[stdout_filename] = outfiles - print_command(cmdspec, outfile=outfiles, env=env(config)) + if config['verbose']: + print_command(cmdspec, outfile=outfiles, env=env(config)) else: stdout_filename = None pc = list(cmdspec) @@ -163,7 +166,8 @@ def run_job(name, config): for (i, name) in out_indexes(cmdspec): pc[i] = outfiles[outfile] outfile += 1 - print_command(pc, env=env(config)) + if config['verbose']: + print_command(pc, env=env(config)) command = list(cmdspec) outfile = 0 @@ -190,15 +194,6 @@ config = { 'ANALYSIS_SCRIPTDIR': os.path.dirname(__file__) } defaults = [ '%s/defaults.py' % config['ANALYSIS_SCRIPTDIR'], '%s/defaults.py' % os.getcwd() ] -for default in defaults: - try: - execfile(default, config) - print("Loaded %s" % default) - except: - pass - -data = config.copy() - parser = argparse.ArgumentParser(description='Statically analyze build tree for rooting hazards.') parser.add_argument('step', metavar='STEP', type=str, nargs='?', help='run starting from this step') @@ -220,8 +215,21 @@ parser.add_argument('--tag', '-t', type=str, nargs='?', help='name of job, also sets build command to "build."') parser.add_argument('--expect-file', type=str, nargs='?', help='deprecated option, temporarily still present for backwards compatibility') +parser.add_argument('--verbose', '-v', action='store_true', + help='Display cut & paste commands to run individual steps') args = parser.parse_args() + +for default in defaults: + try: + execfile(default, config) + if args.verbose: + print("Loaded %s" % default) + except: + pass + +data = config.copy() + for k,v in vars(args).items(): if v is not None: data[k] = v From 7e06b7d972a1c372f3b166ea9939149ff50a19e9 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 10:56:32 -0700 Subject: [PATCH 076/199] Bug 1259850 - Fix chunking implementation, r=terrence MozReview-Commit-ID: F58OBZ4tCXw --HG-- extra : rebase_source : 0e94d189b21cf6071d9d4c0b81d22fffb5a41db2 --- js/src/devtools/rootAnalysis/analyzeRoots.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index ff03912fabaf..a95a208fb73e 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -753,9 +753,9 @@ var minStream = xdb.min_data_stream()|0; var maxStream = xdb.max_data_stream()|0; var N = (maxStream - minStream) + 1; -var each = Math.floor(N/numBatches); -var start = minStream + each * (batch - 1); -var end = Math.min(minStream + each * batch - 1, maxStream); +var start = Math.floor((batch - 1) / numBatches * N) + minStream; +var start_next = Math.floor(batch / numBatches * N) + minStream; +var end = start_next - 1; function process(name, json) { functionName = name; From 7ef77cb0e1e408249878c9b0c4989564e11bba47 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 19 May 2016 12:53:29 -0700 Subject: [PATCH 077/199] Bug 1259850 - In-source annotations for GC suppression, r=terrence MozReview-Commit-ID: HaSt3RVV6CM --HG-- extra : rebase_source : 9208edf58765abab960fa7188070704ee5bcbf56 --- js/public/GCAPI.h | 2 +- js/public/GCAnnotations.h | 3 ++ js/src/devtools/rootAnalysis/analyze.py | 10 +++-- js/src/devtools/rootAnalysis/analyzeRoots.js | 19 ++++---- js/src/devtools/rootAnalysis/annotations.js | 13 +++--- .../devtools/rootAnalysis/computeCallgraph.js | 4 ++ .../devtools/rootAnalysis/computeGCTypes.js | 43 +++++++++++++++++++ js/src/devtools/rootAnalysis/utility.js | 18 ++++++++ .../jsapi-tests/testGCStoreBufferRemoval.cpp | 2 +- js/src/jsgc.h | 2 +- 10 files changed, 94 insertions(+), 22 deletions(-) diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 150dce3ac0cc..c95ab18b1ac0 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -551,7 +551,7 @@ class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertNoAlloc public: AutoSuppressGCAnalysis() : AutoAssertNoAlloc() {} explicit AutoSuppressGCAnalysis(JSRuntime* rt) : AutoAssertNoAlloc(rt) {} -}; +} JS_HAZ_GC_SUPPRESSED; /** * Assert that code is only ever called from a GC callback, disable the static diff --git a/js/public/GCAnnotations.h b/js/public/GCAnnotations.h index e73caf8d4da6..28813a4db991 100644 --- a/js/public/GCAnnotations.h +++ b/js/public/GCAnnotations.h @@ -39,6 +39,8 @@ // invalidating GC pointers. # define JS_HAZ_GC_CALL __attribute__((tag("GC Call"))) +# define JS_HAZ_GC_SUPPRESSED __attribute__((tag("Suppress GC"))) + #else # define JS_HAZ_GC_THING @@ -47,6 +49,7 @@ # define JS_HAZ_GC_INVALIDATED # define JS_HAZ_NON_GC_POINTER # define JS_HAZ_GC_CALL +# define JS_HAZ_GC_SUPPRESSED #endif diff --git a/js/src/devtools/rootAnalysis/analyze.py b/js/src/devtools/rootAnalysis/analyze.py index dca1461fc70d..5d41828ca02c 100755 --- a/js/src/devtools/rootAnalysis/analyze.py +++ b/js/src/devtools/rootAnalysis/analyze.py @@ -72,6 +72,7 @@ def generate_hazards(config, outfilename): '%(gcEdges)s', '%(suppressedFunctions_list)s', '%(gcTypes)s', + '%(typeInfo)s', str(i+1), '%(jobs)s', 'tmp.%s' % (i+1,)), config) @@ -113,7 +114,7 @@ JOBS = { 'dbs': ()), 'callgraph': - (('%(js)s', '%(analysis_scriptdir)s/computeCallgraph.js'), + (('%(js)s', '%(analysis_scriptdir)s/computeCallgraph.js', '%(typeInfo)s'), 'callgraph.txt'), 'gcFunctions': @@ -122,8 +123,9 @@ JOBS = { 'dbs': ('gcFunctions.txt', 'gcFunctions.lst', 'gcEdges.txt', 'suppressedFunctions.lst')), 'gcTypes': - (('%(js)s', '%(analysis_scriptdir)s/computeGCTypes.js',), - 'gcTypes.txt'), + (('%(js)s', '%(analysis_scriptdir)s/computeGCTypes.js', + '[gcTypes]', '[typeInfo]'), + ('gcTypes.txt', 'typeInfo.txt')), 'allFunctions': (('%(sixgill_bin)s/xdbkeys', 'src_body.xdb',), @@ -259,8 +261,8 @@ if not data.get('source') and data.get('sixgill_bin'): data['source'] = path.replace("/js/src/jsapi.cpp", "") steps = [ 'dbs', - 'callgraph', 'gcTypes', + 'callgraph', 'gcFunctions', 'allFunctions', 'hazards', diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index a95a208fb73e..a10f63d0cadd 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -12,7 +12,7 @@ var functionName; var functionBodies; if (typeof scriptArgs[0] != 'string' || typeof scriptArgs[1] != 'string') - throw "Usage: analyzeRoots.js [-f function_name] [start end [tmpfile]]"; + throw "Usage: analyzeRoots.js [-f function_name] [start end [tmpfile]]"; var theFunctionNameToFind; if (scriptArgs[0] == '--function') { @@ -20,13 +20,16 @@ if (scriptArgs[0] == '--function') { scriptArgs = scriptArgs.slice(2); } -var gcFunctionsFile = scriptArgs[0]; -var gcEdgesFile = scriptArgs[1]; -var suppressedFunctionsFile = scriptArgs[2]; -var gcTypesFile = scriptArgs[3]; -var batch = (scriptArgs[4]|0) || 1; -var numBatches = (scriptArgs[5]|0) || 1; -var tmpfile = scriptArgs[6] || "tmp.txt"; +var gcFunctionsFile = scriptArgs[0] || "gcFunctions.lst"; +var gcEdgesFile = scriptArgs[1] || "gcEdges.txt"; +var suppressedFunctionsFile = scriptArgs[2] || "suppressedFunctions.lst"; +var gcTypesFile = scriptArgs[3] || "gcTypes.txt"; +var typeInfoFile = scriptArgs[4] || "typeInfo.txt"; +var batch = (scriptArgs[5]|0) || 1; +var numBatches = (scriptArgs[6]|0) || 1; +var tmpfile = scriptArgs[7] || "tmp.txt"; + +GCSuppressionTypes = loadTypeInfo(typeInfoFile)["Suppress GC"]; var gcFunctions = {}; var text = snarf("gcFunctions.lst").split("\n"); diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index fd4dac78e09e..cb50fc61b8dd 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -2,6 +2,10 @@ "use strict"; +// RAII types within which we should assume GC is suppressed, eg +// AutoSuppressGC. +var GCSuppressionTypes = []; + // Ignore calls made through these function pointers var ignoreIndirectCalls = { "mallocSizeOf" : true, @@ -186,6 +190,7 @@ var ignoreFunctions = { // up wrapping a pending exception. See bug 898815 for the heavyweight fix. "void js::AutoCompartment::~AutoCompartment(int32)" : true, "void JSAutoCompartment::~JSAutoCompartment(int32)" : true, + "void js::AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()" : true, // Bug 948646 - the only thing AutoJSContext's constructor calls // is an Init() routine whose entire body is covered with an @@ -322,13 +327,7 @@ function isUnsafeStorage(typeName) function isSuppressConstructor(varName) { // varName[1] contains the unqualified name - return [ - "AutoSuppressGC", - "AutoAssertGCCallback", - "AutoEnterAnalysis", - "AutoSuppressGCAnalysis", - "AutoIgnoreRootingHazards" - ].indexOf(varName[1]) != -1; + return GCSuppressionTypes.indexOf(varName[1]) != -1; } // nsISupports subclasses' methods may be scriptable (or overridden diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index e72c67313c85..59c7944fc708 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -12,6 +12,8 @@ if (scriptArgs[0] == '--function') { scriptArgs = scriptArgs.slice(2); } +var typeInfo_filename = scriptArgs[0] || "typeInfo.txt"; + var subclasses = new Map(); // Map from csu => set of immediate subclasses var superclasses = new Map(); // Map from csu => set of immediate superclasses var classFunctions = new Map(); // Map from "csu:name" => set of full method name @@ -306,6 +308,8 @@ function processBody(functionName, body) } } +GCSuppressionTypes = loadTypeInfo(typeInfo_filename)["Suppress GC"]; + var xdb = xdbLibrary(); xdb.open("src_comp.xdb"); diff --git a/js/src/devtools/rootAnalysis/computeGCTypes.js b/js/src/devtools/rootAnalysis/computeGCTypes.js index f0712ecb47db..af4d703896e1 100644 --- a/js/src/devtools/rootAnalysis/computeGCTypes.js +++ b/js/src/devtools/rootAnalysis/computeGCTypes.js @@ -5,14 +5,20 @@ loadRelativeToScript('utility.js'); loadRelativeToScript('annotations.js'); +var gcTypes_filename = scriptArgs[0] || "gcTypes.txt"; +var typeInfo_filename = scriptArgs[1] || "typeInfo.txt"; + var annotations = { 'GCPointers': [], 'GCThings': [], 'NonGCTypes': {}, // unused 'NonGCPointers': {}, 'RootedPointers': {}, + 'GCSuppressors': {}, }; +var gDescriptors = new Map; // Map from descriptor string => Set of typeName + var structureParents = {}; // Map from field => list of var pointerParents = {}; // Map from field => list of var baseClasses = {}; // Map from struct name => list of base class name strings @@ -64,6 +70,8 @@ function processCSU(csu, body) annotations.NonGCPointers[csu] = true; else if (tag == 'Rooted Pointer') annotations.RootedPointers[csu] = true; + else if (tag == 'Suppress GC') + annotations.GCSuppressors[csu] = true; } } @@ -207,6 +215,26 @@ function addGCPointer(typeName) markGCType(typeName, '', '(annotation)', 1, 0, ""); } +// Add an arbitrary descriptor to a type, and apply it recursively to all base +// structs and structs that contain the given typeName as a field. +function addDescriptor(typeName, descriptor) +{ + if (!gDescriptors.has(descriptor)) + gDescriptors.set(descriptor, new Set); + let descriptorTypes = gDescriptors.get(descriptor); + if (!descriptorTypes.has(typeName)) { + descriptorTypes.add(typeName); + if (typeName in structureParents) { + for (let [holder, field] of structureParents[typeName]) + addDescriptor(holder, descriptor); + } + if (typeName in baseClasses) { + for (let base of baseClasses[typeName]) + addDescriptor(base, descriptor); + } + } +} + for (var type of listNonGCPointers()) annotations.NonGCPointers[type] = true; @@ -246,6 +274,8 @@ function explain(csu, indent, seen) { } } +var origOut = os.file.redirect(gcTypes_filename); + for (var csu in gcTypes) { print("GCThing: " + csu); explain(csu, " "); @@ -254,3 +284,16 @@ for (var csu in gcPointers) { print("GCPointer: " + csu); explain(csu, " "); } + +// Redirect output to the typeInfo file and close the gcTypes file. +os.file.close(os.file.redirect(typeInfo_filename)); + +for (let csu in annotations.GCSuppressors) + addDescriptor(csu, 'Suppress GC'); + +for (let [descriptor, types] of gDescriptors) { + for (let csu of types) + print(descriptor + "$$" + csu); +} + +os.file.close(os.file.redirect(origOut)); diff --git a/js/src/devtools/rootAnalysis/utility.js b/js/src/devtools/rootAnalysis/utility.js index e116a07cd8e3..06c18804f362 100644 --- a/js/src/devtools/rootAnalysis/utility.js +++ b/js/src/devtools/rootAnalysis/utility.js @@ -191,3 +191,21 @@ function* readFileLines_gen(filename) libc.fclose(fp); libc.free(ctypes.void_t.ptr(linebuf)); } + +function addToKeyedList(collection, key, entry) +{ + if (!(key in collection)) + collection[key] = []; + collection[key].push(entry); +} + +function loadTypeInfo(filename) +{ + var info = {}; + for (var line of readFileLines_gen(filename)) { + line = line.replace(/\n/, ""); + let [property, name] = line.split("$$"); + addToKeyedList(info, property, name); + } + return info; +} diff --git a/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp b/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp index c3bcb1d642c4..abf5bf00198f 100644 --- a/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp +++ b/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp @@ -16,7 +16,7 @@ struct AutoIgnoreRootingHazards { static volatile int depth; AutoIgnoreRootingHazards() { depth++; } ~AutoIgnoreRootingHazards() { depth--; } -}; +} JS_HAZ_GC_SUPPRESSED; volatile int AutoIgnoreRootingHazards::depth = 0; BEGIN_TEST(testGCStoreBufferRemoval) diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 409acd22dde6..9e2a8b5a7aca 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1269,7 +1269,7 @@ MaybeVerifyBarriers(JSContext* cx, bool always = false) * read the comment in vm/Runtime.h above |suppressGC| and take all appropriate * precautions before instantiating this class. */ -class MOZ_RAII AutoSuppressGC +class MOZ_RAII JS_HAZ_GC_SUPPRESSED AutoSuppressGC { int32_t& suppressGC_; From c6f370b8e1ebad3684c6a8c24addfdb7ecba72d0 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 11:41:54 -0700 Subject: [PATCH 078/199] Bug 1259850 - Various refactorings, r=terrence MozReview-Commit-ID: GYrqbzK3U8W --HG-- extra : rebase_source : dfe6bfb523799becf7ab34c9dbbc365c46035bfc --- .hgignore | 1 + js/src/devtools/rootAnalysis/analyze.py | 4 ++-- js/src/devtools/rootAnalysis/explain.py | 19 +++++++++++-------- js/src/devtools/rootAnalysis/loadCallgraph.js | 9 ++------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.hgignore b/.hgignore index 895885254eab..7aa79886a2da 100644 --- a/.hgignore +++ b/.hgignore @@ -43,6 +43,7 @@ _OPT\.OBJ/ ^js/src/autom4te.cache$ # SpiderMonkey test result logs ^js/src/tests/results-.*\.(html|txt)$ +^js/src/devtools/rootAnalysis/t/out # Java HTML5 parser classes ^parser/html/java/(html|java)parser/ diff --git a/js/src/devtools/rootAnalysis/analyze.py b/js/src/devtools/rootAnalysis/analyze.py index 5d41828ca02c..69482dab7b71 100755 --- a/js/src/devtools/rootAnalysis/analyze.py +++ b/js/src/devtools/rootAnalysis/analyze.py @@ -242,7 +242,7 @@ if args.tag and not args.buildcommand: if args.jobs is not None: data['jobs'] = args.jobs if not data.get('jobs'): - data['jobs'] = subprocess.check_output(['nproc', '--ignore=1']) + data['jobs'] = subprocess.check_output(['nproc', '--ignore=1']).strip() if args.buildcommand: data['buildcommand'] = args.buildcommand @@ -286,7 +286,7 @@ for step in steps: for (i, name) in out_indexes(command): data[name] = outfiles[outfile] outfile += 1 - assert len(outfiles) == outfile, 'step \'%s\': mismatched number of output files and params' % step + assert len(outfiles) == outfile, 'step \'%s\': mismatched number of output files (%d) and params (%d)' % (step, outfile, len(outfiles)) if args.step: steps = steps[steps.index(args.step):] diff --git a/js/src/devtools/rootAnalysis/explain.py b/js/src/devtools/rootAnalysis/explain.py index ec7ef1949b1d..dc8b76f5c687 100755 --- a/js/src/devtools/rootAnalysis/explain.py +++ b/js/src/devtools/rootAnalysis/explain.py @@ -3,6 +3,8 @@ import re import argparse +from collections import defaultdict + parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument('rootingHazards', nargs='?', default='rootingHazards.txt') parser.add_argument('gcFunctions', nargs='?', default='gcFunctions.txt') @@ -22,7 +24,7 @@ try: # Map from a GC function name to the list of hazards resulting from # that GC function - hazardousGCFunctions = {} + hazardousGCFunctions = defaultdict(list) # List of tuples (gcFunction, index of hazard) used to maintain the # ordering of the hazards @@ -53,7 +55,7 @@ try: # Function names are surrounded by single quotes. Field calls # are unquoted. current_gcFunction = m.group(2) - hazardousGCFunctions.setdefault(current_gcFunction, []).append(line) + hazardousGCFunctions[current_gcFunction].append(line) hazardOrder.append((current_gcFunction, len(hazardousGCFunctions[current_gcFunction]) - 1)) num_hazards += 1 continue @@ -84,12 +86,13 @@ try: if current_func: gcExplanations[current_func] = explanation - for gcFunction, index in hazardOrder: - gcHazards = hazardousGCFunctions[gcFunction] - if gcFunction in gcExplanations: - print >>hazards, (gcHazards[index] + gcExplanations[gcFunction]) - else: - print >>hazards, gcHazards[index] + for gcFunction, index in hazardOrder: + gcHazards = hazardousGCFunctions[gcFunction] + + if gcFunction in gcExplanations: + print >>hazards, (gcHazards[index] + gcExplanations[gcFunction]) + else: + print >>hazards, gcHazards[index] except IOError as e: print 'Failed: %s' % str(e) diff --git a/js/src/devtools/rootAnalysis/loadCallgraph.js b/js/src/devtools/rootAnalysis/loadCallgraph.js index a221d7143764..7661aad8e53a 100644 --- a/js/src/devtools/rootAnalysis/loadCallgraph.js +++ b/js/src/devtools/rootAnalysis/loadCallgraph.js @@ -53,13 +53,8 @@ function addGCFunction(caller, reason) function addCallEdge(caller, callee, suppressed) { - if (!(caller in calleeGraph)) - calleeGraph[caller] = []; - calleeGraph[caller].push({callee:callee, suppressed:suppressed}); - - if (!(callee in callerGraph)) - callerGraph[callee] = []; - callerGraph[callee].push({caller:caller, suppressed:suppressed}); + addToKeyedList(calleeGraph, caller, {callee:callee, suppressed:suppressed}); + addToKeyedList(callerGraph, callee, {caller:caller, suppressed:suppressed}); } // Map from identifier to full "mangled|readable" name. Or sometimes to a From 3cb5c6a3e832935610cb483e622407452bf5c65b Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 11:44:21 -0700 Subject: [PATCH 079/199] Bug 1259850 - Handle D4 destructors, r=terrence MozReview-Commit-ID: 1hArAcAxvZV --HG-- extra : rebase_source : d9d65df85ac312bab022847843d4b75b25ecd55e --- .../devtools/rootAnalysis/computeCallgraph.js | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index 59c7944fc708..ade5f26f0600 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -372,7 +372,7 @@ function process(functionName, functionBodies) // Bug 1056410: Oh joy. GCC does something even funkier internally, // where it generates calls to ~Foo() but a body for ~Foo(int32) even // though it uses the same mangled name for both. So we need to add a - // synthetic edge from the former to the latter. + // synthetic edge from ~Foo() -> ~Foo(int32). // // inChargeXTor will have the (int32). if (functionName.indexOf("::~") > 0) { @@ -390,7 +390,7 @@ function process(functionName, functionBodies) // D1 # complete object destructor // D2 # base object destructor // - // In actual practice, I have observed a C4 constructor generated by gcc + // In actual practice, I have observed C4 and D4 xtors generated by gcc // 4.9.3 (but not 4.7.3). The gcc source code says: // // /* This is the old-style "[unified]" constructor. @@ -398,23 +398,29 @@ function process(functionName, functionBodies) // it from the clones in order to share code and save space. */ // // Unfortunately, that "call... from the clones" does not seem to appear in - // the CFG we get from GCC. So if we see a C4 constructor, inject an edge - // to it from C1, C2, and C3. (Note that C3 isn't even used in current GCC, - // but add the edge anyway just in case.) - if (functionName.indexOf("C4E") != -1) { + // the CFG we get from GCC. So if we see a C4 constructor or D4 destructor, + // inject an edge to it from C1, C2, and C3 (or D1, D2, and D3). (Note that + // C3 isn't even used in current GCC, but add the edge anyway just in + // case.) + if (functionName.indexOf("C4E") != -1 || functionName.indexOf("D4Ev") != -1) { var [ mangled, unmangled ] = splitFunction(functionName); // E terminates the method name (and precedes the method parameters). - if (mangled.indexOf("C4E") != -1) { - // If "C4E" shows up in the mangled name for another reason, this - // will create bogus edges in the callgraph. But that shouldn't - // matter too much, and is somewhat difficult to avoid, so we will - // live with it. - var C1 = mangled.replace("C4E", "C1E"); - var C2 = mangled.replace("C4E", "C2E"); - var C3 = mangled.replace("C4E", "C3E"); - print("D " + memo(C1) + " " + memo(mangled)); - print("D " + memo(C2) + " " + memo(mangled)); - print("D " + memo(C3) + " " + memo(mangled)); + // If eg "C4E" shows up in the mangled name for another reason, this + // will create bogus edges in the callgraph. But will affect little and + // is somewhat difficult to avoid, so we will live with it. + for (let [synthetic, variant] of [['C4E', 'C1E'], + ['C4E', 'C2E'], + ['C4E', 'C3E'], + ['D4Ev', 'D1Ev'], + ['D4Ev', 'D2Ev'], + ['D4Ev', 'D3Ev']]) + { + if (mangled.indexOf(synthetic) == -1) + continue; + + let variant_mangled = mangled.replace(synthetic, variant); + let variant_full = variant_mangled + "$" + unmangled; + print("D " + memo(variant_full) + " " + memo(functionName)); } } } From 44e27262d8d766bbfd0fb04012a39b3cfa8b1d4c Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 11:45:05 -0700 Subject: [PATCH 080/199] Bug 1259850 - Provide GC stacks for synthesized functions, r=terrence MozReview-Commit-ID: 6WYqoXN3k9J --HG-- extra : rebase_source : 31bdc58f9aff90bc7aecb076885e56f37885a320 --- .../rootAnalysis/computeGCFunctions.js | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/js/src/devtools/rootAnalysis/computeGCFunctions.js b/js/src/devtools/rootAnalysis/computeGCFunctions.js index 086cfef35886..97efcb38af22 100644 --- a/js/src/devtools/rootAnalysis/computeGCFunctions.js +++ b/js/src/devtools/rootAnalysis/computeGCFunctions.js @@ -21,16 +21,20 @@ loadCallgraph(callgraph_filename); printErr("Writing " + gcFunctions_filename); redirect(gcFunctions_filename); + for (var name in gcFunctions) { - print(""); - print("GC Function: " + name + "$" + readableNames[name][0]); - do { - name = gcFunctions[name]; - if (name in readableNames) - print(" " + readableNames[name][0]); - else - print(" " + name); - } while (name in gcFunctions); + for (let readable of readableNames[name]) { + print(""); + print("GC Function: " + name + "$" + readable); + let current = name; + do { + current = gcFunctions[current]; + if (current in readableNames) + print(" " + readableNames[current][0]); + else + print(" " + current); + } while (current in gcFunctions); + } } printErr("Writing " + gcFunctionsList_filename); From cfed81ff96cd98a93ad69c90d4c483824f1cb29e Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 14:40:23 -0700 Subject: [PATCH 081/199] Bug 1259850 - Rewrite the test suite, add several tests, r=terrence MozReview-Commit-ID: HtJ0uA3IfXZ --HG-- rename : js/src/devtools/rootAnalysis/run-test.py => js/src/devtools/rootAnalysis/t/sixgill.py extra : rebase_source : ae42485def39c26798bee72a2544034d1a557d3c --- js/src/devtools/rootAnalysis/run-test.py | 196 ++++++------------ .../rootAnalysis/t/hazards/source.cpp | 89 ++++++++ .../devtools/rootAnalysis/t/hazards/test.py | 36 ++++ .../rootAnalysis/t/sixgill-tree/source.cpp | 70 +++++++ .../rootAnalysis/t/sixgill-tree/test.py | 60 ++++++ js/src/devtools/rootAnalysis/t/sixgill.py | 63 ++++++ .../rootAnalysis/t/suppression/source.cpp | 64 ++++++ .../rootAnalysis/t/suppression/test.py | 23 ++ js/src/devtools/rootAnalysis/t/testlib.py | 119 +++++++++++ 9 files changed, 589 insertions(+), 131 deletions(-) create mode 100644 js/src/devtools/rootAnalysis/t/hazards/source.cpp create mode 100644 js/src/devtools/rootAnalysis/t/hazards/test.py create mode 100644 js/src/devtools/rootAnalysis/t/sixgill-tree/source.cpp create mode 100644 js/src/devtools/rootAnalysis/t/sixgill-tree/test.py create mode 100644 js/src/devtools/rootAnalysis/t/sixgill.py create mode 100644 js/src/devtools/rootAnalysis/t/suppression/source.cpp create mode 100644 js/src/devtools/rootAnalysis/t/suppression/test.py create mode 100644 js/src/devtools/rootAnalysis/t/testlib.py diff --git a/js/src/devtools/rootAnalysis/run-test.py b/js/src/devtools/rootAnalysis/run-test.py index a960b57d1399..0d205b0d421a 100644 --- a/js/src/devtools/rootAnalysis/run-test.py +++ b/js/src/devtools/rootAnalysis/run-test.py @@ -3,150 +3,84 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -import sys import os -import re -import json +import site import subprocess +import argparse -testdir = os.path.abspath(os.path.dirname(__file__)) +testdir = os.path.abspath(os.path.join(os.path.dirname(__file__), 't')) +site.addsitedir(testdir) +from testlib import Test, equal -cfg = {} -cfg['SIXGILL_ROOT'] = os.environ.get('SIXGILL', - os.path.join(testdir, "sixgill")) -cfg['SIXGILL_BIN'] = os.environ.get('SIXGILL_BIN', - os.path.join(cfg['SIXGILL_ROOT'], "usr", "bin")) -cfg['SIXGILL_PLUGIN'] = os.environ.get('SIXGILL_PLUGIN', - os.path.join(cfg['SIXGILL_ROOT'], "usr", "libexec", "sixgill", "gcc", "xgill.so")) -cfg['CC'] = os.environ.get("CC", - "gcc") -cfg['CXX'] = os.environ.get("CXX", - cfg.get('CC', 'g++')) -cfg['JS_BIN'] = os.environ["JS"] +scriptdir = os.path.abspath(os.path.dirname(__file__)) + +parser = argparse.ArgumentParser(description='run hazard analysis tests') +parser.add_argument( + '--js', default=os.environ.get('JS'), + help='JS binary to run the tests with') +parser.add_argument( + '--sixgill', default=os.environ.get('SIXGILL', os.path.join(testdir, "sixgill")), + help='Path to root of sixgill installation') +parser.add_argument( + '--sixgill-bin', default=os.environ.get('SIXGILL_BIN'), + help='Path to sixgill binary dir') +parser.add_argument( + '--sixgill-plugin', default=os.environ.get('SIXGILL_PLUGIN'), + help='Full path to sixgill gcc plugin') +parser.add_argument( + '--gccdir', default=os.environ.get('GCCDIR'), + help='Path to GCC installation dir') +parser.add_argument( + '--cc', default=os.environ.get('CC'), + help='Path to gcc') +parser.add_argument( + '--cxx', default=os.environ.get('CXX'), + help='Path to g++') +parser.add_argument( + '--verbose', '-v', action='store_true', + help='Display verbose output, including commands executed') + +cfg = parser.parse_args() + +if not cfg.js: + exit('Must specify JS binary through environment variable or --js option') +if not cfg.cc: + if cfg.gccdir: + cfg.cc = os.path.join(cfg.gccdir, "bin", "gcc") + else: + cfg.cc = "gcc" +if not cfg.cxx: + if cfg.gccdir: + cfg.cxx = os.path.join(cfg.gccdir, "bin", "g++") + else: + cfg.cxx = "g++" +if not cfg.sixgill_bin: + cfg.sixgill_bin = os.path.join(cfg.sixgill, "usr", "bin") +if not cfg.sixgill_plugin: + cfg.sixgill_plugin = os.path.join(cfg.sixgill, "usr", "libexec", "sixgill", "gcc", "xgill.so") + +subprocess.check_call([cfg.js, '-e', 'if (!getBuildConfiguration()["has-ctypes"]) quit(1)']) def binpath(prog): - return os.path.join(cfg['SIXGILL_BIN'], prog) + return os.path.join(cfg.sixgill_bin, prog) -if not os.path.exists("test-output"): - os.mkdir("test-output") +try: + os.mkdir(os.path.join('t', 'out')) +except OSError: + pass -# Simplified version of the body info. -class Body(dict): - def __init__(self, body): - self['BlockIdKind'] = body['BlockId']['Kind'] - if 'Variable' in body['BlockId']: - self['BlockName'] = body['BlockId']['Variable']['Name'][0] - self['LineRange'] = [ body['Location'][0]['Line'], body['Location'][1]['Line'] ] - self['Filename'] = body['Location'][0]['CacheString'] - self['Edges'] = body.get('PEdge', []) - self['Points'] = { i+1: body['PPoint'][i]['Location']['Line'] for i in range(len(body['PPoint'])) } - self['Index'] = body['Index'] - self['Variables'] = { x['Variable']['Name'][0]: x['Type'] for x in body['DefineVariable'] } - - # Indexes - self['Line2Points'] = {} - for point, line in self['Points'].items(): - self['Line2Points'].setdefault(line, []).append(point) - self['SrcPoint2Edges'] = {} - for edge in self['Edges']: - (src, dst) = edge['Index'] - self['SrcPoint2Edges'].setdefault(src, []).append(edge) - self['Line2Edges'] = {} - for (src, edges) in self['SrcPoint2Edges'].items(): - line = self['Points'][src] - self['Line2Edges'].setdefault(line, []).extend(edges) - - def edges_from_line(self, line): - return self['Line2Edges'][line] - - def edge_from_line(self, line): - edges = self.edges_from_line(line) - assert(len(edges) == 1) - return edges[0] - - def edges_from_point(self, point): - return self['SrcPoint2Edges'][point] - - def edge_from_point(self, point): - edges = self.edges_from_point(point) - assert(len(edges) == 1) - return edges[0] - - def assignment_point(self, varname): - for edge in self['Edges']: - if edge['Kind'] != 'Assign': - continue - dst = edge['Exp'][0] - if dst['Kind'] != 'Var': - continue - if dst['Variable']['Name'][0] == varname: - return edge['Index'][0] - raise Exception("assignment to variable %s not found" % varname) - - def assignment_line(self, varname): - return self['Points'][self.assignment_point(varname)] - -tests = ['test'] +tests = ['sixgill-tree', 'suppression', 'hazards'] for name in tests: indir = os.path.join(testdir, name) - outdir = os.path.join(testdir, "test-output", name) - if not os.path.exists(outdir): + outdir = os.path.join(testdir, 'out', name) + try: os.mkdir(outdir) + except OSError: + pass - def compile(source): - cmd = "{CXX} -c {source} -fplugin={sixgill}".format(source=os.path.join(indir, source), - CXX=cfg['CXX'], sixgill=cfg['SIXGILL_PLUGIN']) - print("Running %s" % cmd) - subprocess.check_call(["sh", "-c", cmd]) - - def load_db_entry(dbname, pattern): - if not isinstance(pattern, basestring): - output = subprocess.check_output([binpath("xdbkeys"), dbname + ".xdb"]) - entries = output.splitlines() - matches = [f for f in entries if re.search(pattern, f)] - if len(matches) == 0: - raise Exception("entry not found") - if len(matches) > 1: - raise Exception("multiple entries found") - pattern = matches[0] - - output = subprocess.check_output([binpath("xdbfind"), "-json", dbname + ".xdb", pattern]) - return json.loads(output) - - def computeGCTypes(): - file("defaults.py", "w").write('''\ -analysis_scriptdir = '{testdir}' -sixgill_bin = '{bindir}' -'''.format(testdir=testdir, bindir=cfg['SIXGILL_BIN'])) - cmd = [ - os.path.join(testdir, "analyze.py"), - "gcTypes", "--upto", "gcTypes", - "--source=%s" % indir, - "--objdir=%s" % outdir, - "--js=%s" % cfg['JS_BIN'], - ] - print("Running " + " ".join(cmd)) - output = subprocess.check_call(cmd) - - def loadGCTypes(): - gctypes = {'GCThings': [], 'GCPointers': []} - for line in file(os.path.join(outdir, "gcTypes.txt")): - m = re.match(r'^(GC\w+): (.*)', line) - if m: - gctypes[m.group(1) + 's'].append(m.group(2)) - return gctypes - - def process_body(body): - return Body(body) - - def process_bodies(bodies): - return [ process_body(b) for b in bodies ] - - def equal(got, expected): - if got != expected: - print("Got '%s', expected '%s'" % (got, expected)) + test = Test(indir, outdir, cfg) os.chdir(outdir) subprocess.call(["sh", "-c", "rm *.xdb"]) - execfile(os.path.join(indir, "test.py")) + execfile(os.path.join(indir, "test.py"), {'test': test, 'equal': equal}) print("TEST-PASSED: %s" % name) diff --git a/js/src/devtools/rootAnalysis/t/hazards/source.cpp b/js/src/devtools/rootAnalysis/t/hazards/source.cpp new file mode 100644 index 000000000000..1e5191bc9445 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/hazards/source.cpp @@ -0,0 +1,89 @@ +#define ANNOTATE(property) __attribute__((tag(property))) + +struct Cell { int f; } ANNOTATE("GC Thing"); + +class AutoSuppressGC_Base { + public: + AutoSuppressGC_Base() {} + ~AutoSuppressGC_Base() {} +} ANNOTATE("Suppress GC"); + +class AutoSuppressGC_Child : public AutoSuppressGC_Base { + public: + AutoSuppressGC_Child() : AutoSuppressGC_Base() {} +}; + +class AutoSuppressGC { + AutoSuppressGC_Child helpImBeingSuppressed; + + public: + AutoSuppressGC() {} +}; + +extern void GC() ANNOTATE("GC Call"); +extern void invisible(); + +void GC() +{ + // If the implementation is too trivial, the function body won't be emitted at all. + asm(""); + invisible(); +} + +extern void foo(Cell*); + +void suppressedFunction() { + GC(); // Calls GC, but is always called within AutoSuppressGC +} + +void halfSuppressedFunction() { + GC(); // Calls GC, but is sometimes called within AutoSuppressGC +} + +void unsuppressedFunction() { + GC(); // Calls GC, never within AutoSuppressGC +} + +volatile static int x = 3; +volatile static int* xp = &x; +struct GCInDestructor { + ~GCInDestructor() { + invisible(); + asm(""); + *xp = 4; + GC(); + } +}; + +Cell* +f() +{ + GCInDestructor kaboom; + + Cell cell; + Cell* cell1 = &cell; + Cell* cell2 = &cell; + Cell* cell3 = &cell; + Cell* cell4 = &cell; + { + AutoSuppressGC nogc; + suppressedFunction(); + halfSuppressedFunction(); + } + foo(cell1); + halfSuppressedFunction(); + foo(cell2); + unsuppressedFunction(); + { + // Old bug: it would look from the first AutoSuppressGC constructor it + // found to the last destructor. This statement *should* have no effect. + AutoSuppressGC nogc; + } + foo(cell3); + Cell* cell5 = &cell; + foo(cell5); + + // Hazard in return value due to ~GCInDestructor + Cell* cell6 = &cell; + return cell6; +} diff --git a/js/src/devtools/rootAnalysis/t/hazards/test.py b/js/src/devtools/rootAnalysis/t/hazards/test.py new file mode 100644 index 000000000000..5e234d18061d --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/hazards/test.py @@ -0,0 +1,36 @@ +test.compile("source.cpp") +test.run_analysis_script('gcTypes') + +# gcFunctions should be the inverse, but we get to rely on unmangled names here. +gcFunctions = test.load_gcFunctions() +print(gcFunctions) +assert('void GC()' in gcFunctions) +assert('void suppressedFunction()' not in gcFunctions) +assert('void halfSuppressedFunction()' in gcFunctions) +assert('void unsuppressedFunction()' in gcFunctions) +assert('Cell* f()' in gcFunctions) + +hazards = test.load_hazards() +hazmap = {haz.variable: haz for haz in hazards} +assert('cell1' not in hazmap) +assert('cell2' in hazmap) +assert('cell3' in hazmap) +assert('cell4' not in hazmap) +assert('cell5' not in hazmap) +assert('cell6' not in hazmap) +assert('' in hazmap) + +# All hazards should be in f() +assert(hazmap['cell2'].function == 'Cell* f()') +assert(len(set(haz.function for haz in hazards)) == 1) + +# Check that the correct GC call is reported for each hazard. (cell3 has a +# hazard from two different GC calls; it doesn't really matter which is +# reported.) +assert(hazmap['cell2'].GCFunction == 'void halfSuppressedFunction()') +assert(hazmap['cell3'].GCFunction in ('void halfSuppressedFunction()', 'void unsuppressedFunction()')) +assert(hazmap[''].GCFunction == 'void GCInDestructor::~GCInDestructor()') + +# Type names are handy to have in the report. +assert(hazmap['cell2'].type == 'Cell*') +assert(hazmap[''].type == 'Cell*') diff --git a/js/src/devtools/rootAnalysis/t/sixgill-tree/source.cpp b/js/src/devtools/rootAnalysis/t/sixgill-tree/source.cpp new file mode 100644 index 000000000000..2de9ef4bb90b --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/sixgill-tree/source.cpp @@ -0,0 +1,70 @@ +#define ANNOTATE(property) __attribute__((tag(property))) + +namespace js { +namespace gc { +struct Cell { int f; } ANNOTATE("GC Thing"); +} +} + +struct Bogon { +}; + +struct JustACell : public js::gc::Cell { + bool iHaveNoDataMembers() { return true; } +}; + +struct JSObject : public js::gc::Cell, public Bogon { + int g; +}; + +struct SpecialObject : public JSObject { + int z; +}; + +struct ErrorResult { + bool hasObj; + JSObject *obj; + void trace() {} +} ANNOTATE("Suppressed GC Pointer"); + +struct OkContainer { + ErrorResult res; + bool happy; +}; + +struct UnrootedPointer { + JSObject *obj; +}; + +template +class Rooted { + T data; +} ANNOTATE("Rooted Pointer"); + +extern void js_GC() ANNOTATE("GC Call") ANNOTATE("Slow"); + +void js_GC() {} + +void root_arg(JSObject *obj, JSObject *random) +{ + // Use all these types so they get included in the output. + SpecialObject so; + UnrootedPointer up; + Bogon b; + OkContainer okc; + Rooted ro; + Rooted rso; + + obj = random; + + JSObject *other1 = obj; + js_GC(); + + float MARKER1 = 0; + JSObject *other2 = obj; + other1->f = 1; + other2->f = -1; + + unsigned int u1 = 1; + unsigned int u2 = -1; +} diff --git a/js/src/devtools/rootAnalysis/t/sixgill-tree/test.py b/js/src/devtools/rootAnalysis/t/sixgill-tree/test.py new file mode 100644 index 000000000000..c0c0263cd024 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/sixgill-tree/test.py @@ -0,0 +1,60 @@ +import re + +test.compile("source.cpp") +test.computeGCTypes() +body = test.process_body(test.load_db_entry("src_body", re.compile(r'root_arg'))[0]) + +# Rendering positive and negative integers +marker1 = body.assignment_line('MARKER1') +equal(body.edge_from_line(marker1 + 2)['Exp'][1]['String'], '1') +equal(body.edge_from_line(marker1 + 3)['Exp'][1]['String'], '-1') + +equal(body.edge_from_point(body.assignment_point('u1'))['Exp'][1]['String'], '1') +equal(body.edge_from_point(body.assignment_point('u2'))['Exp'][1]['String'], '4294967295') + +assert('obj' in body['Variables']) +assert('random' in body['Variables']) +assert('other1' in body['Variables']) +assert('other2' in body['Variables']) + +# Test function annotations +js_GC = test.process_body(test.load_db_entry("src_body", re.compile(r'js_GC'))[0]) +annotations = js_GC['Variables']['void js_GC()']['Annotation'] +assert(annotations) +found_call_tag = False +for annotation in annotations: + (annType, value) = annotation['Name'] + if annType == 'Tag' and value == 'GC Call': + found_call_tag = True +assert(found_call_tag) + +# Test type annotations + +# js::gc::Cell first +cell = test.load_db_entry("src_comp", 'js::gc::Cell')[0] +assert(cell['Kind'] == 'Struct') +annotations = cell['Annotation'] +assert(len(annotations) == 1) +(tag, value) = annotations[0]['Name'] +assert(tag == 'Tag') +assert(value == 'GC Thing') + +# Check JSObject inheritance. +JSObject = test.load_db_entry("src_comp", 'JSObject')[0] +bases = [ b['Base'] for b in JSObject['CSUBaseClass'] ] +assert('js::gc::Cell' in bases) +assert('Bogon' in bases) +assert(len(bases) == 2) + +# Check type analysis +gctypes = test.load_gcTypes() +assert('js::gc::Cell' in gctypes['GCThings']) +assert('JustACell' in gctypes['GCThings']) +assert('JSObject' in gctypes['GCThings']) +assert('SpecialObject' in gctypes['GCThings']) +assert('UnrootedPointer' in gctypes['GCPointers']) +assert('Bogon' not in gctypes['GCThings']) +assert('Bogon' not in gctypes['GCPointers']) +assert('ErrorResult' not in gctypes['GCPointers']) +assert('OkContainer' not in gctypes['GCPointers']) +assert('class Rooted' not in gctypes['GCPointers']) diff --git a/js/src/devtools/rootAnalysis/t/sixgill.py b/js/src/devtools/rootAnalysis/t/sixgill.py new file mode 100644 index 000000000000..2bdf76a49b44 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/sixgill.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from collections import defaultdict + +# Simplified version of the body info. +class Body(dict): + def __init__(self, body): + self['BlockIdKind'] = body['BlockId']['Kind'] + if 'Variable' in body['BlockId']: + self['BlockName'] = body['BlockId']['Variable']['Name'][0].split("$")[-1] + loc = body['Location'] + self['LineRange'] = (loc[0]['Line'], loc[1]['Line']) + self['Filename'] = loc[0]['CacheString'] + self['Edges'] = body.get('PEdge', []) + self['Points'] = { i: p['Location']['Line'] for i, p in enumerate(body['PPoint'], 1) } + self['Index'] = body['Index'] + self['Variables'] = { x['Variable']['Name'][0].split("$")[-1]: x['Type'] for x in body['DefineVariable'] } + + # Indexes + self['Line2Points'] = defaultdict(list) + for point, line in self['Points'].items(): + self['Line2Points'][line].append(point) + self['SrcPoint2Edges'] = defaultdict(list) + for edge in self['Edges']: + src, dst = edge['Index'] + self['SrcPoint2Edges'][src].append(edge) + self['Line2Edges'] = defaultdict(list) + for (src, edges) in self['SrcPoint2Edges'].items(): + line = self['Points'][src] + self['Line2Edges'][line].extend(edges) + + def edges_from_line(self, line): + return self['Line2Edges'][line] + + def edge_from_line(self, line): + edges = self.edges_from_line(line) + assert(len(edges) == 1) + return edges[0] + + def edges_from_point(self, point): + return self['SrcPoint2Edges'][point] + + def edge_from_point(self, point): + edges = self.edges_from_point(point) + assert(len(edges) == 1) + return edges[0] + + def assignment_point(self, varname): + for edge in self['Edges']: + if edge['Kind'] != 'Assign': + continue + dst = edge['Exp'][0] + if dst['Kind'] != 'Var': + continue + if dst['Variable']['Name'][0] == varname: + return edge['Index'][0] + raise Exception("assignment to variable %s not found" % varname) + + def assignment_line(self, varname): + return self['Points'][self.assignment_point(varname)] diff --git a/js/src/devtools/rootAnalysis/t/suppression/source.cpp b/js/src/devtools/rootAnalysis/t/suppression/source.cpp new file mode 100644 index 000000000000..e7b41b4cb326 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/suppression/source.cpp @@ -0,0 +1,64 @@ +#define ANNOTATE(property) __attribute__((tag(property))) + +struct Cell { int f; } ANNOTATE("GC Thing"); + +class AutoSuppressGC_Base { + public: + AutoSuppressGC_Base() {} + ~AutoSuppressGC_Base() {} +} ANNOTATE("Suppress GC"); + +class AutoSuppressGC_Child : public AutoSuppressGC_Base { + public: + AutoSuppressGC_Child() : AutoSuppressGC_Base() {} +}; + +class AutoSuppressGC { + AutoSuppressGC_Child helpImBeingSuppressed; + + public: + AutoSuppressGC() {} +}; + +extern void GC() ANNOTATE("GC Call"); + +void GC() +{ + // If the implementation is too trivial, the function body won't be emitted at all. + asm(""); +} + +extern void foo(Cell*); + +void suppressedFunction() { + GC(); // Calls GC, but is always called within AutoSuppressGC +} + +void halfSuppressedFunction() { + GC(); // Calls GC, but is sometimes called within AutoSuppressGC +} + +void unsuppressedFunction() { + GC(); // Calls GC, never within AutoSuppressGC +} + +void f() { + Cell* cell1 = nullptr; + Cell* cell2 = nullptr; + Cell* cell3 = nullptr; + { + AutoSuppressGC nogc; + suppressedFunction(); + halfSuppressedFunction(); + } + foo(cell1); + halfSuppressedFunction(); + foo(cell2); + unsuppressedFunction(); + { + // Old bug: it would look from the first AutoSuppressGC constructor it + // found to the last destructor. This statement *should* have no effect. + AutoSuppressGC nogc; + } + foo(cell3); +} diff --git a/js/src/devtools/rootAnalysis/t/suppression/test.py b/js/src/devtools/rootAnalysis/t/suppression/test.py new file mode 100644 index 000000000000..65974cc3369a --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/suppression/test.py @@ -0,0 +1,23 @@ +test.compile("source.cpp") +test.run_analysis_script('gcTypes', upto='gcFunctions') + +# The suppressions file uses only mangled names since it's for internal use, +# though I may change that soon given (1) the unfortunate non-uniqueness of +# mangled constructor names, and (2) the usefulness of this file for +# mrgiggles's reporting. +suppressed = test.load_suppressed_functions() + +# Only one of these is fully suppressed (ie, *always* called within the scope +# of an AutoSuppressGC). +assert(len(filter(lambda f: 'suppressedFunction' in f, suppressed)) == 1) +assert(len(filter(lambda f: 'halfSuppressedFunction' in f, suppressed)) == 0) +assert(len(filter(lambda f: 'unsuppressedFunction' in f, suppressed)) == 0) + +# gcFunctions should be the inverse, but we get to rely on unmangled names here. +gcFunctions = test.load_gcFunctions() +print(gcFunctions) +assert('void GC()' in gcFunctions) +assert('void suppressedFunction()' not in gcFunctions) +assert('void halfSuppressedFunction()' in gcFunctions) +assert('void unsuppressedFunction()' in gcFunctions) +assert('void f()' in gcFunctions) diff --git a/js/src/devtools/rootAnalysis/t/testlib.py b/js/src/devtools/rootAnalysis/t/testlib.py new file mode 100644 index 000000000000..6f48ab19f278 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/testlib.py @@ -0,0 +1,119 @@ +import json +import os +import re +import subprocess + +from sixgill import Body +from collections import defaultdict, namedtuple + +scriptdir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + +HazardSummary = namedtuple('HazardSummary', ['function', 'variable', 'type', 'GCFunction', 'location']) + + +def equal(got, expected): + if got != expected: + print("Got '%s', expected '%s'" % (got, expected)) + +def extract_unmangled(func): + return func.split('$')[-1] + + +class Test(object): + def __init__(self, indir, outdir, cfg): + self.indir = indir + self.outdir = outdir + self.cfg = cfg + + def infile(self, path): + return os.path.join(self.indir, path) + + def binpath(self, prog): + return os.path.join(self.cfg.sixgill_bin, prog) + + def compile(self, source): + cmd = "{CXX} -c {source} -O3 -std=c++11 -fplugin={sixgill} -fplugin-arg-xgill-mangle=1".format( + source=self.infile(source), + CXX=self.cfg.cxx, sixgill=self.cfg.sixgill_plugin) + if self.cfg.verbose: + print("Running %s" % cmd) + subprocess.check_call(["sh", "-c", cmd]) + + def load_db_entry(self, dbname, pattern): + '''Look up an entry from an XDB database file, 'pattern' may be an exact + matching string, or an re pattern object matching a single entry.''' + + if not isinstance(pattern, basestring): + output = subprocess.check_output([self.binpath("xdbkeys"), dbname + ".xdb"]) + matches = filter(lambda _: re.search(pattern, _), output.splitlines()) + if len(matches) == 0: + raise Exception("entry not found") + if len(matches) > 1: + raise Exception("multiple entries found") + pattern = matches[0] + + output = subprocess.check_output([self.binpath("xdbfind"), "-json", dbname + ".xdb", pattern]) + return json.loads(output) + + def run_analysis_script(self, phase, upto=None): + file("defaults.py", "w").write('''\ +analysis_scriptdir = '{scriptdir}' +sixgill_bin = '{bindir}' +'''.format(scriptdir=scriptdir, bindir=self.cfg.sixgill_bin)) + cmd = [os.path.join(scriptdir, "analyze.py"), phase] + if upto: + cmd += ["--upto", upto] + cmd.append("--source=%s" % self.indir) + cmd.append("--objdir=%s" % self.outdir) + cmd.append("--js=%s" % self.cfg.js) + if self.cfg.verbose: + cmd.append("--verbose") + print("Running " + " ".join(cmd)) + subprocess.check_call(cmd) + + def computeGCTypes(self): + self.run_analysis_script("gcTypes", upto="gcTypes") + + def computeHazards(self): + self.run_analysis_script("callgraph") + + def load_text_file(self, filename, extract=lambda l: l): + fullpath = os.path.join(self.outdir, filename) + values = (extract(line.strip()) for line in file(fullpath)) + return filter(lambda _: _ is not None, values) + + def load_suppressed_functions(self): + return set(self.load_text_file("suppressedFunctions.lst")) + + def load_gcTypes(self): + def grab_type(line): + m = re.match(r'^(GC\w+): (.*)', line) + if m: + return (m.group(1) + 's', m.group(2)) + return None + + gctypes = defaultdict(list) + for collection, typename in self.load_text_file('gcTypes.txt', extract=grab_type): + gctypes[collection].append(typename) + return gctypes + + def load_gcFunctions(self): + return self.load_text_file('gcFunctions.lst', extract=extract_unmangled) + + def load_hazards(self): + def grab_hazard(line): + m = re.match(r"Function '(.*?)' has unrooted '(.*?)' of type '(.*?)' live across GC call '(.*?)' at (.*)", line) + if m: + info = list(m.groups()) + info[0] = info[0].split("$")[-1] + info[3] = info[3].split("$")[-1] + return HazardSummary(*info) + return None + + return self.load_text_file('rootingHazards.txt', extract=grab_hazard) + + def process_body(self, body): + return Body(body) + + def process_bodies(self, bodies): + return [self.process_body(b) for b in bodies] From 8e2cd9d734f2ac339a5b2e949807248f7e52f371 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 4 May 2016 18:36:23 -0700 Subject: [PATCH 082/199] Bug 1259850 - Update sixgill to ignore exception cleanup and properly qualify some type names MozReview-Commit-ID: 7WSYmLAVb7M --HG-- extra : rebase_source : adb56e3297546f013f669582aa452a5e16e758dd --- .../linux64/hazard.manifest | 68 +++++++++---------- .../linux64/hazard.manifest | 56 +++++++-------- .../rootAnalysis/build/sixgill.manifest | 16 ++--- 3 files changed, 70 insertions(+), 70 deletions(-) diff --git a/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest b/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest index fcef5bfc5738..f4ab1fcabc74 100644 --- a/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest +++ b/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest @@ -1,47 +1,47 @@ [ { -"version": "gcc 4.9.3", -"size": 102421980, -"digest": "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", -"algorithm": "sha512", -"filename": "gcc.tar.xz", -"unpack": true +"size" : 102421980, +"digest" : "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", +"version" : "gcc 4.9.3", +"unpack" : true, +"filename" : "gcc.tar.xz", +"algorithm" : "sha512" }, { -"hg_id" : "cd93f15a30ce", +"unpack" : true, "algorithm" : "sha512", -"digest" : "541eb3842ab6b91bd87223cad7a5e4387ef3e496e5b580c8047b8b586bc7eb69fecf3c9eb8c45a1e0deebb53554f0e8acedfe1b4ca64d93b6d008f3f2eb11389", "filename" : "sixgill.tar.xz", -"size" : 2626640, +"hg_id" : "8cb9c3fb039a+ tip", +"digest" : "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029", +"size" : 2631908 +}, +{ +"algorithm" : "sha512", +"filename" : "gtk3.tar.xz", +"setup" : "setup.sh", +"unpack" : true, +"digest" : "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", +"size" : 12072532 +}, +{ +"size" : 89319524, +"digest" : "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", +"algorithm" : "sha512", +"filename" : "rustc.tar.xz", "unpack" : true }, { -"size": 12072532, -"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", -"algorithm": "sha512", -"filename": "gtk3.tar.xz", -"setup": "setup.sh", -"unpack": true +"algorithm" : "sha512", +"filename" : "sccache.tar.bz2", +"unpack" : true, +"digest" : "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", +"size" : 167175 }, { -"size": 89319524, -"digest": "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", -"algorithm": "sha512", -"filename": "rustc.tar.xz", -"unpack": true -}, -{ -"size": 167175, -"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", -"algorithm": "sha512", -"filename": "sccache.tar.bz2", -"unpack": true -}, -{ -"size": 31078810, -"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c", -"algorithm": "sha512", -"filename": "moz-tt.tar.bz2", -"unpack": true +"filename" : "moz-tt.tar.bz2", +"algorithm" : "sha512", +"unpack" : true, +"digest" : "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c", +"size" : 31078810 } ] diff --git a/browser/config/tooltool-manifests/linux64/hazard.manifest b/browser/config/tooltool-manifests/linux64/hazard.manifest index 9d0818273012..f05dcf166c5b 100644 --- a/browser/config/tooltool-manifests/linux64/hazard.manifest +++ b/browser/config/tooltool-manifests/linux64/hazard.manifest @@ -1,40 +1,40 @@ [ { -"version": "gcc 4.9.3", -"size": 102421980, -"digest": "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", -"algorithm": "sha512", -"filename": "gcc.tar.xz", -"unpack": true -}, -{ -"hg_id" : "cd93f15a30ce", +"size" : 102421980, +"version" : "gcc 4.9.3", +"filename" : "gcc.tar.xz", "algorithm" : "sha512", -"digest" : "541eb3842ab6b91bd87223cad7a5e4387ef3e496e5b580c8047b8b586bc7eb69fecf3c9eb8c45a1e0deebb53554f0e8acedfe1b4ca64d93b6d008f3f2eb11389", -"filename" : "sixgill.tar.xz", -"size" : 2626640, +"digest" : "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", "unpack" : true }, { -"size": 12072532, -"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", -"algorithm": "sha512", -"filename": "gtk3.tar.xz", -"setup": "setup.sh", -"unpack": true +"digest" : "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029", +"unpack" : true, +"algorithm" : "sha512", +"filename" : "sixgill.tar.xz", +"size" : 2631908, +"hg_id" : "8cb9c3fb039a+ tip" }, { -"size": 89319524, -"digest": "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", -"algorithm": "sha512", -"filename": "rustc.tar.xz", -"unpack": true +"digest" : "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", +"unpack" : true, +"setup" : "setup.sh", +"algorithm" : "sha512", +"filename" : "gtk3.tar.xz", +"size" : 12072532 }, { -"size": 167175, -"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", -"algorithm": "sha512", -"filename": "sccache.tar.bz2", -"unpack": true +"unpack" : true, +"digest" : "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", +"filename" : "rustc.tar.xz", +"algorithm" : "sha512", +"size" : 89319524 +}, +{ +"filename" : "sccache.tar.bz2", +"algorithm" : "sha512", +"digest" : "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", +"unpack" : true, +"size" : 167175 } ] diff --git a/js/src/devtools/rootAnalysis/build/sixgill.manifest b/js/src/devtools/rootAnalysis/build/sixgill.manifest index de0d12c709c6..d02bb1bf4226 100644 --- a/js/src/devtools/rootAnalysis/build/sixgill.manifest +++ b/js/src/devtools/rootAnalysis/build/sixgill.manifest @@ -1,10 +1,10 @@ [ - { - "hg_id" : "cd93f15a30ce", - "algorithm" : "sha512", - "digest" : "541eb3842ab6b91bd87223cad7a5e4387ef3e496e5b580c8047b8b586bc7eb69fecf3c9eb8c45a1e0deebb53554f0e8acedfe1b4ca64d93b6d008f3f2eb11389", - "filename" : "sixgill.tar.xz", - "size" : 2626640, - "unpack" : true - } +{ +"digest" : "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029", +"size" : 2631908, +"hg_id" : "8cb9c3fb039a+ tip", +"unpack" : true, +"filename" : "sixgill.tar.xz", +"algorithm" : "sha512" +} ] From 0c242cd71566f6ead08e9936370f318b073b36a6 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 28 Apr 2016 14:19:01 -0700 Subject: [PATCH 083/199] Bug 1259850 - Fix constructor recognition, r=terrence MozReview-Commit-ID: 46064amRbCQ --HG-- extra : rebase_source : 18bd3043d699a55041388e54236620cdd5299202 --- js/src/devtools/rootAnalysis/CFG.js | 2 +- js/src/devtools/rootAnalysis/annotations.js | 27 ++++++++++++++++--- .../devtools/rootAnalysis/computeCallgraph.js | 1 + js/src/devtools/rootAnalysis/run-test.py | 7 +++-- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/js/src/devtools/rootAnalysis/CFG.js b/js/src/devtools/rootAnalysis/CFG.js index 0b35e8914505..6e9facaa1201 100644 --- a/js/src/devtools/rootAnalysis/CFG.js +++ b/js/src/devtools/rootAnalysis/CFG.js @@ -70,7 +70,7 @@ function allRAIIGuardedCallPoints(bodies, body, isConstructor) continue; var variable = callee.Variable; assert(variable.Kind == "Func"); - if (!isConstructor(variable.Name)) + if (!isConstructor(edge.Type, variable.Name)) continue; if (!("PEdgeCallInstance" in edge)) continue; diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index cb50fc61b8dd..7008cbbacb95 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -324,10 +324,31 @@ function isUnsafeStorage(typeName) return typeName.startsWith('UniquePtr<'); } -function isSuppressConstructor(varName) +function isSuppressConstructor(edgeType, varName) { - // varName[1] contains the unqualified name - return GCSuppressionTypes.indexOf(varName[1]) != -1; + // Check whether this could be a constructor + if (edgeType.Kind != 'Function') + return false; + if (!('TypeFunctionCSU' in edgeType)) + return false; + if (edgeType.Type.Kind != 'Void') + return false; + + // Check whether the type is a known suppression type. + var type = edgeType.TypeFunctionCSU.Type.Name; + if (GCSuppressionTypes.indexOf(type) == -1) + return false; + + // And now make sure this is the constructor, not some other method on a + // suppression type. varName[0] contains the qualified name. + var [ mangled, unmangled ] = splitFunction(varName[0]); + if (mangled.search(/C\dE/) == -1) + return false; // Mangled names of constructors have CE + var m = unmangled.match(/([~\w]+)(?:<.*>)?\(/); + if (!m) + return false; + var type_stem = type.replace(/\w+::/g, '').replace(/\<.*\>/g, ''); + return m[1] == type_stem; } // nsISupports subclasses' methods may be scriptable (or overridden diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index ade5f26f0600..ae30d9bd5bca 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -346,6 +346,7 @@ function process(functionName, functionBodies) { for (var body of functionBodies) body.suppressed = []; + for (var body of functionBodies) { for (var [pbody, id] of allRAIIGuardedCallPoints(functionBodies, body, isSuppressConstructor)) pbody.suppressed[id] = true; diff --git a/js/src/devtools/rootAnalysis/run-test.py b/js/src/devtools/rootAnalysis/run-test.py index 0d205b0d421a..113e5fe351a1 100644 --- a/js/src/devtools/rootAnalysis/run-test.py +++ b/js/src/devtools/rootAnalysis/run-test.py @@ -39,6 +39,9 @@ parser.add_argument( parser.add_argument( '--verbose', '-v', action='store_true', help='Display verbose output, including commands executed') +parser.add_argument( + 'tests', nargs='*', default=['sixgill-tree', 'suppression', 'hazards'], + help='tests to run') cfg = parser.parse_args() @@ -69,8 +72,8 @@ try: except OSError: pass -tests = ['sixgill-tree', 'suppression', 'hazards'] -for name in tests: +for name in cfg.tests: + name = os.path.basename(name) indir = os.path.join(testdir, name) outdir = os.path.join(testdir, 'out', name) try: From 169685f9732fb7b0360c787fd5ede81de098ab01 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 28 Apr 2016 11:27:50 -0700 Subject: [PATCH 084/199] Bug 1259850 - Remove unused test files, r=terrence MozReview-Commit-ID: sBZct85zDJ --HG-- extra : rebase_source : f03a5ac2ff9072c7c1e6610d1ec2bd84e81bdb54 --- js/src/devtools/rootAnalysis/test/source.cpp | 70 -------------------- js/src/devtools/rootAnalysis/test/test.py | 58 ---------------- 2 files changed, 128 deletions(-) delete mode 100644 js/src/devtools/rootAnalysis/test/source.cpp delete mode 100644 js/src/devtools/rootAnalysis/test/test.py diff --git a/js/src/devtools/rootAnalysis/test/source.cpp b/js/src/devtools/rootAnalysis/test/source.cpp deleted file mode 100644 index 2de9ef4bb90b..000000000000 --- a/js/src/devtools/rootAnalysis/test/source.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#define ANNOTATE(property) __attribute__((tag(property))) - -namespace js { -namespace gc { -struct Cell { int f; } ANNOTATE("GC Thing"); -} -} - -struct Bogon { -}; - -struct JustACell : public js::gc::Cell { - bool iHaveNoDataMembers() { return true; } -}; - -struct JSObject : public js::gc::Cell, public Bogon { - int g; -}; - -struct SpecialObject : public JSObject { - int z; -}; - -struct ErrorResult { - bool hasObj; - JSObject *obj; - void trace() {} -} ANNOTATE("Suppressed GC Pointer"); - -struct OkContainer { - ErrorResult res; - bool happy; -}; - -struct UnrootedPointer { - JSObject *obj; -}; - -template -class Rooted { - T data; -} ANNOTATE("Rooted Pointer"); - -extern void js_GC() ANNOTATE("GC Call") ANNOTATE("Slow"); - -void js_GC() {} - -void root_arg(JSObject *obj, JSObject *random) -{ - // Use all these types so they get included in the output. - SpecialObject so; - UnrootedPointer up; - Bogon b; - OkContainer okc; - Rooted ro; - Rooted rso; - - obj = random; - - JSObject *other1 = obj; - js_GC(); - - float MARKER1 = 0; - JSObject *other2 = obj; - other1->f = 1; - other2->f = -1; - - unsigned int u1 = 1; - unsigned int u2 = -1; -} diff --git a/js/src/devtools/rootAnalysis/test/test.py b/js/src/devtools/rootAnalysis/test/test.py deleted file mode 100644 index 59281570999d..000000000000 --- a/js/src/devtools/rootAnalysis/test/test.py +++ /dev/null @@ -1,58 +0,0 @@ -compile("source.cpp") -computeGCTypes() -body = process_body(load_db_entry("src_body", re.compile(r'root_arg'))[0]) - -# Rendering positive and negative integers -marker1 = body.assignment_line('MARKER1') -equal(body.edge_from_line(marker1 + 2)['Exp'][1]['String'], '1') -equal(body.edge_from_line(marker1 + 3)['Exp'][1]['String'], '-1') - -equal(body.edge_from_point(body.assignment_point('u1'))['Exp'][1]['String'], '1') -equal(body.edge_from_point(body.assignment_point('u2'))['Exp'][1]['String'], '4294967295') - -assert('obj' in body['Variables']) -assert('random' in body['Variables']) -assert('other1' in body['Variables']) -assert('other2' in body['Variables']) - -# Test function annotations -js_GC = process_body(load_db_entry("src_body", re.compile(r'js_GC'))[0]) -annotations = js_GC['Variables']['void js_GC()']['Annotation'] -assert(annotations) -found_call_tag = False -for annotation in annotations: - (annType, value) = annotation['Name'] - if annType == 'Tag' and value == 'GC Call': - found_call_tag = True -assert(found_call_tag) - -# Test type annotations - -# js::gc::Cell first -cell = load_db_entry("src_comp", 'js::gc::Cell')[0] -assert(cell['Kind'] == 'Struct') -annotations = cell['Annotation'] -assert(len(annotations) == 1) -(tag, value) = annotations[0]['Name'] -assert(tag == 'Tag') -assert(value == 'GC Thing') - -# Check JSObject inheritance. -JSObject = load_db_entry("src_comp", 'JSObject')[0] -bases = [ b['Base'] for b in JSObject['CSUBaseClass'] ] -assert('js::gc::Cell' in bases) -assert('Bogon' in bases) -assert(len(bases) == 2) - -# Check type analysis -gctypes = loadGCTypes() -assert('js::gc::Cell' in gctypes['GCThings']) -assert('JustACell' in gctypes['GCThings']) -assert('JSObject' in gctypes['GCThings']) -assert('SpecialObject' in gctypes['GCThings']) -assert('UnrootedPointer' in gctypes['GCPointers']) -assert('Bogon' not in gctypes['GCThings']) -assert('Bogon' not in gctypes['GCPointers']) -assert('ErrorResult' not in gctypes['GCPointers']) -assert('OkContainer' not in gctypes['GCPointers']) -assert('class Rooted' not in gctypes['GCPointers']) From f651a379e87b7e7e7fc1146b2730f86fd72c5286 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 28 Apr 2016 15:05:09 -0700 Subject: [PATCH 085/199] Bug 1259850 - Test for sixgill exception handling + analysis constructor/destructor matching, r=terrence MozReview-Commit-ID: H5NZlyS2rx5 --HG-- rename : js/src/devtools/rootAnalysis/t/suppression/source.cpp => js/src/devtools/rootAnalysis/t/exceptions/source.cpp rename : js/src/devtools/rootAnalysis/t/suppression/test.py => js/src/devtools/rootAnalysis/t/exceptions/test.py extra : rebase_source : c4f173bd51bf2a19b1805da5f71370c455ad12ca --- js/src/devtools/rootAnalysis/analyzeRoots.js | 2 +- .../devtools/rootAnalysis/computeCallgraph.js | 2 +- js/src/devtools/rootAnalysis/run-test.py | 2 +- .../rootAnalysis/t/exceptions/source.cpp | 42 +++++++++++++++++++ .../rootAnalysis/t/exceptions/test.py | 19 +++++++++ js/src/devtools/rootAnalysis/t/testlib.py | 9 ++-- 6 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 js/src/devtools/rootAnalysis/t/exceptions/source.cpp create mode 100644 js/src/devtools/rootAnalysis/t/exceptions/test.py diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index a10f63d0cadd..a0fa94e061c2 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -29,7 +29,7 @@ var batch = (scriptArgs[5]|0) || 1; var numBatches = (scriptArgs[6]|0) || 1; var tmpfile = scriptArgs[7] || "tmp.txt"; -GCSuppressionTypes = loadTypeInfo(typeInfoFile)["Suppress GC"]; +GCSuppressionTypes = loadTypeInfo(typeInfoFile)["Suppress GC"] || []; var gcFunctions = {}; var text = snarf("gcFunctions.lst").split("\n"); diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index ae30d9bd5bca..dab3f76216f6 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -308,7 +308,7 @@ function processBody(functionName, body) } } -GCSuppressionTypes = loadTypeInfo(typeInfo_filename)["Suppress GC"]; +GCSuppressionTypes = loadTypeInfo(typeInfo_filename)["Suppress GC"] || []; var xdb = xdbLibrary(); xdb.open("src_comp.xdb"); diff --git a/js/src/devtools/rootAnalysis/run-test.py b/js/src/devtools/rootAnalysis/run-test.py index 113e5fe351a1..3bc9085a0aaf 100644 --- a/js/src/devtools/rootAnalysis/run-test.py +++ b/js/src/devtools/rootAnalysis/run-test.py @@ -40,7 +40,7 @@ parser.add_argument( '--verbose', '-v', action='store_true', help='Display verbose output, including commands executed') parser.add_argument( - 'tests', nargs='*', default=['sixgill-tree', 'suppression', 'hazards'], + 'tests', nargs='*', default=['sixgill-tree', 'suppression', 'hazards', 'exceptions'], help='tests to run') cfg = parser.parse_args() diff --git a/js/src/devtools/rootAnalysis/t/exceptions/source.cpp b/js/src/devtools/rootAnalysis/t/exceptions/source.cpp new file mode 100644 index 000000000000..14169740e8f8 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/exceptions/source.cpp @@ -0,0 +1,42 @@ +#define ANNOTATE(property) __attribute__((tag(property))) + +struct Cell { int f; } ANNOTATE("GC Thing"); + +extern void GC() ANNOTATE("GC Call"); + +void GC() +{ + // If the implementation is too trivial, the function body won't be emitted at all. + asm(""); +} + +class RAII_GC { + public: + RAII_GC() {} + ~RAII_GC() { GC(); } +}; + +// ~AutoSomething calls GC because of the RAII_GC field. The constructor, +// though, should *not* GC -- unless it throws an exception. Which is not +// possible when compiled with -fno-exceptions. +class AutoSomething { + RAII_GC gc; + public: + AutoSomething() : gc() { + asm(""); // Ooh, scary, this might throw an exception + } + ~AutoSomething() { + asm(""); + } +}; + +extern void usevar(Cell* cell); + +void f() { + Cell* thing = nullptr; // Live range starts here + + { + AutoSomething smth; // Constructor can GC only if exceptions are enabled + usevar(thing); // Live range ends here + } // In particular, 'thing' is dead at the destructor, so no hazard +} diff --git a/js/src/devtools/rootAnalysis/t/exceptions/test.py b/js/src/devtools/rootAnalysis/t/exceptions/test.py new file mode 100644 index 000000000000..f6d7f5e353f2 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/exceptions/test.py @@ -0,0 +1,19 @@ +test.compile("source.cpp", '-fno-exceptions') +test.run_analysis_script('gcTypes') + +hazards = test.load_hazards() +assert(len(hazards) == 0) + +# If we compile with exceptions, then there *should* be a hazard because +# AutoSomething::AutoSomething might throw an exception, which would cause the +# partially-constructed value to be torn down, which will call ~RAII_GC. + +test.compile("source.cpp", '-fexceptions') +test.run_analysis_script('gcTypes') + +hazards = test.load_hazards() +assert(len(hazards) == 1) +hazard = hazards[0] +assert(hazard.function == 'void f()') +assert(hazard.variable == 'thing') +assert("AutoSomething::AutoSomething" in hazard.GCFunction) diff --git a/js/src/devtools/rootAnalysis/t/testlib.py b/js/src/devtools/rootAnalysis/t/testlib.py index 6f48ab19f278..438398f1ed66 100644 --- a/js/src/devtools/rootAnalysis/t/testlib.py +++ b/js/src/devtools/rootAnalysis/t/testlib.py @@ -31,10 +31,11 @@ class Test(object): def binpath(self, prog): return os.path.join(self.cfg.sixgill_bin, prog) - def compile(self, source): - cmd = "{CXX} -c {source} -O3 -std=c++11 -fplugin={sixgill} -fplugin-arg-xgill-mangle=1".format( + def compile(self, source, options = ''): + cmd = "{CXX} -c {source} -O3 -std=c++11 -fplugin={sixgill} -fplugin-arg-xgill-mangle=1 {options}".format( source=self.infile(source), - CXX=self.cfg.cxx, sixgill=self.cfg.sixgill_plugin) + CXX=self.cfg.cxx, sixgill=self.cfg.sixgill_plugin, + options=options) if self.cfg.verbose: print("Running %s" % cmd) subprocess.check_call(["sh", "-c", cmd]) @@ -75,7 +76,7 @@ sixgill_bin = '{bindir}' self.run_analysis_script("gcTypes", upto="gcTypes") def computeHazards(self): - self.run_analysis_script("callgraph") + self.run_analysis_script("gcTypes") def load_text_file(self, filename, extract=lambda l: l): fullpath = os.path.join(self.outdir, filename) From 4ec00ba75f2ec96b126c5f66f85686728943d3b9 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 24 May 2016 18:26:21 -0700 Subject: [PATCH 086/199] Bug 1259850 - Comments MozReview-Commit-ID: 9RjVxl4EX8N --HG-- extra : rebase_source : 66b76123d3ef36ece9c911eb7815c70f15ed5ad6 --- js/public/GCAnnotations.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/public/GCAnnotations.h b/js/public/GCAnnotations.h index 28813a4db991..366d787bf4dd 100644 --- a/js/public/GCAnnotations.h +++ b/js/public/GCAnnotations.h @@ -23,7 +23,7 @@ # define JS_HAZ_ROOTED __attribute__((tag("Rooted Pointer"))) // Mark a type as something that should not be held live across a GC, but which -// is itself not a GC pointer. +// is not itself a GC pointer. # define JS_HAZ_GC_INVALIDATED __attribute__((tag("Invalidated by GC"))) // Mark a type that would otherwise be considered a GC Pointer (eg because it @@ -39,6 +39,7 @@ // invalidating GC pointers. # define JS_HAZ_GC_CALL __attribute__((tag("GC Call"))) +// Mark an RAII class as suppressing GC within its scope. # define JS_HAZ_GC_SUPPRESSED __attribute__((tag("Suppress GC"))) #else From 69331f349ac3f5b1b184e6735ebe3b55b68f1449 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 27 May 2016 16:48:31 -0700 Subject: [PATCH 087/199] Bug 1259850 - Annotate mFree function pointer as not GCing, r=terrence MozReview-Commit-ID: LrzzLYHsBnO --HG-- extra : rebase_source : 4843a0d61884022398457ba788bb4f03d5bc8846 --- js/src/devtools/rootAnalysis/annotations.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 7008cbbacb95..ea26539dc64d 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -85,6 +85,7 @@ var ignoreCallees = { "std::strstreambuf._M_alloc_fun" : true, "std::strstreambuf._M_free_fun" : true, "struct js::gc::Callback.op" : true, + "mozilla::ThreadSharedFloatArrayBufferList::Storage.mFree" : true, }; function fieldCallCannotGC(csu, fullfield) @@ -160,6 +161,7 @@ function isSuppressedVirtualMethod(csu, method) var ignoreFunctions = { "ptio.c:pt_MapError" : true, "je_malloc_printf" : true, + "vprintf_stderr" : true, "PR_ExplodeTime" : true, "PR_ErrorInstallTable" : true, "PR_SetThreadPrivate" : true, @@ -223,6 +225,13 @@ var ignoreFunctions = { "uint64 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true, "uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true, "void js::Nursery::freeMallocedBuffers()" : true, + + // It would be cool to somehow annotate that nsTHashtable will use + // nsTHashtable::s_MatchEntry for its matchEntry function pointer, but + // there is no mechanism for that. So we will just annotate a particularly + // troublesome logging-related usage. + "EntryType* nsTHashtable::PutEntry(nsTHashtable::KeyType) [with EntryType = nsBaseHashtableET >; nsTHashtable::KeyType = const char*]" : true, + "EntryType* nsTHashtable::GetEntry(nsTHashtable::KeyType) const [with EntryType = nsBaseHashtableET >; nsTHashtable::KeyType = const char*]" : true, }; function isProtobuf(name) From 96d0fa6dcf18fa0c84a53b13e28bf3616e31aac2 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 27 May 2016 22:00:10 -0700 Subject: [PATCH 088/199] Bug 1259850 - Make ZoneCellIter variants to communicate nogc to the hazard analysis, r=jonco Also create accessors on gc::Zone for a nicer API to ZoneCellIters. MozReview-Commit-ID: AxRTTUfWrND --HG-- extra : rebase_source : cf32560db5a123bdc64178a7567ed36e1ecb5b2b --- js/src/gc/Iteration.cpp | 22 +++--- js/src/gc/Statistics.cpp | 45 ++++++++--- js/src/gc/Statistics.h | 31 ++++++-- js/src/gc/Zone.cpp | 13 ++-- js/src/gc/Zone.h | 10 +++ js/src/jit/BaselineJIT.cpp | 10 +-- js/src/jit/Ion.cpp | 8 +- js/src/jscompartment.cpp | 7 +- js/src/jsgc.cpp | 62 +++++++++------- js/src/jsgc.h | 89 ++++++++++++++++++++++ js/src/jsgcinlines.h | 144 ++++++++++++++++++++++++++---------- js/src/jsopcode.cpp | 11 ++- js/src/vm/Debugger.cpp | 4 +- js/src/vm/HelperThreads.cpp | 5 +- js/src/vm/NativeObject.cpp | 2 +- js/src/vm/TypeInference.cpp | 18 ++--- 16 files changed, 341 insertions(+), 140 deletions(-) diff --git a/js/src/gc/Iteration.cpp b/js/src/gc/Iteration.cpp index daa8c741a8ef..6c76f326954a 100644 --- a/js/src/gc/Iteration.cpp +++ b/js/src/gc/Iteration.cpp @@ -95,21 +95,19 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment, void* data, IterateScriptCallback scriptCallback) { MOZ_ASSERT(!rt->mainThread.suppressGC); - rt->gc.evictNursery(); - MOZ_ASSERT(rt->gc.nursery.isEmpty()); - + AutoEmptyNursery empty(rt); AutoPrepareForTracing prep(rt, SkipAtoms); if (compartment) { - for (ZoneCellIterUnderGC i(compartment->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + Zone* zone = compartment->zone(); + for (auto script = zone->cellIter(empty); !script.done(); script.next()) { if (script->compartment() == compartment) scriptCallback(rt, data, script); } } else { for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { - for (ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) - scriptCallback(rt, data, i.get()); + for (auto script = zone->cellIter(empty); !script.done(); script.next()) + scriptCallback(rt, data, script); } } } @@ -117,14 +115,14 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment, void js::IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data) { - zone->runtimeFromMainThread()->gc.evictNursery(); - AutoPrepareForTracing prep(zone->runtimeFromMainThread(), SkipAtoms); + JSRuntime* rt = zone->runtimeFromMainThread(); + AutoEmptyNursery empty(rt); + AutoPrepareForTracing prep(rt, SkipAtoms); for (auto thingKind : ObjectAllocKinds()) { - for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) { - JSObject* obj = i.get(); + for (auto obj = zone->cellIter(thingKind, empty); !obj.done(); obj.next()) { if (obj->asTenured().isMarked(GRAY)) - cellCallback(data, JS::GCCellPtr(obj)); + cellCallback(data, JS::GCCellPtr(obj.get())); } } } diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 7dedbb8a8894..fbfd199f1763 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -761,7 +761,7 @@ Statistics::Statistics(JSRuntime* rt) maxPauseInInterval(0), phaseNestingDepth(0), activeDagSlot(PHASE_DAG_NONE), - suspendedPhaseNestingDepth(0), + suspended(0), sliceCallback(nullptr), nurseryCollectionCallback(nullptr), aborted(false) @@ -1069,7 +1069,7 @@ Statistics::startTimingMutator() return false; } - MOZ_ASSERT(suspendedPhaseNestingDepth == 0); + MOZ_ASSERT(suspended == 0); timedGCTime = 0; phaseStartTimes[PHASE_MUTATOR] = 0; @@ -1094,6 +1094,35 @@ Statistics::stopTimingMutator(double& mutator_ms, double& gc_ms) return true; } +void +Statistics::suspendPhases(Phase suspension) +{ + MOZ_ASSERT(suspension == PHASE_EXPLICIT_SUSPENSION || suspension == PHASE_IMPLICIT_SUSPENSION); + while (phaseNestingDepth) { + MOZ_ASSERT(suspended < mozilla::ArrayLength(suspendedPhases)); + Phase parent = phaseNesting[phaseNestingDepth - 1]; + suspendedPhases[suspended++] = parent; + recordPhaseEnd(parent); + } + suspendedPhases[suspended++] = suspension; +} + +void +Statistics::resumePhases() +{ + Phase popped = suspendedPhases[--suspended]; + MOZ_ASSERT(popped == PHASE_EXPLICIT_SUSPENSION || popped == PHASE_IMPLICIT_SUSPENSION); + while (suspended && + suspendedPhases[suspended - 1] != PHASE_EXPLICIT_SUSPENSION && + suspendedPhases[suspended - 1] != PHASE_IMPLICIT_SUSPENSION) + { + Phase resumePhase = suspendedPhases[--suspended]; + if (resumePhase == PHASE_MUTATOR) + timedGCTime += PRMJ_Now() - timedGCStart; + beginPhase(resumePhase); + } +} + void Statistics::beginPhase(Phase phase) { @@ -1105,9 +1134,7 @@ Statistics::beginPhase(Phase phase) // // Reuse this mechanism for managing PHASE_MUTATOR. if (parent == PHASE_GC_BEGIN || parent == PHASE_GC_END || parent == PHASE_MUTATOR) { - MOZ_ASSERT(suspendedPhaseNestingDepth < mozilla::ArrayLength(suspendedPhases)); - suspendedPhases[suspendedPhaseNestingDepth++] = parent; - recordPhaseEnd(parent); + suspendPhases(PHASE_IMPLICIT_SUSPENSION); parent = phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : PHASE_NO_PARENT; } @@ -1154,12 +1181,8 @@ Statistics::endPhase(Phase phase) // When emptying the stack, we may need to resume a callback phase // (PHASE_GC_BEGIN/END) or return to timing the mutator (PHASE_MUTATOR). - if (phaseNestingDepth == 0 && suspendedPhaseNestingDepth > 0) { - Phase resumePhase = suspendedPhases[--suspendedPhaseNestingDepth]; - if (resumePhase == PHASE_MUTATOR) - timedGCTime += PRMJ_Now() - timedGCStart; - beginPhase(resumePhase); - } + if (phaseNestingDepth == 0 && suspended > 0 && suspendedPhases[suspended - 1] == PHASE_IMPLICIT_SUSPENSION) + resumePhases(); } void diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 5fa4748a94c6..4de504e226e7 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -86,6 +86,8 @@ enum Phase : uint8_t { PHASE_LIMIT, PHASE_NONE = PHASE_LIMIT, + PHASE_EXPLICIT_SUSPENSION = PHASE_LIMIT, + PHASE_IMPLICIT_SUSPENSION, PHASE_MULTI_PARENTS }; @@ -169,6 +171,22 @@ struct Statistics void endPhase(Phase phase); void endParallelPhase(Phase phase, const GCParallelTask* task); + // Occasionally, we may be in the middle of something that is tracked by + // this class, and we need to do something unusual (eg evict the nursery) + // that doesn't normally nest within the current phase. Suspend the + // currently tracked phase stack, at which time the caller is free to do + // other tracked operations. + // + // This also happens internally with PHASE_GC_BEGIN and other "non-GC" + // phases. While in these phases, any beginPhase will automatically suspend + // the non-GC phase, until that inner stack is complete, at which time it + // will automatically resume the non-GC phase. Explicit suspensions do not + // get auto-resumed. + void suspendPhases(Phase suspension = PHASE_EXPLICIT_SUSPENSION); + + // Resume a suspended stack of phases. + void resumePhases(); + void beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind, SliceBudget budget, JS::gcreason::Reason reason); void endSlice(); @@ -318,13 +336,14 @@ struct Statistics size_t activeDagSlot; /* - * To avoid recursive nesting, we discontinue a callback phase when any - * other phases are started. Remember what phase to resume when the inner - * phases are complete. (And because GCs can nest within the callbacks any - * number of times, we need a whole stack of of phases to resume.) + * Certain phases can interrupt the phase stack, eg callback phases. When + * this happens, we move the suspended phases over to a sepearate list, + * terminated by a dummy PHASE_SUSPENSION phase (so that we can nest + * suspensions by suspending multiple stacks with a PHASE_SUSPENSION in + * between). */ - Phase suspendedPhases[MAX_NESTING]; - size_t suspendedPhaseNestingDepth; + Phase suspendedPhases[MAX_NESTING * 3]; + size_t suspended; /* Sweep times for SCCs of compartments. */ Vector sccTimes; diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index 1f4c6dee3b37..19d80af1551d 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -153,13 +153,13 @@ Zone::sweepBreakpoints(FreeOp* fop) */ MOZ_ASSERT(isGCSweepingOrCompacting()); - for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto iter = cellIter(); !iter.done(); iter.next()) { + JSScript* script = iter; if (!script->hasAnyBreakpointsOrStepMode()) continue; bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script); - MOZ_ASSERT(script == i.get()); + MOZ_ASSERT(script == iter); for (unsigned i = 0; i < script->length(); i++) { BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i)); if (!site) @@ -207,10 +207,8 @@ Zone::discardJitCode(FreeOp* fop) #ifdef DEBUG /* Assert no baseline scripts are marked as active. */ - for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = cellIter(); !script.done(); script.next()) MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active()); - } #endif /* Mark baseline scripts on the stack as active. */ @@ -219,8 +217,7 @@ Zone::discardJitCode(FreeOp* fop) /* Only mark OSI points if code is being discarded. */ jit::InvalidateAll(fop, this); - for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = cellIter(); !script.done(); script.next()) { jit::FinishInvalidation(fop, script); /* diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 1d019e4a426a..17e53425be57 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -84,6 +84,9 @@ using UniqueIdMap = GCHashMap +class ZoneCellIter; + } // namespace gc } // namespace js @@ -157,6 +160,13 @@ struct Zone : public JS::shadow::Zone, onTooMuchMalloc(); } + // Iterate over all cells in the zone. See the definition of ZoneCellIter + // in jsgcinlines.h for the possible arguments and documentation. + template + js::gc::ZoneCellIter cellIter(Args... args) { + return js::gc::ZoneCellIter(const_cast(this), mozilla::Forward(args)...); + } + bool isTooMuchMalloc() const { return gcMallocBytes <= 0; } void onTooMuchMalloc(); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index d649a5fb9ec7..06529002441e 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -488,6 +488,7 @@ BaselineScript::Trace(JSTracer* trc, BaselineScript* script) void BaselineScript::Destroy(FreeOp* fop, BaselineScript* script) { + MOZ_ASSERT(!script->hasPendingIonBuilder()); script->unlinkDependentWasmModules(fop); @@ -1171,8 +1172,7 @@ jit::ToggleBaselineProfiling(JSRuntime* runtime, bool enable) return; for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { - for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (!script->hasBaselineScript()) continue; AutoWritableJitCode awjc(script->baselineScript()->method()); @@ -1186,8 +1186,7 @@ void jit::ToggleBaselineTraceLoggerScripts(JSRuntime* runtime, bool enable) { for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { - for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (!script->hasBaselineScript()) continue; script->baselineScript()->toggleTraceLoggerScripts(runtime, script, enable); @@ -1199,8 +1198,7 @@ void jit::ToggleBaselineTraceLoggerEngine(JSRuntime* runtime, bool enable) { for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { - for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (!script->hasBaselineScript()) continue; script->baselineScript()->toggleTraceLoggerEngine(enable); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index ff012116a5f2..78d616f5e1db 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -11,6 +11,7 @@ #include "mozilla/ThreadLocal.h" #include "jscompartment.h" +#include "jsgc.h" #include "jsprf.h" #include "gc/Marking.h" @@ -589,8 +590,8 @@ JitRuntime::Mark(JSTracer* trc, AutoLockForExclusiveAccess& lock) { MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting()); Zone* zone = trc->runtime()->atomsCompartment(lock)->zone(); - for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::JITCODE); !i.done(); i.next()) { - JitCode* code = i.get(); + for (auto i = zone->cellIter(); !i.done(); i.next()) { + JitCode* code = i; TraceRoot(trc, &code, "wrapper"); } } @@ -1351,8 +1352,7 @@ jit::ToggleBarriers(JS::Zone* zone, bool needs) if (!rt->hasJitRuntime()) return; - for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (script->hasIonScript()) script->ionScript()->toggleBarriers(needs); if (script->hasBaselineScript()) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index be7d6213e26c..537cfc4eb06c 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -984,8 +984,8 @@ AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, A // want to delazify in that case: this pointer is weak so the JSScript // could be destroyed at the next GC. - for (gc::ZoneCellIter i(cx->zone(), kind); !i.done(); i.next()) { - JSFunction* fun = &i.get()->as(); + for (auto i = cx->zone()->cellIter(kind); !i.done(); i.next()) { + JSFunction* fun = &i->as(); // Sweeping is incremental; take care to not delazify functions that // are about to be finalized. GC things referenced by objects that are @@ -1157,8 +1157,7 @@ JSCompartment::clearScriptCounts() void JSCompartment::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler) { - for (gc::ZoneCellIter i(zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone()->cellIter(); !script.done(); script.next()) { if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode()) script->clearBreakpointsIn(fop, dbg, handler); } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index d884798c079d..c37269f1b4a4 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2381,15 +2381,10 @@ GCRuntime::sweepTypesAfterCompacting(Zone* zone) AutoClearTypeInferenceStateOnOOM oom(zone); - for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) script->maybeSweepTypes(&oom); - } - - for (ZoneCellIterUnderGC i(zone, AllocKind::OBJECT_GROUP); !i.done(); i.next()) { - ObjectGroup* group = i.get(); + for (auto group = zone->cellIter(); !group.done(); group.next()) group->maybeSweep(&oom); - } zone->types.endSweep(rt); } @@ -3981,10 +3976,11 @@ GCRuntime::checkForCompartmentMismatches() return; CompartmentCheckTracer trc(rt); + AutoAssertEmptyNursery empty(rt); for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { trc.zone = zone; for (auto thingKind : AllAllocKinds()) { - for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) { + for (auto i = zone->cellIter(thingKind, empty); !i.done(); i.next()) { trc.src = i.getCell(); trc.srcKind = MapAllocToTraceKind(thingKind); trc.compartment = DispatchTraceKindTyped(MaybeCompartmentFunctor(), @@ -4003,9 +3999,10 @@ RelazifyFunctions(Zone* zone, AllocKind kind) kind == AllocKind::FUNCTION_EXTENDED); JSRuntime* rt = zone->runtimeFromMainThread(); + AutoAssertEmptyNursery empty(rt); - for (ZoneCellIterUnderGC i(zone, kind); !i.done(); i.next()) { - JSFunction* fun = &i.get()->as(); + for (auto i = zone->cellIter(kind, empty); !i.done(); i.next()) { + JSFunction* fun = &i->as(); if (fun->hasScript()) fun->maybeRelazify(rt); } @@ -6919,8 +6916,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) RootedObject targetStaticGlobalLexicalScope(rt); targetStaticGlobalLexicalScope = &target->maybeGlobal()->lexicalScope().staticBlock(); - for (ZoneCellIter iter(source->zone(), AllocKind::SCRIPT); !iter.done(); iter.next()) { - JSScript* script = iter.get(); + for (auto script = source->zone()->cellIter(); !script.done(); script.next()) { MOZ_ASSERT(script->compartment() == source); script->compartment_ = target; script->setTypesGeneration(target->zone()->types.generation); @@ -6952,14 +6948,12 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) } } - for (ZoneCellIter iter(source->zone(), AllocKind::BASE_SHAPE); !iter.done(); iter.next()) { - BaseShape* base = iter.get(); + for (auto base = source->zone()->cellIter(); !base.done(); base.next()) { MOZ_ASSERT(base->compartment() == source); base->compartment_ = target; } - for (ZoneCellIter iter(source->zone(), AllocKind::OBJECT_GROUP); !iter.done(); iter.next()) { - ObjectGroup* group = iter.get(); + for (auto group = source->zone()->cellIter(); !group.done(); group.next()) { group->setGeneration(target->zone()->types.generation); group->compartment_ = target; @@ -6981,8 +6975,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) // After fixing JSFunctions' compartments, we can fix LazyScripts' // enclosing scopes. - for (ZoneCellIter iter(source->zone(), AllocKind::LAZY_SCRIPT); !iter.done(); iter.next()) { - LazyScript* lazy = iter.get(); + for (auto lazy = source->zone()->cellIter(); !lazy.done(); lazy.next()) { MOZ_ASSERT(lazy->functionNonDelazifying()->compartment() == target); // See warning in handleParseWorkload. If we start optimizing global @@ -7116,12 +7109,9 @@ js::ReleaseAllJITCode(FreeOp* fop) void js::PurgeJITCaches(Zone* zone) { - for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); - - /* Discard Ion caches. */ + /* Discard Ion caches. */ + for (auto script = zone->cellIter(); !script.done(); script.next()) jit::PurgeCaches(script); - } } void @@ -7391,8 +7381,7 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt) for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { zone->checkUniqueIdTableAfterMovingGC(); - for (ZoneCellIterUnderGC i(zone, AllocKind::BASE_SHAPE); !i.done(); i.next()) { - BaseShape* baseShape = i.get(); + for (auto baseShape = zone->cellIter(); !baseShape.done(); baseShape.next()) { if (baseShape->hasTable()) baseShape->table().checkAfterMovingGC(); } @@ -7870,5 +7859,28 @@ StateName(State state) return names[state]; } +void +AutoAssertHeapBusy::checkCondition(JSRuntime *rt) +{ + this->rt = rt; + MOZ_ASSERT(rt->isHeapBusy()); +} + +void +AutoAssertEmptyNursery::checkCondition(JSRuntime *rt) { + this->rt = rt; + MOZ_ASSERT(rt->gc.nursery.isEmpty()); +} + +AutoEmptyNursery::AutoEmptyNursery(JSRuntime *rt) + : AutoAssertEmptyNursery() +{ + MOZ_ASSERT(!rt->mainThread.suppressGC); + rt->gc.stats.suspendPhases(); + rt->gc.evictNursery(); + rt->gc.stats.resumePhases(); + checkCondition(rt); +} + } /* namespace gc */ } /* namespace js */ diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 9e2a8b5a7aca..9ebc3c4ff249 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1328,6 +1328,95 @@ struct MOZ_RAII AutoAssertNoNurseryAlloc #endif }; +/* + * There are a couple of classes here that serve mostly as "tokens" indicating + * that a condition holds. Some functions force the caller to possess such a + * token because they would misbehave if the condition were false, and it is + * far more clear to make the condition visible at the point where it can be + * affected rather than just crashing in an assertion down in the place where + * it is relied upon. + */ + +/* + * Token meaning that the heap is busy and no allocations will be made. + * + * This class may be instantiated directly if it is known that the condition is + * already true, or it can be used as a base class for another RAII class that + * causes the condition to become true. Such base classes will use the no-arg + * constructor, establish the condition, then call checkCondition() to assert + * it and possibly record data needed to re-check the condition during + * destruction. + * + * Ordinarily, you would do something like this with a Maybe<> member that is + * emplaced during the constructor, but token-requiring functions want to + * require a reference to a base class instance. That said, you can always pass + * in the Maybe<> field as the token. + */ +class MOZ_RAII AutoAssertHeapBusy { + protected: + JSRuntime* rt; + + // Check that the heap really is busy, and record the rt for the check in + // the destructor. + void checkCondition(JSRuntime *rt); + + AutoAssertHeapBusy() : rt(nullptr) { + } + + public: + explicit AutoAssertHeapBusy(JSRuntime* rt) { + checkCondition(rt); + } + + ~AutoAssertHeapBusy() { + MOZ_ASSERT(rt); // checkCondition must always be called. + checkCondition(rt); + } +}; + +/* + * A class that serves as a token that the nursery is empty. It descends from + * AutoAssertHeapBusy, which means that it additionally requires the heap to be + * busy (which is not necessarily linked, but turns out to be true in practice + * for all users and simplifies the usage of these classes.) + */ +class MOZ_RAII AutoAssertEmptyNursery +{ + protected: + JSRuntime* rt; + + // Check that the nursery is empty. + void checkCondition(JSRuntime *rt); + + // For subclasses that need to empty the nursery in their constructors. + AutoAssertEmptyNursery() : rt(nullptr) { + } + + public: + explicit AutoAssertEmptyNursery(JSRuntime* rt) { + checkCondition(rt); + } + + ~AutoAssertEmptyNursery() { + checkCondition(rt); + } +}; + +/* + * Evict the nursery upon construction. Serves as a token indicating that the + * nursery is empty. (See AutoAssertEmptyNursery, above.) + * + * Note that this is very improper subclass of AutoAssertHeapBusy, in that the + * heap is *not* busy within the scope of an AutoEmptyNursery. I will most + * likely fix this by removing AutoAssertHeapBusy, but that is currently + * waiting on jonco's review. + */ +class MOZ_RAII AutoEmptyNursery : public AutoAssertEmptyNursery +{ + public: + explicit AutoEmptyNursery(JSRuntime *rt); +}; + const char* StateName(State state); diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index e9687ea71cc9..6b710a85f5bf 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -195,39 +195,71 @@ class ArenaCellIterUnderFinalize : public ArenaCellIterImpl explicit ArenaCellIterUnderFinalize(Arena* arena) : ArenaCellIterImpl(arena) {} }; -class ZoneCellIterImpl -{ +template +class ZoneCellIter; + +template <> +class ZoneCellIter { ArenaIter arenaIter; ArenaCellIterImpl cellIter; + JS::AutoAssertNoAlloc noAlloc; - public: - ZoneCellIterImpl(JS::Zone* zone, AllocKind kind) { + protected: + // For use when a subclass wants to insert some setup before init(). + ZoneCellIter() {} + + void init(JS::Zone* zone, AllocKind kind) { JSRuntime* rt = zone->runtimeFromAnyThread(); MOZ_ASSERT(zone); MOZ_ASSERT_IF(IsNurseryAllocable(kind), rt->gc.nursery.isEmpty()); + // If called from outside a GC, ensure that the heap is in a state + // that allows us to iterate. + if (!rt->isHeapBusy()) { + // Assert that no GCs can occur while a ZoneCellIter is live. + noAlloc.disallowAlloc(rt); + } + // We have a single-threaded runtime, so there's no need to protect // against other threads iterating or allocating. However, we do have // background finalization; we may have to wait for this to finish if // it's currently active. if (IsBackgroundFinalized(kind) && zone->arenas.needBackgroundFinalizeWait(kind)) rt->gc.waitBackgroundSweepEnd(); - arenaIter.init(zone, kind); if (!arenaIter.done()) cellIter.init(arenaIter.get()); } + public: + ZoneCellIter(JS::Zone* zone, AllocKind kind) { + // If we are iterating a nursery-allocated kind then we need to + // evict first so that we can see all things. + if (IsNurseryAllocable(kind)) { + JSRuntime* rt = zone->runtimeFromMainThread(); + rt->gc.evictNursery(); + } + + init(zone, kind); + } + + ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery&) { + // No need to evict the nursery. (This constructor is known statically + // to not GC.) + init(zone, kind); + } + bool done() const { return arenaIter.done(); } - template T* get() const { + template + T* get() const { MOZ_ASSERT(!done()); return cellIter.get(); } - Cell* getCell() const { + TenuredCell* getCell() const { MOZ_ASSERT(!done()); return cellIter.getCell(); } @@ -244,44 +276,74 @@ class ZoneCellIterImpl } }; -class ZoneCellIterUnderGC : public ZoneCellIterImpl -{ +// Iterator over the cells in a Zone, where the GC type (JSString, JSObject) is +// known, for a single AllocKind. Example usages: +// +// for (auto obj = zone->cellIter(AllocKind::OBJECT0); !obj.done(); obj.next()) +// ... +// +// for (auto script = zone->cellIter(); !script.done(); script.next()) +// f(script->code()); +// +// As this code demonstrates, you can use 'script' as if it were a JSScript*. +// Its actual type is ZoneCellIter, but for most purposes it will +// autoconvert to JSScript*. +// +// Note that in the JSScript case, ZoneCellIter is able to infer the AllocKind +// from the type 'JSScript', whereas in the JSObject case, the kind must be +// given (because there are multiple AllocKinds for objects). +// +// Also, the static rooting hazard analysis knows that the JSScript case will +// not GC during construction. The JSObject case needs to GC, or more precisely +// to empty the nursery and clear out the store buffer, so that it can see all +// objects to iterate over (the nursery is not iterable) and remove the +// possibility of having pointers from the store buffer to data hanging off +// stuff we're iterating over that we are going to delete. (The latter should +// not be a problem, since such instances should be using RelocatablePtr do +// remove themselves from the store buffer on deletion, but currently for +// subtle reasons that isn't good enough.) +// +// If the iterator is used within a GC, then there is no need to evict the +// nursery (again). You may select a variant that will skip the eviction either +// by specializing on a GCType that is never allocated in the nursery, or +// explicitly by passing in a trailing AutoAssertEmptyNursery argument. +// +template +class ZoneCellIter : public ZoneCellIter { public: - ZoneCellIterUnderGC(JS::Zone* zone, AllocKind kind) - : ZoneCellIterImpl(zone, kind) + // Non-nursery allocated (equivalent to having an entry in + // MapTypeToFinalizeKind). The template declaration here is to discard this + // constructor overload if MapTypeToFinalizeKind::kind does not + // exist. Note that there will be no remaining overloads that will work, + // which makes sense given that you haven't specified which of the + // AllocKinds to use for GCType. + // + // If we later add a nursery allocable GCType with a single AllocKind, we + // will want to add an overload of this constructor that does the right + // thing (ie, it empties the nursery before iterating.) + explicit ZoneCellIter(JS::Zone* zone) : ZoneCellIter() { + init(zone, MapTypeToFinalizeKind::kind); + } + + // Non-nursery allocated, nursery is known to be empty: same behavior as above. + ZoneCellIter(JS::Zone* zone, const js::gc::AutoAssertEmptyNursery&) : ZoneCellIter(zone) { + } + + // Arbitrary kind, which will be assumed to be nursery allocable (and + // therefore the nursery will be emptied before iterating.) + ZoneCellIter(JS::Zone* zone, AllocKind kind) : ZoneCellIter(zone, kind) { + } + + // Arbitrary kind, which will be assumed to be nursery allocable, but the + // nursery is known to be empty already: same behavior as non-nursery types. + ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery& empty) + : ZoneCellIter(zone, kind, empty) { - MOZ_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy()); - } -}; - -class ZoneCellIter -{ - mozilla::Maybe impl; - JS::AutoAssertNoAlloc noAlloc; - - public: - ZoneCellIter(JS::Zone* zone, AllocKind kind) { - // If called from outside a GC, ensure that the heap is in a state - // that allows us to iterate. - JSRuntime* rt = zone->runtimeFromMainThread(); - if (!rt->isHeapBusy()) { - // If we are iterating a nursery-allocated kind then we need to - // evict first so that we can see all things. - if (IsNurseryAllocable(kind)) - rt->gc.evictNursery(); - - // Assert that no GCs can occur while a ZoneCellIter is live. - noAlloc.disallowAlloc(rt); - } - - impl.emplace(zone, kind); } - bool done() const { return impl->done(); } - template - T* get() const { return impl->get(); } - Cell* getCell() const { return impl->getCell(); } - void next() { impl->next(); } + GCType* get() const { return ZoneCellIter::get(); } + operator GCType*() const { return get(); } + GCType* operator ->() const { return get(); } }; class GCZonesIter diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 4514a3b73ca4..aee66c49f59a 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -201,8 +201,9 @@ js::DumpPCCounts(JSContext* cx, HandleScript script, Sprinter* sp) void js::DumpCompartmentPCCounts(JSContext* cx) { - for (ZoneCellIter i(cx->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) { - RootedScript script(cx, i.get()); + RootedScript script(cx); + for (auto iter = cx->zone()->cellIter(); !iter.done(); iter.next()) { + script = iter; if (script->compartment() != cx->compartment()) continue; @@ -1657,8 +1658,7 @@ js::StopPCCountProfiling(JSContext* cx) return; for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { - for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (script->hasScriptCounts() && script->types()) { if (!vec->append(script)) return; @@ -2025,8 +2025,7 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out) } Rooted topScripts(cx, ScriptVector(cx)); for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { - for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (script->compartment() != comp || !script->isTopLevel() || !script->filename()) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index a97e9ef89a89..642e4e363bfd 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -2352,8 +2352,8 @@ UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone, return false; } } else { - for (gc::ZoneCellIter iter(zone, gc::AllocKind::SCRIPT); !iter.done(); iter.next()) { - JSScript* script = iter.get(); + for (auto iter = zone->cellIter(); !iter.done(); iter.next()) { + JSScript* script = iter; if (obs.shouldRecompileOrInvalidate(script) && !gc::IsAboutToBeFinalizedUnbarriered(&script)) { diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index 5a02869a35dd..c82c2ba82246 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -1271,8 +1271,6 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par LeaveParseTaskZone(rt, parseTask); { - gc::ZoneCellIter iter(parseTask->cx->zone(), gc::AllocKind::OBJECT_GROUP); - // Generator functions don't have Function.prototype as prototype but a // different function object, so the IdentifyStandardPrototype trick // below won't work. Just special-case it. @@ -1288,8 +1286,7 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par // to the corresponding prototype in the new compartment. This will briefly // create cross compartment pointers, which will be fixed by the // MergeCompartments call below. - for (; !iter.done(); iter.next()) { - ObjectGroup* group = iter.get(); + for (auto group = parseTask->cx->zone()->cellIter(); !group.done(); group.next()) { TaggedProto proto(group->proto()); if (!proto.isObject()) continue; diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index ffa472c6f538..5d33a0224a8d 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1049,7 +1049,7 @@ CallAddPropertyHookDense(ExclusiveContext* cx, HandleNativeObject obj, uint32_t } static bool -UpdateShapeTypeAndValue(ExclusiveContext* cx, NativeObject* obj, Shape* shape, const Value& value) +UpdateShapeTypeAndValue(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape, const Value& value) { jsid id = shape->propid(); if (shape->hasSlot()) { diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index fe6780b03f9f..c78e363fc74c 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -2545,16 +2545,15 @@ js::PrintTypes(JSContext* cx, JSCompartment* comp, bool force) if (!force && !InferSpewActive(ISpewResult)) return; - for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { - RootedScript script(cx, i.get()); + RootedScript script(cx); + for (auto iter = zone->cellIter(); !iter.done(); iter.next()) { + script = iter; if (script->types()) script->types()->printTypes(cx, script); } - for (gc::ZoneCellIter i(zone, gc::AllocKind::OBJECT_GROUP); !i.done(); i.next()) { - ObjectGroup* group = i.get(); + for (auto group = zone->cellIter(); !group.done(); group.next()) group->print(); - } #endif } @@ -4425,10 +4424,8 @@ TypeZone::endSweep(JSRuntime* rt) void TypeZone::clearAllNewScriptsOnOOM() { - for (gc::ZoneCellIter iter(zone(), gc::AllocKind::OBJECT_GROUP); - !iter.done(); iter.next()) - { - ObjectGroup* group = iter.get(); + for (auto iter = zone()->cellIter(); !iter.done(); iter.next()) { + ObjectGroup* group = iter; if (!IsAboutToBeFinalizedUnbarriered(&group)) group->maybeClearNewScriptOnOOM(); } @@ -4437,8 +4434,9 @@ TypeZone::clearAllNewScriptsOnOOM() AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM() { if (oom) { + JSRuntime* rt = zone->runtimeFromMainThread(); zone->setPreservingCode(false); - zone->discardJitCode(zone->runtimeFromMainThread()->defaultFreeOp()); + zone->discardJitCode(rt->defaultFreeOp()); zone->types.clearAllNewScriptsOnOOM(); } } From db844b8bcb678ed48189f1d20f4b6a32c1c09750 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Tue, 31 May 2016 18:54:41 +0200 Subject: [PATCH 089/199] Backed out changeset 830cccc16bd9 (bug 882718) --- dom/html/TextTrackManager.cpp | 7 +------ dom/html/TextTrackManager.h | 35 ----------------------------------- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/dom/html/TextTrackManager.cpp b/dom/html/TextTrackManager.cpp index e232c817c10f..773dbc5236ca 100644 --- a/dom/html/TextTrackManager.cpp +++ b/dom/html/TextTrackManager.cpp @@ -22,8 +22,6 @@ namespace mozilla { namespace dom { -NS_IMPL_ISUPPORTS(TextTrackManager::ShutdownObserverProxy, nsIObserver); - CompareTextTracks::CompareTextTracks(HTMLMediaElement* aMediaElement) { mMediaElement = aMediaElement; @@ -96,7 +94,6 @@ TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement) , mLastTimeMarchesOnCalled(0.0) , mTimeMarchesOnDispatched(false) , performedTrackSelection(false) - , mShutdown(false) { nsISupports* parentObject = mMediaElement->OwnerDoc()->GetParentObject(); @@ -115,12 +112,10 @@ TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement) sParserWrapper = parserWrapper; ClearOnShutdown(&sParserWrapper); } - mShutdownProxy = new ShutdownObserverProxy(this); } TextTrackManager::~TextTrackManager() { - nsContentUtils::UnregisterShutdownObserver(mShutdownProxy); } TextTrackList* @@ -517,7 +512,7 @@ TextTrackManager::DispatchTimeMarchesOn() // enqueue the current playback position and whether only that changed // through its usual monotonic increase during normal playback; current // executing call upon completion will check queue for further 'work'. - if (!mTimeMarchesOnDispatched && !mShutdown) { + if (!mTimeMarchesOnDispatched) { NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::TimeMarchesOn)); mTimeMarchesOnDispatched = true; } diff --git a/dom/html/TextTrackManager.h b/dom/html/TextTrackManager.h index 97cee41abfc7..b67a819e3166 100644 --- a/dom/html/TextTrackManager.h +++ b/dom/html/TextTrackManager.h @@ -11,7 +11,6 @@ #include "mozilla/dom/TextTrackList.h" #include "mozilla/dom/TextTrackCueList.h" #include "mozilla/StaticPtr.h" -#include "nsContentUtils.h" class nsIWebVTTParserWrapper; @@ -98,11 +97,6 @@ public: void DispatchTimeMarchesOn(); - void NotifyShutdown() - { - mShutdown = true; - } - private: void TimeMarchesOn(); @@ -141,35 +135,6 @@ private: void GetTextTracksOfKind(TextTrackKind aTextTrackKind, nsTArray& aTextTracks); bool TrackIsDefault(TextTrack* aTextTrack); - - class ShutdownObserverProxy final : public nsIObserver - { - NS_DECL_ISUPPORTS - - public: - explicit ShutdownObserverProxy(TextTrackManager* aManager) - : mManager(aManager) - { - nsContentUtils::RegisterShutdownObserver(this); - } - - NS_IMETHODIMP Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) override - { - MOZ_ASSERT(NS_IsMainThread()); - if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { - nsContentUtils::UnregisterShutdownObserver(this); - mManager->NotifyShutdown(); - } - return NS_OK; - } - - private: - ~ShutdownObserverProxy() {}; - TextTrackManager* mManager; - }; - - RefPtr mShutdownProxy; - bool mShutdown; }; } // namespace dom From de05b7902efb1372613a99d84f417c5569ba3ce8 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Tue, 31 May 2016 18:54:41 +0200 Subject: [PATCH 090/199] Backed out changeset 883bfabfde46 (bug 882718) --- dom/html/HTMLMediaElement.cpp | 6 +++--- dom/html/TextTrackManager.cpp | 19 +++++++------------ dom/media/TextTrack.cpp | 15 +++++++++++++++ dom/media/TextTrackCueList.cpp | 6 ------ dom/media/TextTrackCueList.h | 1 - dom/media/test/test_texttrackcue.html | 4 ---- 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 88c494ffd01c..61a6339e5846 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -3740,13 +3740,13 @@ void HTMLMediaElement::SeekCompleted() { mPlayingBeforeSeek = false; SetPlayedOrSeeked(true); - if (mTextTrackManager) { - mTextTrackManager->DidSeek(); - } FireTimeUpdate(false); DispatchAsyncEvent(NS_LITERAL_STRING("seeked")); // We changed whether we're seeking so we need to AddRemoveSelfReference AddRemoveSelfReference(); + if (mTextTrackManager) { + mTextTrackManager->DidSeek(); + } if (mCurrentPlayRangeStart == -1.0) { mCurrentPlayRangeStart = CurrentTime(); } diff --git a/dom/html/TextTrackManager.cpp b/dom/html/TextTrackManager.cpp index 773dbc5236ca..44ea8e6a36d6 100644 --- a/dom/html/TextTrackManager.cpp +++ b/dom/html/TextTrackManager.cpp @@ -193,7 +193,8 @@ TextTrackManager::RemoveTextTrack(TextTrack* aTextTrack, bool aPendingListOnly) TextTrackCueList* removeCueList = aTextTrack->GetCues(); if (removeCueList) { for (uint32_t i = 0; i < removeCueList->Length(); ++i) { - mNewCues->RemoveCue(*((*removeCueList)[i])); + ErrorResult dummyRv; + mNewCues->RemoveCue(*((*removeCueList)[i]), dummyRv); } DispatchTimeMarchesOn(); } @@ -205,9 +206,6 @@ TextTrackManager::DidSeek() if (mTextTracks) { mTextTracks->DidSeek(); } - if (mMediaElement) { - mLastTimeMarchesOnCalled = mMediaElement->CurrentTime(); - } mHasSeeked = true; } @@ -266,7 +264,8 @@ void TextTrackManager::NotifyCueRemoved(TextTrackCue& aCue) { if (mNewCues) { - mNewCues->RemoveCue(aCue); + ErrorResult dummyRv; + mNewCues->RemoveCue(aCue, dummyRv); } DispatchTimeMarchesOn(); } @@ -533,8 +532,7 @@ TextTrackManager::TimeMarchesOn() } nsCOMPtr window = do_QueryInterface(parentObject); - if (mMediaElement && - (!(mMediaElement->GetPlayedOrSeeked())|| mMediaElement->Seeking())) { + if (mMediaElement && !(mMediaElement->GetPlayedOrSeeked())) { return; } @@ -572,7 +570,8 @@ TextTrackManager::TimeMarchesOn() } for (uint32_t i = 0; i < currentCues->Length(); ++i) { TextTrackCue* cue = (*currentCues)[i]; - otherCues->RemoveCue(*cue); + ErrorResult dummy; + otherCues->RemoveCue(*cue, dummy); } // Step 4. @@ -697,10 +696,6 @@ TextTrackManager::TimeMarchesOn() TextTrack* ttrack = affectedTracks[i]; if (ttrack) { ttrack->DispatchTrustedEvent(NS_LITERAL_STRING("cuechange")); - HTMLTrackElement* trackElement = ttrack->GetTrackElement(); - if (trackElement) { - trackElement->DispatchTrackRunnable(NS_LITERAL_STRING("cuechange")); - } } } diff --git a/dom/media/TextTrack.cpp b/dom/media/TextTrack.cpp index 1eb21ccf832d..26834089113c 100644 --- a/dom/media/TextTrack.cpp +++ b/dom/media/TextTrack.cpp @@ -160,6 +160,10 @@ TextTrack::UpdateActiveCueList() return; } + // Flag that indicates whether or not this call of UpdateActiveCueList has + // changed the activeCueList. + bool hasChanged = false; + // If we are dirty, i.e. an event happened that may cause the sorted mCueList // to have changed like a seek or an insert for a cue, than we need to rebuild // the active cue list from scratch. @@ -175,6 +179,7 @@ TextTrack::UpdateActiveCueList() for (uint32_t i = mActiveCueList->Length(); i > 0; i--) { if ((*mActiveCueList)[i - 1]->EndTime() < playbackTime) { mActiveCueList->RemoveCueAt(i - 1); + hasChanged = true; } } // Add all the cues, starting from the position of the last cue that was @@ -185,6 +190,16 @@ TextTrack::UpdateActiveCueList() (*mCueList)[mCuePos]->StartTime() <= playbackTime; mCuePos++) { if ((*mCueList)[mCuePos]->EndTime() >= playbackTime) { mActiveCueList->AddCue(*(*mCueList)[mCuePos]); + hasChanged = true; + } + } + + if (hasChanged) { + RefPtr asyncDispatcher = + new AsyncEventDispatcher(this, NS_LITERAL_STRING("cuechange"), false); + asyncDispatcher->PostDOMEvent(); + if (mTrackElement) { + mTrackElement->DispatchTrackRunnable(NS_LITERAL_STRING("cuechange")); } } } diff --git a/dom/media/TextTrackCueList.cpp b/dom/media/TextTrackCueList.cpp index f3fe66662adb..e3f98d38d7c1 100644 --- a/dom/media/TextTrackCueList.cpp +++ b/dom/media/TextTrackCueList.cpp @@ -103,12 +103,6 @@ TextTrackCueList::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv) mList.RemoveElement(&aCue); } -void -TextTrackCueList::RemoveCue(TextTrackCue& aCue) -{ - mList.RemoveElement(&aCue); -} - void TextTrackCueList::RemoveCueAt(uint32_t aIndex) { diff --git a/dom/media/TextTrackCueList.h b/dom/media/TextTrackCueList.h index 8a77045f76e6..aa88f3c062cb 100644 --- a/dom/media/TextTrackCueList.h +++ b/dom/media/TextTrackCueList.h @@ -50,7 +50,6 @@ public: // from the end of the current array should be more efficient than a general // sort step after all cues are loaded. void AddCue(TextTrackCue& aCue); - void RemoveCue(TextTrackCue& aCue); void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv); void RemoveCueAt(uint32_t aIndex); void RemoveAll(); diff --git a/dom/media/test/test_texttrackcue.html b/dom/media/test/test_texttrackcue.html index 08a2016ec540..29fc9b3cf869 100644 --- a/dom/media/test/test_texttrackcue.html +++ b/dom/media/test/test_texttrackcue.html @@ -83,10 +83,6 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]}, is(cue.endTime, 0.71, "Cue's end time should be 0.71."); cue.pauseOnExit = true; is(cue.pauseOnExit, true, "Cue's pause on exit flag should be true."); - video.addEventListener("pause", function pauseOnExit() { - video.removeEventListener("pause", pauseOnExit, false); - video.play(); - }); var exceptionHappened; function checkPercentageValue(prop) { From 200b6282f26c6e476bbca0a278dc6a57370fd031 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Tue, 31 May 2016 18:54:41 +0200 Subject: [PATCH 091/199] Backed out changeset 7f61a6bd8a3d (bug 882718) --- dom/html/HTMLMediaElement.h | 5 ----- dom/html/TextTrackManager.cpp | 30 ------------------------------ dom/html/TextTrackManager.h | 2 +- dom/media/TextTrack.cpp | 4 ---- 4 files changed, 1 insertion(+), 40 deletions(-) diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index cda703eafe8e..f514419d67cb 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -699,11 +699,6 @@ public: mTextTrackManager->AddCue(aCue); } } - void NotifyCueRemoved(TextTrackCue& aCue) { - if (mTextTrackManager) { - mTextTrackManager->NotifyCueRemoved(aCue); - } - } /** * A public wrapper for FinishDecoderSetup() diff --git a/dom/html/TextTrackManager.cpp b/dom/html/TextTrackManager.cpp index 44ea8e6a36d6..fed128d30a04 100644 --- a/dom/html/TextTrackManager.cpp +++ b/dom/html/TextTrackManager.cpp @@ -172,7 +172,6 @@ TextTrackManager::AddCues(TextTrack* aTextTrack) for (uint32_t i = 0; i < cueList->Length(); ++i) { mNewCues->AddCue(*cueList->IndexedGetter(i, dummy)); } - DispatchTimeMarchesOn(); } } @@ -189,15 +188,6 @@ TextTrackManager::RemoveTextTrack(TextTrack* aTextTrack, bool aPendingListOnly) } mTextTracks->RemoveTextTrack(aTextTrack); - // Remove the cues in mNewCues belong to aTextTrack. - TextTrackCueList* removeCueList = aTextTrack->GetCues(); - if (removeCueList) { - for (uint32_t i = 0; i < removeCueList->Length(); ++i) { - ErrorResult dummyRv; - mNewCues->RemoveCue(*((*removeCueList)[i]), dummyRv); - } - DispatchTimeMarchesOn(); - } } void @@ -245,10 +235,6 @@ TextTrackManager::UpdateCueDisplay() } else if (overlay->Length() > 0) { nsContentUtils::SetNodeTextContent(overlay, EmptyString(), true); } - // Call TimeMarchesOn() directly instead DispatchTimeMarchesOn() - // because we had render the new cue, so we must run - // TimeMarchesOn immediately. - TimeMarchesOn(); } void @@ -257,17 +243,6 @@ TextTrackManager::AddCue(TextTrackCue& aCue) if (mNewCues) { mNewCues->AddCue(aCue); } - DispatchTimeMarchesOn(); -} - -void -TextTrackManager::NotifyCueRemoved(TextTrackCue& aCue) -{ - if (mNewCues) { - ErrorResult dummyRv; - mNewCues->RemoveCue(aCue, dummyRv); - } - DispatchTimeMarchesOn(); } void @@ -532,10 +507,6 @@ TextTrackManager::TimeMarchesOn() } nsCOMPtr window = do_QueryInterface(parentObject); - if (mMediaElement && !(mMediaElement->GetPlayedOrSeeked())) { - return; - } - // Step 3. double currentPlaybackTime = mMediaElement->CurrentTime(); bool hasNormalPlayback = !mHasSeeked; @@ -550,7 +521,6 @@ TextTrackManager::TimeMarchesOn() for (uint32_t index = 0; index < mTextTracks->Length(); ++index) { TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy); if (ttrack && dummy) { - // TODO: call GetCueListByTimeInterval on mNewCues? TextTrackCueList* activeCueList = ttrack->GetActiveCues(); if (activeCueList) { for (uint32_t i = 0; i < activeCueList->Length(); ++i) { diff --git a/dom/html/TextTrackManager.h b/dom/html/TextTrackManager.h index b67a819e3166..e6e8d94289c6 100644 --- a/dom/html/TextTrackManager.h +++ b/dom/html/TextTrackManager.h @@ -57,7 +57,7 @@ public: void AddCue(TextTrackCue& aCue); void AddCues(TextTrack* aTextTrack); - void NotifyCueRemoved(TextTrackCue& aCue); + /** * Overview of WebVTT cuetext and anonymous content setup. * diff --git a/dom/media/TextTrack.cpp b/dom/media/TextTrack.cpp index 26834089113c..6dded2583ff7 100644 --- a/dom/media/TextTrack.cpp +++ b/dom/media/TextTrack.cpp @@ -133,10 +133,6 @@ TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv) aCue.SetActive(false); mCueList->RemoveCue(aCue, aRv); - HTMLMediaElement* mediaElement = mTextTrackList->GetMediaElement(); - if (mediaElement) { - mediaElement->NotifyCueRemoved(aCue); - } SetDirty(); } From 33dc9fca70bba7a5a27b631be960252b053831a9 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Tue, 31 May 2016 18:54:41 +0200 Subject: [PATCH 092/199] Backed out changeset f6363fdbbf29 (bug 882718) --- dom/html/TextTrackManager.cpp | 295 +-------------------------------- dom/html/TextTrackManager.h | 19 +-- dom/media/TextTrackCueList.cpp | 7 - dom/media/TextTrackCueList.h | 2 +- 4 files changed, 3 insertions(+), 320 deletions(-) diff --git a/dom/html/TextTrackManager.cpp b/dom/html/TextTrackManager.cpp index fed128d30a04..e86490287be7 100644 --- a/dom/html/TextTrackManager.cpp +++ b/dom/html/TextTrackManager.cpp @@ -76,8 +76,7 @@ CompareTextTracks::LessThan(TextTrack* aOne, TextTrack* aTwo) const } NS_IMPL_CYCLE_COLLECTION(TextTrackManager, mMediaElement, mTextTracks, - mPendingTextTracks, mNewCues, - mLastActiveCues) + mPendingTextTracks, mNewCues) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextTrackManager) NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) @@ -90,9 +89,6 @@ StaticRefPtr TextTrackManager::sParserWrapper; TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement) : mMediaElement(aMediaElement) - , mHasSeeked(false) - , mLastTimeMarchesOnCalled(0.0) - , mTimeMarchesOnDispatched(false) , performedTrackSelection(false) { nsISupports* parentObject = @@ -102,7 +98,6 @@ TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement) nsCOMPtr window = do_QueryInterface(parentObject); mNewCues = new TextTrackCueList(window); - mLastActiveCues = new TextTrackCueList(window); mTextTracks = new TextTrackList(window, this); mPendingTextTracks = new TextTrackList(window, this); @@ -196,7 +191,6 @@ TextTrackManager::DidSeek() if (mTextTracks) { mTextTracks->DidSeek(); } - mHasSeeked = true; } void @@ -386,292 +380,5 @@ TextTrackManager::HandleEvent(nsIDOMEvent* aEvent) return NS_OK; } - -class SimpleTextTrackEvent : public Runnable -{ -public: - friend class CompareSimpleTextTrackEvents; - SimpleTextTrackEvent(const nsAString& aEventName, double aTime, - TextTrack* aTrack, TextTrackCue* aCue) - : mName(aEventName), - mTime(aTime), - mTrack(aTrack), - mCue(aCue) - {} - - NS_IMETHOD Run() { - mCue->DispatchTrustedEvent(mName); - return NS_OK; - } - -private: - nsString mName; - double mTime; - TextTrack* mTrack; - RefPtr mCue; -}; - -class CompareSimpleTextTrackEvents { -private: - int32_t TrackChildPosition(SimpleTextTrackEvent* aEvent) const - { - HTMLTrackElement* trackElement = aEvent->mTrack->GetTrackElement();; - if (!trackElement) { - return -1; - } - return mMediaElement->IndexOf(trackElement); - } - HTMLMediaElement* mMediaElement; -public: - explicit CompareSimpleTextTrackEvents(HTMLMediaElement* aMediaElement) - { - mMediaElement = aMediaElement; - } - - bool Equals(SimpleTextTrackEvent* aOne, SimpleTextTrackEvent* aTwo) const - { - return false; - } - - bool LessThan(SimpleTextTrackEvent* aOne, SimpleTextTrackEvent* aTwo) const - { - if (aOne->mTime < aTwo->mTime) { - return true; - } else if (aOne->mTime > aTwo->mTime) { - return false; - } - - int32_t positionOne = TrackChildPosition(aOne); - int32_t positionTwo = TrackChildPosition(aTwo); - if (positionOne < positionTwo) { - return true; - } else if (positionOne > positionTwo) { - return false; - } - - if (aOne->mName.EqualsLiteral("enter") || - aTwo->mName.EqualsLiteral("exit")) { - return true; - } - return false; - } -}; - -class TextTrackListInternal -{ -public: - void AddTextTrack(TextTrack* aTextTrack, - const CompareTextTracks& aCompareTT) - { - if (!mTextTracks.Contains(aTextTrack)) { - mTextTracks.InsertElementSorted(aTextTrack, aCompareTT); - } - } - uint32_t Length() const - { - return mTextTracks.Length(); - } - TextTrack* operator[](uint32_t aIndex) - { - return mTextTracks.SafeElementAt(aIndex, nullptr); - } -private: - nsTArray> mTextTracks; -}; - -void -TextTrackManager::DispatchTimeMarchesOn() -{ - // Run the algorithm if no previous instance is still running, otherwise - // enqueue the current playback position and whether only that changed - // through its usual monotonic increase during normal playback; current - // executing call upon completion will check queue for further 'work'. - if (!mTimeMarchesOnDispatched) { - NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::TimeMarchesOn)); - mTimeMarchesOnDispatched = true; - } -} - -// https://html.spec.whatwg.org/multipage/embedded-content.html#time-marches-on -void -TextTrackManager::TimeMarchesOn() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - mTimeMarchesOnDispatched = false; - - nsISupports* parentObject = - mMediaElement->OwnerDoc()->GetParentObject(); - if (NS_WARN_IF(!parentObject)) { - return; - } - nsCOMPtr window = do_QueryInterface(parentObject); - - // Step 3. - double currentPlaybackTime = mMediaElement->CurrentTime(); - bool hasNormalPlayback = !mHasSeeked; - mHasSeeked = false; - - // Step 1, 2. - RefPtr currentCues = - new TextTrackCueList(window); - RefPtr otherCues = - new TextTrackCueList(window); - bool dummy; - for (uint32_t index = 0; index < mTextTracks->Length(); ++index) { - TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy); - if (ttrack && dummy) { - TextTrackCueList* activeCueList = ttrack->GetActiveCues(); - if (activeCueList) { - for (uint32_t i = 0; i < activeCueList->Length(); ++i) { - currentCues->AddCue(*((*activeCueList)[i])); - } - } - } - } - // Populate otherCues with 'non-active" cues. - if (hasNormalPlayback) { - media::Interval interval(mLastTimeMarchesOnCalled, - currentPlaybackTime); - otherCues = mNewCues->GetCueListByTimeInterval(interval);; - } else { - // Seek case. Put the mLastActiveCues into otherCues. - otherCues = mLastActiveCues; - } - for (uint32_t i = 0; i < currentCues->Length(); ++i) { - TextTrackCue* cue = (*currentCues)[i]; - ErrorResult dummy; - otherCues->RemoveCue(*cue, dummy); - } - - // Step 4. - RefPtr missedCues = new TextTrackCueList(window); - if (hasNormalPlayback) { - for (uint32_t i = 0; i < otherCues->Length(); ++i) { - TextTrackCue* cue = (*otherCues)[i]; - if (cue->StartTime() >= mLastTimeMarchesOnCalled && - cue->EndTime() <= currentPlaybackTime) { - missedCues->AddCue(*cue); - } - } - } - - // Step 5. Empty now. - // TODO: Step 6: fire timeupdate? - - // Step 7. Abort steps if condition 1, 2, 3 are satisfied. - // 1. All of the cues in current cues have their active flag set. - // 2. None of the cues in other cues have their active flag set. - // 3. Missed cues is empty. - bool c1 = true; - for (uint32_t i = 0; i < currentCues->Length(); ++i) { - if (!(*currentCues)[i]->GetActive()) { - c1 = false; - break; - } - } - bool c2 = true; - for (uint32_t i = 0; i < otherCues->Length(); ++i) { - if ((*otherCues)[i]->GetActive()) { - c2 = false; - break; - } - } - bool c3 = (missedCues->Length() == 0); - if (c1 && c2 && c3) { - mLastTimeMarchesOnCalled = currentPlaybackTime; - return; - } - - // Step 8. Respect PauseOnExit flag if not seek. - if (hasNormalPlayback) { - for (uint32_t i = 0; i < otherCues->Length(); ++i) { - TextTrackCue* cue = (*otherCues)[i]; - if (cue && cue->PauseOnExit() && cue->GetActive()) { - mMediaElement->Pause(); - break; - } - } - for (uint32_t i = 0; i < missedCues->Length(); ++i) { - TextTrackCue* cue = (*missedCues)[i]; - if (cue && cue->PauseOnExit()) { - mMediaElement->Pause(); - break; - } - } - } - - // Step 15. - // Sort text tracks in the same order as the text tracks appear - // in the media element's list of text tracks, and remove - // duplicates. - TextTrackListInternal affectedTracks; - // Step 13, 14. - nsTArray> eventList; - // Step 9, 10. - // For each text track cue in missed cues, prepare an event named - // enter for the TextTrackCue object with the cue start time. - for (uint32_t i = 0; i < missedCues->Length(); ++i) { - TextTrackCue* cue = (*missedCues)[i]; - if (cue) { - SimpleTextTrackEvent* event = - new SimpleTextTrackEvent(NS_LITERAL_STRING("enter"), - cue->StartTime(), cue->GetTrack(), - cue); - eventList.InsertElementSorted(event, - CompareSimpleTextTrackEvents(mMediaElement)); - affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement)); - } - } - - // Step 11, 17. - for (uint32_t i = 0; i < otherCues->Length(); ++i) { - TextTrackCue* cue = (*otherCues)[i]; - if (cue->GetActive() || - missedCues->GetCueById(cue->Id()) != nullptr) { - double time = cue->StartTime() > cue->EndTime() ? cue->StartTime() - : cue->EndTime(); - SimpleTextTrackEvent* event = - new SimpleTextTrackEvent(NS_LITERAL_STRING("exit"), time, - cue->GetTrack(), cue); - eventList.InsertElementSorted(event, - CompareSimpleTextTrackEvents(mMediaElement)); - affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement)); - } - cue->SetActive(false); - } - - // Step 12, 17. - for (uint32_t i = 0; i < currentCues->Length(); ++i) { - TextTrackCue* cue = (*currentCues)[i]; - if (!cue->GetActive()) { - SimpleTextTrackEvent* event = - new SimpleTextTrackEvent(NS_LITERAL_STRING("enter"), - cue->StartTime(), cue->GetTrack(), - cue); - eventList.InsertElementSorted(event, - CompareSimpleTextTrackEvents(mMediaElement)); - affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement)); - } - cue->SetActive(true); - } - - // Fire the eventList - for (uint32_t i = 0; i < eventList.Length(); ++i) { - NS_DispatchToMainThread(eventList[i].forget()); - } - - // Step 16. - for (uint32_t i = 0; i < affectedTracks.Length(); ++i) { - TextTrack* ttrack = affectedTracks[i]; - if (ttrack) { - ttrack->DispatchTrustedEvent(NS_LITERAL_STRING("cuechange")); - } - } - - mLastTimeMarchesOnCalled = currentPlaybackTime; - mLastActiveCues = currentCues; -} - } // namespace dom } // namespace mozilla diff --git a/dom/html/TextTrackManager.h b/dom/html/TextTrackManager.h index e6e8d94289c6..40e585fb7b2d 100644 --- a/dom/html/TextTrackManager.h +++ b/dom/html/TextTrackManager.h @@ -22,9 +22,9 @@ class HTMLMediaElement; class CompareTextTracks { private: HTMLMediaElement* mMediaElement; - int32_t TrackChildPosition(TextTrack* aTrack) const; public: explicit CompareTextTracks(HTMLMediaElement* aMediaElement); + int32_t TrackChildPosition(TextTrack* aTrack) const; bool Equals(TextTrack* aOne, TextTrack* aTwo) const; bool LessThan(TextTrack* aOne, TextTrack* aTwo) const; }; @@ -94,30 +94,13 @@ public: // The HTMLMediaElement that this TextTrackManager manages the TextTracks of. RefPtr mMediaElement; - - void DispatchTimeMarchesOn(); - private: - void TimeMarchesOn(); - // List of the TextTrackManager's owning HTMLMediaElement's TextTracks. RefPtr mTextTracks; // List of text track objects awaiting loading. RefPtr mPendingTextTracks; // List of newly introduced Text Track cues. - - // Contain all cues for a MediaElement. RefPtr mNewCues; - // The active cues for the last TimeMarchesOn iteration. - RefPtr mLastActiveCues; - - // True if the media player playback changed due to seeking prior to and - // during running the "Time Marches On" algorithm. - bool mHasSeeked; - // Playback position at the time of last "Time Marches On" call - double mLastTimeMarchesOnCalled; - - bool mTimeMarchesOnDispatched; static StaticRefPtr sParserWrapper; diff --git a/dom/media/TextTrackCueList.cpp b/dom/media/TextTrackCueList.cpp index e3f98d38d7c1..655ebf7065e7 100644 --- a/dom/media/TextTrackCueList.cpp +++ b/dom/media/TextTrackCueList.cpp @@ -62,13 +62,6 @@ TextTrackCueList::operator[](uint32_t aIndex) return mList.SafeElementAt(aIndex, nullptr); } -TextTrackCueList& -TextTrackCueList::operator=(const TextTrackCueList& aOther) -{ - mList = aOther.mList; - return *this; -} - TextTrackCue* TextTrackCueList::GetCueById(const nsAString& aId) { diff --git a/dom/media/TextTrackCueList.h b/dom/media/TextTrackCueList.h index aa88f3c062cb..6ae019c8daac 100644 --- a/dom/media/TextTrackCueList.h +++ b/dom/media/TextTrackCueList.h @@ -44,7 +44,7 @@ public: TextTrackCue* IndexedGetter(uint32_t aIndex, bool& aFound); TextTrackCue* operator[](uint32_t aIndex); TextTrackCue* GetCueById(const nsAString& aId); - TextTrackCueList& operator=(const TextTrackCueList& aOther); + // Adds a cue to mList by performing an insertion sort on mList. // We expect most files to already be sorted, so an insertion sort starting // from the end of the current array should be more efficient than a general From c13a947fcf9fed47773b6088af643566eebfeaf6 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Tue, 31 May 2016 18:54:41 +0200 Subject: [PATCH 093/199] Backed out changeset f2bf27c13c37 (bug 882718) --- dom/media/TextTrackCueList.cpp | 14 -------------- dom/media/TextTrackCueList.h | 4 ---- 2 files changed, 18 deletions(-) diff --git a/dom/media/TextTrackCueList.cpp b/dom/media/TextTrackCueList.cpp index 655ebf7065e7..09d09f9eb297 100644 --- a/dom/media/TextTrackCueList.cpp +++ b/dom/media/TextTrackCueList.cpp @@ -125,19 +125,5 @@ TextTrackCueList::SetCuesInactive() } } -already_AddRefed -TextTrackCueList::GetCueListByTimeInterval(media::Interval& aInterval) -{ - RefPtr output = new TextTrackCueList(mParent); - for (uint32_t i = 0; i < mList.Length(); ++i) { - TextTrackCue* cue = mList[i]; - if (cue->StartTime() <= aInterval.mEnd && - aInterval.mStart <= cue->EndTime()) { - output->AddCue(*cue); - } - } - return output.forget(); -} - } // namespace dom } // namespace mozilla diff --git a/dom/media/TextTrackCueList.h b/dom/media/TextTrackCueList.h index 6ae019c8daac..5220cbb2a281 100644 --- a/dom/media/TextTrackCueList.h +++ b/dom/media/TextTrackCueList.h @@ -12,7 +12,6 @@ #include "nsCycleCollectionParticipant.h" #include "nsWrapperCache.h" #include "mozilla/ErrorResult.h" -#include "Intervals.h" namespace mozilla { namespace dom { @@ -57,9 +56,6 @@ public: void SetCuesInactive(); - already_AddRefed - GetCueListByTimeInterval(media::Interval& aInterval); - private: ~TextTrackCueList(); From 5a8c6aa49496cc8d8538a5c000dea0a85d155e0f Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Tue, 31 May 2016 18:54:41 +0200 Subject: [PATCH 094/199] Backed out changeset 453431d7a2c8 (bug 882718) for crashing in track.html with nsXBLPrototypeBinding::GetRuleProcessor(). r=backout --- dom/html/HTMLMediaElement.cpp | 5 ----- dom/media/TextTrack.cpp | 13 ------------- dom/media/TextTrack.h | 2 -- dom/media/TextTrackCue.cpp | 1 - dom/media/TextTrackCue.h | 12 ------------ dom/media/TextTrackCueList.cpp | 8 -------- dom/media/TextTrackCueList.h | 2 -- dom/media/TextTrackList.cpp | 8 -------- dom/media/TextTrackList.h | 1 - 9 files changed, 52 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 61a6339e5846..734a85148ba7 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -776,11 +776,6 @@ void HTMLMediaElement::AbortExistingLoads() ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY); ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING); - //TODO: Apply the rules for text track cue rendering Bug 865407 - if (mTextTrackManager) { - mTextTrackManager->GetTextTracks()->SetCuesInactive(); - } - if (fireTimeUpdate) { // Since we destroyed the decoder above, the current playback position // will now be reported as 0. The playback position was non-zero when diff --git a/dom/media/TextTrack.cpp b/dom/media/TextTrack.cpp index 6dded2583ff7..12f63580dbb0 100644 --- a/dom/media/TextTrack.cpp +++ b/dom/media/TextTrack.cpp @@ -92,10 +92,6 @@ TextTrack::SetMode(TextTrackMode aValue) { if (mMode != aValue) { mMode = aValue; - if (aValue == TextTrackMode::Disabled) { - SetCuesInactive(); - //TODO: Apply the rules for text track cue rendering Bug 865407 - } if (mTextTrackList) { mTextTrackList->CreateAndDispatchChangeEvent(); } @@ -129,9 +125,6 @@ TextTrack::AddCue(TextTrackCue& aCue) void TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv) { - //TODO: Apply the rules for text track cue rendering Bug 865407 - aCue.SetActive(false); - mCueList->RemoveCue(aCue, aRv); SetDirty(); } @@ -270,11 +263,5 @@ TextTrack::SetTrackElement(HTMLTrackElement* aTrackElement) { mTrackElement = aTrackElement; } -void -TextTrack::SetCuesInactive() -{ - mCueList->SetCuesInactive(); -} - } // namespace dom } // namespace mozilla diff --git a/dom/media/TextTrack.h b/dom/media/TextTrack.h index 7eaeeacb1293..a9725c2fc2cc 100644 --- a/dom/media/TextTrack.h +++ b/dom/media/TextTrack.h @@ -118,8 +118,6 @@ public: return mTextTrackSource; } - void SetCuesInactive(); - private: ~TextTrack(); diff --git a/dom/media/TextTrackCue.cpp b/dom/media/TextTrackCue.cpp index 970b61772852..44d2babae553 100644 --- a/dom/media/TextTrackCue.cpp +++ b/dom/media/TextTrackCue.cpp @@ -41,7 +41,6 @@ TextTrackCue::SetDefaultCueSettings() mAlign = AlignSetting::Middle; mLineAlign = AlignSetting::Start; mVertical = DirectionSetting::_empty; - mActive = false; } TextTrackCue::TextTrackCue(nsPIDOMWindowInner* aOwnerWindow, diff --git a/dom/media/TextTrackCue.h b/dom/media/TextTrackCue.h index 103cb36b1fb1..d4b83ab6b1e7 100644 --- a/dom/media/TextTrackCue.h +++ b/dom/media/TextTrackCue.h @@ -332,16 +332,6 @@ public: void SetTrackElement(HTMLTrackElement* aTrackElement); - void SetActive(bool aActive) - { - mActive = aActive; - } - - bool GetActive() - { - return mActive; - } - private: ~TextTrackCue(); @@ -376,8 +366,6 @@ private: // changed. bool mReset; - bool mActive; - static StaticRefPtr sParserWrapper; }; diff --git a/dom/media/TextTrackCueList.cpp b/dom/media/TextTrackCueList.cpp index 09d09f9eb297..0a54ed654445 100644 --- a/dom/media/TextTrackCueList.cpp +++ b/dom/media/TextTrackCueList.cpp @@ -117,13 +117,5 @@ TextTrackCueList::GetArray(nsTArray >& aCues) } -void -TextTrackCueList::SetCuesInactive() -{ - for(uint32_t i = 0; i < mList.Length(); ++i) { - mList[i]->SetActive(false); - } -} - } // namespace dom } // namespace mozilla diff --git a/dom/media/TextTrackCueList.h b/dom/media/TextTrackCueList.h index 5220cbb2a281..0fe435bb98cb 100644 --- a/dom/media/TextTrackCueList.h +++ b/dom/media/TextTrackCueList.h @@ -54,8 +54,6 @@ public: void RemoveAll(); void GetArray(nsTArray >& aCues); - void SetCuesInactive(); - private: ~TextTrackCueList(); diff --git a/dom/media/TextTrackList.cpp b/dom/media/TextTrackList.cpp index 259475a3b880..14fb2b6d6e00 100644 --- a/dom/media/TextTrackList.cpp +++ b/dom/media/TextTrackList.cpp @@ -211,13 +211,5 @@ TextTrackList::SetTextTrackManager(TextTrackManager* aTextTrackManager) mTextTrackManager = aTextTrackManager; } -void -TextTrackList::SetCuesInactive() -{ - for (uint32_t i = 0; i < Length(); i++) { - mTextTracks[i]->SetCuesInactive(); - } -} - } // namespace dom } // namespace mozilla diff --git a/dom/media/TextTrackList.h b/dom/media/TextTrackList.h index dc470b1634ba..b05dc7b1efe6 100644 --- a/dom/media/TextTrackList.h +++ b/dom/media/TextTrackList.h @@ -61,7 +61,6 @@ public: nsresult DispatchTrackEvent(nsIDOMEvent* aEvent); void CreateAndDispatchChangeEvent(); - void SetCuesInactive(); IMPL_EVENT_HANDLER(change) IMPL_EVENT_HANDLER(addtrack) From ae8caa1fe4f422a3b273ca1f9bce413b54328aba Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 31 May 2016 09:59:13 -0700 Subject: [PATCH 095/199] Backed out changeset 5f50f2c7e813 (bug 968273) for android mochitest failures in the test this added --- .../sessionstore/SessionHistory.jsm | 7 +++ docshell/base/nsDocShell.cpp | 51 +++++++++++++++---- docshell/base/nsDocShell.h | 5 ++ docshell/base/nsDocShellLoadInfo.cpp | 17 ++++++- docshell/base/nsDocShellLoadInfo.h | 1 + docshell/base/nsIDocShell.idl | 12 +++-- docshell/base/nsIDocShellLoadInfo.idl | 5 ++ docshell/shistory/nsISHEntry.idl | 5 ++ docshell/shistory/nsSHEntry.cpp | 16 ++++++ docshell/shistory/nsSHEntry.h | 1 + docshell/shistory/nsSHistory.cpp | 4 ++ docshell/test/bug968273_new.html | 1 - docshell/test/bug968273_redirect.html | 1 - .../test/bug968273_redirect.html^headers^ | 2 - docshell/test/mochitest.ini | 4 -- docshell/test/test_bug968273.html | 40 --------------- mobile/android/components/SessionStore.js | 8 +++ 17 files changed, 116 insertions(+), 64 deletions(-) delete mode 100644 docshell/test/bug968273_new.html delete mode 100644 docshell/test/bug968273_redirect.html delete mode 100644 docshell/test/bug968273_redirect.html^headers^ delete mode 100644 docshell/test/test_bug968273.html diff --git a/browser/components/sessionstore/SessionHistory.jsm b/browser/components/sessionstore/SessionHistory.jsm index fc35bb49e95b..0735fee6a94c 100644 --- a/browser/components/sessionstore/SessionHistory.jsm +++ b/browser/components/sessionstore/SessionHistory.jsm @@ -145,6 +145,10 @@ var SessionHistoryInternal = { entry.originalURI = shEntry.originalURI.spec; } + if (shEntry.loadReplace) { + entry.loadReplace = shEntry.loadReplace; + } + if (shEntry.srcdocData) entry.srcdocData = shEntry.srcdocData; @@ -311,6 +315,9 @@ var SessionHistoryInternal = { if (entry.originalURI) { shEntry.originalURI = Utils.makeURI(entry.originalURI); } + if (entry.loadReplace) { + shEntry.loadReplace = entry.loadReplace; + } if (entry.isSrcdocEntry) shEntry.srcdocData = entry.srcdocData; if (entry.baseURI) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index c4479a29f325..8447b7142f39 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -1253,6 +1253,7 @@ nsDocShell::LoadURI(nsIURI* aURI, nsCOMPtr referrer; nsCOMPtr originalURI; + bool loadReplace = false; nsCOMPtr postStream; nsCOMPtr headersStream; nsCOMPtr owner; @@ -1280,6 +1281,7 @@ nsDocShell::LoadURI(nsIURI* aURI, if (aLoadInfo) { aLoadInfo->GetReferrer(getter_AddRefs(referrer)); aLoadInfo->GetOriginalURI(getter_AddRefs(originalURI)); + aLoadInfo->GetLoadReplace(&loadReplace); nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal; aLoadInfo->GetLoadType(<); // Get the appropriate loadType from nsIDocShellLoadInfo type @@ -1539,6 +1541,7 @@ nsDocShell::LoadURI(nsIURI* aURI, return InternalLoad(aURI, originalURI, + loadReplace, referrer, referrerPolicy, owner, @@ -5285,7 +5288,7 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL, rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl); NS_ENSURE_SUCCESS(rv, rv); - return InternalLoad(errorPageURI, nullptr, nullptr, + return InternalLoad(errorPageURI, nullptr, false, nullptr, mozilla::net::RP_Default, nullptr, INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr, NullString(), nullptr, nullptr, LOAD_ERROR_PAGE, @@ -5337,6 +5340,7 @@ nsDocShell::Reload(uint32_t aReloadFlags) nsAutoString contentTypeHint; nsCOMPtr baseURI; nsCOMPtr originalURI; + bool loadReplace = false; if (doc) { principal = doc->NodePrincipal(); doc->GetContentType(contentTypeHint); @@ -5348,6 +5352,9 @@ nsDocShell::Reload(uint32_t aReloadFlags) } nsCOMPtr chan = doc->GetChannel(); if (chan) { + uint32_t loadFlags; + chan->GetLoadFlags(&loadFlags); + loadReplace = loadFlags & nsIChannel::LOAD_REPLACE; nsCOMPtr httpChan(do_QueryInterface(chan)); if (httpChan) { httpChan->GetOriginalURI(getter_AddRefs(originalURI)); @@ -5357,6 +5364,7 @@ nsDocShell::Reload(uint32_t aReloadFlags) rv = InternalLoad(mCurrentURI, originalURI, + loadReplace, mReferrerURI, mReferrerPolicy, principal, @@ -9498,7 +9506,7 @@ class InternalLoadEvent : public Runnable { public: InternalLoadEvent(nsDocShell* aDocShell, nsIURI* aURI, - nsIURI* aOriginalURI, + nsIURI* aOriginalURI, bool aLoadReplace, nsIURI* aReferrer, uint32_t aReferrerPolicy, nsISupports* aOwner, uint32_t aFlags, const char* aTypeHint, nsIInputStream* aPostData, @@ -9510,6 +9518,7 @@ public: , mDocShell(aDocShell) , mURI(aURI) , mOriginalURI(aOriginalURI) + , mLoadReplace(aLoadReplace) , mReferrer(aReferrer) , mReferrerPolicy(aReferrerPolicy) , mOwner(aOwner) @@ -9532,6 +9541,7 @@ public: Run() { return mDocShell->InternalLoad(mURI, mOriginalURI, + mLoadReplace, mReferrer, mReferrerPolicy, mOwner, mFlags, @@ -9551,6 +9561,7 @@ private: RefPtr mDocShell; nsCOMPtr mURI; nsCOMPtr mOriginalURI; + bool mLoadReplace; nsCOMPtr mReferrer; uint32_t mReferrerPolicy; nsCOMPtr mOwner; @@ -9617,6 +9628,7 @@ nsDocShell::IsAboutNewtab(nsIURI* aURI) NS_IMETHODIMP nsDocShell::InternalLoad(nsIURI* aURI, nsIURI* aOriginalURI, + bool aLoadReplace, nsIURI* aReferrer, uint32_t aReferrerPolicy, nsISupports* aOwner, @@ -9880,6 +9892,7 @@ nsDocShell::InternalLoad(nsIURI* aURI, if (NS_SUCCEEDED(rv) && targetDocShell) { rv = targetDocShell->InternalLoad(aURI, aOriginalURI, + aLoadReplace, aReferrer, aReferrerPolicy, owner, @@ -9960,7 +9973,7 @@ nsDocShell::InternalLoad(nsIURI* aURI, // Do this asynchronously nsCOMPtr ev = - new InternalLoadEvent(this, aURI, aOriginalURI, + new InternalLoadEvent(this, aURI, aOriginalURI, aLoadReplace, aReferrer, aReferrerPolicy, aOwner, aFlags, aTypeHint, aPostData, aHeadersData, aLoadType, aSHEntry, aFirstParty, aSrcdoc, @@ -10477,10 +10490,7 @@ nsDocShell::InternalLoad(nsIURI* aURI, nsINetworkPredictor::PREDICT_LOAD, this, nullptr); nsCOMPtr req; - // At this point we will open a new channel to load data. If aOriginalURI - // is present, we load aOriginalURI instead of aURI because we want to load - // all redirects again. - rv = DoURILoad(aOriginalURI ? aOriginalURI : aURI, aReferrer, + rv = DoURILoad(aURI, aOriginalURI, aLoadReplace, aReferrer, !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER), aReferrerPolicy, owner, aTypeHint, aFileName, aPostData, aHeadersData, @@ -10557,6 +10567,8 @@ nsDocShell::GetInheritedPrincipal(bool aConsiderCurrentDocument) nsresult nsDocShell::DoURILoad(nsIURI* aURI, + nsIURI* aOriginalURI, + bool aLoadReplace, nsIURI* aReferrerURI, bool aSendReferrer, uint32_t aReferrerPolicy, @@ -10829,7 +10841,17 @@ nsDocShell::DoURILoad(nsIURI* aURI, NS_ADDREF(*aRequest = channel); } - channel->SetOriginalURI(aURI); + if (aOriginalURI) { + channel->SetOriginalURI(aOriginalURI); + if (aLoadReplace) { + uint32_t loadFlags; + channel->GetLoadFlags(&loadFlags); + NS_ENSURE_SUCCESS(rv, rv); + channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE); + } + } else { + channel->SetOriginalURI(aURI); + } if (aTypeHint && *aTypeHint) { channel->SetContentType(nsDependentCString(aTypeHint)); @@ -10966,8 +10988,8 @@ nsDocShell::DoURILoad(nsIURI* aURI, httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy); } // set Content-Signature enforcing bit if aOriginalURI == about:newtab - if (httpChannel) { - if (IsAboutNewtab(aURI)) { + if (aOriginalURI && httpChannel) { + if (IsAboutNewtab(aOriginalURI)) { nsCOMPtr loadInfo = httpChannel->GetLoadInfo(); if (loadInfo) { loadInfo->SetVerifySignedContent(true); @@ -11831,6 +11853,7 @@ nsDocShell::AddState(JS::Handle aData, const nsAString& aTitle, newSHEntry = mOSHE; newSHEntry->SetURI(newURI); newSHEntry->SetOriginalURI(newURI); + newSHEntry->SetLoadReplace(false); } // Step 4: Modify new/original session history entry and clear its POST @@ -12041,6 +12064,7 @@ nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel, // Get the post data & referrer nsCOMPtr inputStream; nsCOMPtr originalURI; + bool loadReplace = false; nsCOMPtr referrerURI; uint32_t referrerPolicy = mozilla::net::RP_Default; nsCOMPtr cacheKey; @@ -12071,6 +12095,7 @@ nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel, httpChannel->GetOriginalURI(getter_AddRefs(originalURI)); uint32_t loadFlags; aChannel->GetLoadFlags(&loadFlags); + loadReplace = loadFlags & nsIChannel::LOAD_REPLACE; httpChannel->GetReferrer(getter_AddRefs(referrerURI)); httpChannel->GetReferrerPolicy(&referrerPolicy); @@ -12114,6 +12139,7 @@ nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel, mDynamicallyCreated); entry->SetOriginalURI(originalURI); + entry->SetLoadReplace(loadReplace); entry->SetReferrerURI(referrerURI); entry->SetReferrerPolicy(referrerPolicy); nsCOMPtr inStrmChan = do_QueryInterface(aChannel); @@ -12213,6 +12239,7 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) nsCOMPtr uri; nsCOMPtr originalURI; + bool loadReplace = false; nsCOMPtr postData; nsCOMPtr referrerURI; uint32_t referrerPolicy; @@ -12224,6 +12251,8 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(aEntry->GetOriginalURI(getter_AddRefs(originalURI)), NS_ERROR_FAILURE); + NS_ENSURE_SUCCESS(aEntry->GetLoadReplace(&loadReplace), + NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(aEntry->GetReferrerPolicy(&referrerPolicy), @@ -12304,6 +12333,7 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) // first created. bug 947716 has been created to address this issue. rv = InternalLoad(uri, originalURI, + loadReplace, referrerURI, referrerPolicy, owner, @@ -13790,6 +13820,7 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent, nsresult rv = InternalLoad(clonedURI, // New URI nullptr, // Original URI + false, // LoadReplace referer, // Referer URI refererPolicy, // Referer policy aContent->NodePrincipal(), // Owner is our node's diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 030a5a793d16..b19a691eaa9c 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -352,7 +352,12 @@ protected: // not have an owner on the channel should just pass null. // If aSrcdoc is not void, the load will be considered as a srcdoc load, // and the contents of aSrcdoc will be loaded instead of aURI. + // aOriginalURI will be set as the originalURI on the channel that does the + // load. If aOriginalURI is null, aURI will be set as the originalURI. + // If aLoadReplace is true, OLOAD_REPLACE flag will be set to the nsIChannel. nsresult DoURILoad(nsIURI* aURI, + nsIURI* aOriginalURI, + bool aLoadReplace, nsIURI* aReferrer, bool aSendReferrer, uint32_t aReferrerPolicy, diff --git a/docshell/base/nsDocShellLoadInfo.cpp b/docshell/base/nsDocShellLoadInfo.cpp index ad302d27c043..0dac1cfc1815 100644 --- a/docshell/base/nsDocShellLoadInfo.cpp +++ b/docshell/base/nsDocShellLoadInfo.cpp @@ -12,7 +12,8 @@ #include "mozilla/net/ReferrerPolicy.h" nsDocShellLoadInfo::nsDocShellLoadInfo() - : mInheritOwner(false) + : mLoadReplace(false) + , mInheritOwner(false) , mOwnerIsExplicit(false) , mSendReferrer(true) , mReferrerPolicy(mozilla::net::RP_Default) @@ -67,6 +68,20 @@ nsDocShellLoadInfo::SetOriginalURI(nsIURI* aOriginalURI) return NS_OK; } +NS_IMETHODIMP +nsDocShellLoadInfo::GetLoadReplace(bool* aLoadReplace) +{ + *aLoadReplace = mLoadReplace; + return NS_OK; +} + +NS_IMETHODIMP +nsDocShellLoadInfo::SetLoadReplace(bool aLoadReplace) +{ + mLoadReplace = aLoadReplace; + return NS_OK; +} + NS_IMETHODIMP nsDocShellLoadInfo::GetOwner(nsISupports** aOwner) { diff --git a/docshell/base/nsDocShellLoadInfo.h b/docshell/base/nsDocShellLoadInfo.h index 7879ad9984bb..105a53d433c0 100644 --- a/docshell/base/nsDocShellLoadInfo.h +++ b/docshell/base/nsDocShellLoadInfo.h @@ -34,6 +34,7 @@ protected: nsCOMPtr mReferrer; nsCOMPtr mOriginalURI; nsCOMPtr mOwner; + bool mLoadReplace; bool mInheritOwner; bool mOwnerIsExplicit; bool mSendReferrer; diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index 8d75d57be52c..5f4bb2fb9541 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -125,11 +125,12 @@ interface nsIDocShell : nsIDocShellTreeItem * of an nsIDocShellLoadInfo object... * * @param aURI - The URI to load. - * @param aOriginalURI - If aOriginalURI is present and this function ends - * up actually loading data from network or cache, - * but not from bfcache, (e.g. in case of a reload) - * aOriginalURI will be loaded instead of aURI. - * Thereby we will load all redirects again. + * @param aOriginalURI - The URI to set as the originalURI on the channel + * that does the load. If null, aURI will be set as + * the originalURI. + * @param aLoadReplace - If set LOAD_REPLACE flag will be set on the + * channel. aOriginalURI is null, this argument is + * ignored. * @param aReferrer - Referring URI * @param aReferrerPolicy - Referrer policy * @param aOwner - Owner (security principal) @@ -157,6 +158,7 @@ interface nsIDocShell : nsIDocShellTreeItem */ [noscript]void internalLoad(in nsIURI aURI, in nsIURI aOriginalURI, + in boolean aLoadReplace, in nsIURI aReferrer, in unsigned long aReferrerPolicy, in nsISupports aOwner, diff --git a/docshell/base/nsIDocShellLoadInfo.idl b/docshell/base/nsIDocShellLoadInfo.idl index d06a4f72b7fb..89337152d72c 100644 --- a/docshell/base/nsIDocShellLoadInfo.idl +++ b/docshell/base/nsIDocShellLoadInfo.idl @@ -30,6 +30,11 @@ interface nsIDocShellLoadInfo : nsISupports */ attribute nsIURI originalURI; + /** + * loadReplace flag to be passed to nsIDocShell.internalLoad. + */ + attribute boolean loadReplace; + /** The owner of the load, that is, the entity responsible for * causing the load to occur. This should be a nsIPrincipal typically. */ diff --git a/docshell/shistory/nsISHEntry.idl b/docshell/shistory/nsISHEntry.idl index 9eee45ea5733..b212ebd8522c 100644 --- a/docshell/shistory/nsISHEntry.idl +++ b/docshell/shistory/nsISHEntry.idl @@ -47,6 +47,11 @@ interface nsISHEntry : nsISupports */ attribute nsIURI originalURI; + /** + * This flag remembers whether channel has LOAD_REPLACE set. + */ + attribute boolean loadReplace; + /** * A readonly property that returns the title * of the current entry. The object returned diff --git a/docshell/shistory/nsSHEntry.cpp b/docshell/shistory/nsSHEntry.cpp index 7185e2d7e717..3d6b96ee9c9f 100644 --- a/docshell/shistory/nsSHEntry.cpp +++ b/docshell/shistory/nsSHEntry.cpp @@ -24,6 +24,7 @@ static uint32_t gEntryID = 0; nsSHEntry::nsSHEntry() : mShared(new nsSHEntryShared()) + , mLoadReplace(false) , mReferrerPolicy(mozilla::net::RP_Default) , mLoadType(0) , mID(gEntryID++) @@ -40,6 +41,7 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther) : mShared(aOther.mShared) , mURI(aOther.mURI) , mOriginalURI(aOther.mOriginalURI) + , mLoadReplace(aOther.mLoadReplace) , mReferrerURI(aOther.mReferrerURI) , mReferrerPolicy(aOther.mReferrerPolicy) , mTitle(aOther.mTitle) @@ -135,6 +137,20 @@ nsSHEntry::SetOriginalURI(nsIURI* aOriginalURI) return NS_OK; } +NS_IMETHODIMP +nsSHEntry::GetLoadReplace(bool* aLoadReplace) +{ + *aLoadReplace = mLoadReplace; + return NS_OK; +} + +NS_IMETHODIMP +nsSHEntry::SetLoadReplace(bool aLoadReplace) +{ + mLoadReplace = aLoadReplace; + return NS_OK; +} + NS_IMETHODIMP nsSHEntry::GetReferrerURI(nsIURI** aReferrerURI) { diff --git a/docshell/shistory/nsSHEntry.h b/docshell/shistory/nsSHEntry.h index a10fc9df0b76..0181e9e6c13a 100644 --- a/docshell/shistory/nsSHEntry.h +++ b/docshell/shistory/nsSHEntry.h @@ -50,6 +50,7 @@ private: // See nsSHEntry.idl for comments on these members. nsCOMPtr mURI; nsCOMPtr mOriginalURI; + bool mLoadReplace; nsCOMPtr mReferrerURI; uint32_t mReferrerPolicy; nsString mTitle; diff --git a/docshell/shistory/nsSHistory.cpp b/docshell/shistory/nsSHistory.cpp index 80796a9bf77c..6e1ca4283449 100644 --- a/docshell/shistory/nsSHistory.cpp +++ b/docshell/shistory/nsSHistory.cpp @@ -1767,6 +1767,10 @@ nsSHistory::InitiateLoad(nsISHEntry* aFrameEntry, nsIDocShell* aFrameDS, aFrameEntry->GetOriginalURI(getter_AddRefs(originalURI)); loadInfo->SetOriginalURI(originalURI); + bool loadReplace; + aFrameEntry->GetLoadReplace(&loadReplace); + loadInfo->SetLoadReplace(loadReplace); + nsCOMPtr nextURI; aFrameEntry->GetURI(getter_AddRefs(nextURI)); // Time to initiate a document load diff --git a/docshell/test/bug968273_new.html b/docshell/test/bug968273_new.html deleted file mode 100644 index d31d24e21a16..000000000000 --- a/docshell/test/bug968273_new.html +++ /dev/null @@ -1 +0,0 @@ -This is bug968273_new.html. diff --git a/docshell/test/bug968273_redirect.html b/docshell/test/bug968273_redirect.html deleted file mode 100644 index a1ee6c19a8c4..000000000000 --- a/docshell/test/bug968273_redirect.html +++ /dev/null @@ -1 +0,0 @@ -This document is redirected to bug968273_new.html. diff --git a/docshell/test/bug968273_redirect.html^headers^ b/docshell/test/bug968273_redirect.html^headers^ deleted file mode 100644 index 86334513cf3b..000000000000 --- a/docshell/test/bug968273_redirect.html^headers^ +++ /dev/null @@ -1,2 +0,0 @@ -HTTP 302 Moved Temporarily -Location: bug968273_new.html diff --git a/docshell/test/mochitest.ini b/docshell/test/mochitest.ini index 6c8b63494848..8cbff7962eee 100644 --- a/docshell/test/mochitest.ini +++ b/docshell/test/mochitest.ini @@ -11,9 +11,6 @@ support-files = bug668513_redirect.html bug668513_redirect.html^headers^ bug691547_frame.html - bug968273_new.html - bug968273_redirect.html - bug968273_redirect.html^headers^ file_anchor_scroll_after_document_open.html file_bug385434_1.html file_bug385434_2.html @@ -111,4 +108,3 @@ support-files = file_framedhistoryframes.html skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage [test_bug1121701.html] skip-if = (buildapp == 'b2g' || buildapp == 'mulet') -[test_bug968273.html] diff --git a/docshell/test/test_bug968273.html b/docshell/test/test_bug968273.html deleted file mode 100644 index 8ffe3d1bfdff..000000000000 --- a/docshell/test/test_bug968273.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - Test for Bug 968273 - - - - - - - - - diff --git a/mobile/android/components/SessionStore.js b/mobile/android/components/SessionStore.js index 29f8309969e7..eca8a9c98d23 100644 --- a/mobile/android/components/SessionStore.js +++ b/mobile/android/components/SessionStore.js @@ -1035,6 +1035,10 @@ SessionStore.prototype = { entry.originalURI = aEntry.originalURI.spec; } + if (aEntry.loadReplace) { + entry.loadReplace = aEntry.loadReplace; + } + if (aEntry.contentType) { entry.contentType = aEntry.contentType; } @@ -1123,6 +1127,10 @@ SessionStore.prototype = { shEntry.originalURI = Services.io.newURI(aEntry.originalURI, null, null); } + if (aEntry.loadReplace) { + shEntry.loadReplace = aEntry.loadReplace; + } + if (aEntry.cacheKey) { let cacheKey = Cc["@mozilla.org/supports-PRUint32;1"].createInstance(Ci.nsISupportsPRUint32); cacheKey.data = aEntry.cacheKey; From e62326427f71ab5d5c9c9b5d33894fa2483d619f Mon Sep 17 00:00:00 2001 From: Chris Manchester Date: Tue, 31 May 2016 10:14:35 -0700 Subject: [PATCH 096/199] Bug 1276043 - Move ANDROID_SUPPORT_LIBRARY_VERSION and ADNROID_GOOGLE_PLAY_SERVICES_VERSION to Python configure. r=glandium MozReview-Commit-ID: 2UctER22IGt --- build/autoconf/android.m4 | 6 ------ mobile/android/moz.configure | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/autoconf/android.m4 b/build/autoconf/android.m4 index de2b57cd2ac3..fdc1b0091269 100644 --- a/build/autoconf/android.m4 +++ b/build/autoconf/android.m4 @@ -207,12 +207,6 @@ AC_DEFUN([MOZ_ANDROID_AAR],[ MOZ_ANDROID_AAR_COMPONENT(concat(local_aar_var, _ASSETS), concat(root, assets), $6) ]) -ANDROID_SUPPORT_LIBRARY_VERSION="23.0.1" -AC_SUBST(ANDROID_SUPPORT_LIBRARY_VERSION) - -ANDROID_GOOGLE_PLAY_SERVICES_VERSION="8.4.0" -AC_SUBST(ANDROID_GOOGLE_PLAY_SERVICES_VERSION) - AC_DEFUN([MOZ_ANDROID_GOOGLE_PLAY_SERVICES], [ diff --git a/mobile/android/moz.configure b/mobile/android/moz.configure index 759d6f807a56..bb9d660798ea 100644 --- a/mobile/android/moz.configure +++ b/mobile/android/moz.configure @@ -56,6 +56,12 @@ imply_option('MOZ_SOCIAL', False) imply_option('MOZ_SERVICES_HEALTHREPORT', True) imply_option('MOZ_ANDROID_HISTORY', True) +set_config('ANDROID_SUPPORT_LIBRARY_VERSION', '23.0.1') +add_old_configure_assignment('ANDROID_SUPPORT_LIBRARY_VERSION', '23.0.1') + +set_config('ANDROID_GOOGLE_PLAY_SERVICES_VERSION', '8.4.0') +add_old_configure_assignment('ANDROID_GOOGLE_PLAY_SERVICES_VERSION', '8.4.0') + @depends(target) def check_target(target): if target.os != 'Android': From ebb9f7599cd7408eda566e6027a9d7bd80d196d3 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 31 May 2016 10:19:32 -0700 Subject: [PATCH 097/199] Bug 1259850 warning fix on a CLOSED TREE MozReview-Commit-ID: 8OsRyn3HD3j --- js/src/gc/Statistics.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index fbfd199f1763..b5cc36eabef7 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -1110,8 +1110,10 @@ Statistics::suspendPhases(Phase suspension) void Statistics::resumePhases() { +#ifdef DEBUG Phase popped = suspendedPhases[--suspended]; MOZ_ASSERT(popped == PHASE_EXPLICIT_SUSPENSION || popped == PHASE_IMPLICIT_SUSPENSION); +#endif while (suspended && suspendedPhases[suspended - 1] != PHASE_EXPLICIT_SUSPENSION && suspendedPhases[suspended - 1] != PHASE_IMPLICIT_SUSPENSION) From fee4c629b4335325dede98e66401ecde0d60865d Mon Sep 17 00:00:00 2001 From: Ralph Giles Date: Mon, 30 May 2016 14:45:54 -0700 Subject: [PATCH 098/199] Bug 1276747 - Update windows tooltool rust to 1.9.0. r=mshal Repacked official binaries of the 1.9.0 stable release and std library. MozReview-Commit-ID: CMXBZZZfMCn --- browser/config/tooltool-manifests/win32/releng.manifest | 6 +++--- browser/config/tooltool-manifests/win64/releng.manifest | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/browser/config/tooltool-manifests/win32/releng.manifest b/browser/config/tooltool-manifests/win32/releng.manifest index ad169bca8a32..29d0646f08f0 100644 --- a/browser/config/tooltool-manifests/win32/releng.manifest +++ b/browser/config/tooltool-manifests/win32/releng.manifest @@ -6,9 +6,9 @@ "filename": "mozmake.exe" }, { -"version": "rustc 1.8.0 (db2939409 2016-04-11)", -"size": 78733276, -"digest": "96ab09cd667ed854efeeca41881a92c9fdc5f3cdeff9b02b12c514183c0b54a21dee8574367abe532429e04660681a1f6c37f6d22cc877d63fbcca7b986d3495", +"version": "rustc 1.9.0 (e4e8b6668 2016-05-18)", +"size": 82463178, +"digest": "a3c54c6792e75d53ec79caf958db25b651fcf968a37b00949fb327c54a54cad6305a4af302f267082d86d70fcf837ed0f273f85b97706c20b957ff3690889b40", "algorithm": "sha512", "filename": "rustc.tar.bz2", "unpack": true diff --git a/browser/config/tooltool-manifests/win64/releng.manifest b/browser/config/tooltool-manifests/win64/releng.manifest index ed50955f9043..fe5d4a454fe8 100644 --- a/browser/config/tooltool-manifests/win64/releng.manifest +++ b/browser/config/tooltool-manifests/win64/releng.manifest @@ -6,9 +6,9 @@ "filename": "mozmake.exe" }, { -"version": "rustc 1.8.0 (db2939409 2016-04-11)", -"size": 85285866, -"digest": "f3862036781df9f699a18a5449d51a4f8880e7d890500b314d1f16f394da77c2c496fa537a691d9d99f3248ec2067beaf20b4361babe0dd449d4f7b4f539acac", +"version": "rustc 1.9.0 (e4e8b6668 2016-05-18)", +"size": 88486080, +"digest": "a4fb99cd637b236a9c30e111757ca560bc8df1b143324c1d9ab58c32470b9b9a0598e3e0d220278ee157959dcd88421496388e2ed856e6261d9c81f18e6310e9", "algorithm": "sha512", "visibility": "public", "filename": "rustc.tar.bz2", From 74c78019e7bc7f64c8c4b8e3c9bf03202facf38f Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 31 May 2016 11:15:41 -0700 Subject: [PATCH 099/199] Backed out 22 changesets (bug 1259850) for GC crashes in various tests CLOSED TREE Backed out changeset ef5cdcca45d9 (bug 1259850) Backed out changeset c95bdd426ced (bug 1259850) Backed out changeset a73f74f718e7 (bug 1259850) Backed out changeset 95107c3ad9cf (bug 1259850) Backed out changeset 788ac18818c9 (bug 1259850) Backed out changeset 19c13aa9b5ad (bug 1259850) Backed out changeset 0b9dedcf7163 (bug 1259850) Backed out changeset b641d01138ab (bug 1259850) Backed out changeset aa434447a11b (bug 1259850) Backed out changeset 4c7373c6c29e (bug 1259850) Backed out changeset 457cb29cad55 (bug 1259850) Backed out changeset 5762a8fba027 (bug 1259850) Backed out changeset 129559d4ac62 (bug 1259850) Backed out changeset d00b9c8a7984 (bug 1259850) Backed out changeset 266befcb8acd (bug 1259850) Backed out changeset c6615c7b0083 (bug 1259850) Backed out changeset 196ac1f813f9 (bug 1259850) Backed out changeset b6108a65dc38 (bug 1259850) Backed out changeset 0d58f8529b86 (bug 1259850) Backed out changeset a8d2730ada95 (bug 1259850) Backed out changeset e8544b072ee6 (bug 1259850) Backed out changeset 15de0d1d0b05 (bug 1259850) --- .hgignore | 1 - .../linux64/hazard.manifest | 68 ++-- .../linux64/hazard.manifest | 56 ++-- js/public/GCAPI.h | 2 +- js/public/GCAnnotations.h | 6 +- js/src/devtools/rootAnalysis/CFG.js | 2 +- js/src/devtools/rootAnalysis/analyze.py | 48 ++- js/src/devtools/rootAnalysis/analyzeRoots.js | 177 ++++------- js/src/devtools/rootAnalysis/annotations.js | 53 +--- .../rootAnalysis/build/sixgill.manifest | 16 +- .../devtools/rootAnalysis/computeCallgraph.js | 294 ++++++++---------- .../rootAnalysis/computeGCFunctions.js | 22 +- .../devtools/rootAnalysis/computeGCTypes.js | 43 --- js/src/devtools/rootAnalysis/explain.py | 19 +- js/src/devtools/rootAnalysis/loadCallgraph.js | 9 +- js/src/devtools/rootAnalysis/run-test.py | 201 ++++++++---- .../rootAnalysis/t/exceptions/source.cpp | 42 --- .../rootAnalysis/t/exceptions/test.py | 19 -- .../rootAnalysis/t/hazards/source.cpp | 89 ------ .../devtools/rootAnalysis/t/hazards/test.py | 36 --- js/src/devtools/rootAnalysis/t/sixgill.py | 63 ---- .../rootAnalysis/t/suppression/source.cpp | 64 ---- .../rootAnalysis/t/suppression/test.py | 23 -- js/src/devtools/rootAnalysis/t/testlib.py | 120 ------- .../{t/sixgill-tree => test}/source.cpp | 0 .../{t/sixgill-tree => test}/test.py | 16 +- js/src/devtools/rootAnalysis/utility.js | 27 -- js/src/gc/Iteration.cpp | 22 +- js/src/gc/Statistics.cpp | 47 +-- js/src/gc/Statistics.h | 31 +- js/src/gc/Zone.cpp | 13 +- js/src/gc/Zone.h | 10 - js/src/jit/BaselineJIT.cpp | 10 +- js/src/jit/Ion.cpp | 8 +- .../jsapi-tests/testGCStoreBufferRemoval.cpp | 2 +- js/src/jscompartment.cpp | 7 +- js/src/jsgc.cpp | 62 ++-- js/src/jsgc.h | 91 +----- js/src/jsgcinlines.h | 144 +++------ js/src/jsopcode.cpp | 11 +- js/src/vm/Debugger.cpp | 4 +- js/src/vm/HelperThreads.cpp | 5 +- js/src/vm/NativeObject.cpp | 2 +- js/src/vm/TypeInference.cpp | 18 +- 44 files changed, 591 insertions(+), 1412 deletions(-) delete mode 100644 js/src/devtools/rootAnalysis/t/exceptions/source.cpp delete mode 100644 js/src/devtools/rootAnalysis/t/exceptions/test.py delete mode 100644 js/src/devtools/rootAnalysis/t/hazards/source.cpp delete mode 100644 js/src/devtools/rootAnalysis/t/hazards/test.py delete mode 100644 js/src/devtools/rootAnalysis/t/sixgill.py delete mode 100644 js/src/devtools/rootAnalysis/t/suppression/source.cpp delete mode 100644 js/src/devtools/rootAnalysis/t/suppression/test.py delete mode 100644 js/src/devtools/rootAnalysis/t/testlib.py rename js/src/devtools/rootAnalysis/{t/sixgill-tree => test}/source.cpp (100%) rename js/src/devtools/rootAnalysis/{t/sixgill-tree => test}/test.py (82%) diff --git a/.hgignore b/.hgignore index 7aa79886a2da..895885254eab 100644 --- a/.hgignore +++ b/.hgignore @@ -43,7 +43,6 @@ _OPT\.OBJ/ ^js/src/autom4te.cache$ # SpiderMonkey test result logs ^js/src/tests/results-.*\.(html|txt)$ -^js/src/devtools/rootAnalysis/t/out # Java HTML5 parser classes ^parser/html/java/(html|java)parser/ diff --git a/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest b/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest index f4ab1fcabc74..fcef5bfc5738 100644 --- a/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest +++ b/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest @@ -1,47 +1,47 @@ [ { -"size" : 102421980, -"digest" : "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", -"version" : "gcc 4.9.3", -"unpack" : true, -"filename" : "gcc.tar.xz", -"algorithm" : "sha512" +"version": "gcc 4.9.3", +"size": 102421980, +"digest": "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", +"algorithm": "sha512", +"filename": "gcc.tar.xz", +"unpack": true }, { -"unpack" : true, +"hg_id" : "cd93f15a30ce", "algorithm" : "sha512", +"digest" : "541eb3842ab6b91bd87223cad7a5e4387ef3e496e5b580c8047b8b586bc7eb69fecf3c9eb8c45a1e0deebb53554f0e8acedfe1b4ca64d93b6d008f3f2eb11389", "filename" : "sixgill.tar.xz", -"hg_id" : "8cb9c3fb039a+ tip", -"digest" : "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029", -"size" : 2631908 -}, -{ -"algorithm" : "sha512", -"filename" : "gtk3.tar.xz", -"setup" : "setup.sh", -"unpack" : true, -"digest" : "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", -"size" : 12072532 -}, -{ -"size" : 89319524, -"digest" : "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", -"algorithm" : "sha512", -"filename" : "rustc.tar.xz", +"size" : 2626640, "unpack" : true }, { -"algorithm" : "sha512", -"filename" : "sccache.tar.bz2", -"unpack" : true, -"digest" : "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", -"size" : 167175 +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", +"algorithm": "sha512", +"filename": "gtk3.tar.xz", +"setup": "setup.sh", +"unpack": true }, { -"filename" : "moz-tt.tar.bz2", -"algorithm" : "sha512", -"unpack" : true, -"digest" : "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c", -"size" : 31078810 +"size": 89319524, +"digest": "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", +"algorithm": "sha512", +"filename": "rustc.tar.xz", +"unpack": true +}, +{ +"size": 167175, +"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", +"algorithm": "sha512", +"filename": "sccache.tar.bz2", +"unpack": true +}, +{ +"size": 31078810, +"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c", +"algorithm": "sha512", +"filename": "moz-tt.tar.bz2", +"unpack": true } ] diff --git a/browser/config/tooltool-manifests/linux64/hazard.manifest b/browser/config/tooltool-manifests/linux64/hazard.manifest index f05dcf166c5b..9d0818273012 100644 --- a/browser/config/tooltool-manifests/linux64/hazard.manifest +++ b/browser/config/tooltool-manifests/linux64/hazard.manifest @@ -1,40 +1,40 @@ [ { -"size" : 102421980, -"version" : "gcc 4.9.3", -"filename" : "gcc.tar.xz", +"version": "gcc 4.9.3", +"size": 102421980, +"digest": "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", +"algorithm": "sha512", +"filename": "gcc.tar.xz", +"unpack": true +}, +{ +"hg_id" : "cd93f15a30ce", "algorithm" : "sha512", -"digest" : "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", +"digest" : "541eb3842ab6b91bd87223cad7a5e4387ef3e496e5b580c8047b8b586bc7eb69fecf3c9eb8c45a1e0deebb53554f0e8acedfe1b4ca64d93b6d008f3f2eb11389", +"filename" : "sixgill.tar.xz", +"size" : 2626640, "unpack" : true }, { -"digest" : "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029", -"unpack" : true, -"algorithm" : "sha512", -"filename" : "sixgill.tar.xz", -"size" : 2631908, -"hg_id" : "8cb9c3fb039a+ tip" +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", +"algorithm": "sha512", +"filename": "gtk3.tar.xz", +"setup": "setup.sh", +"unpack": true }, { -"digest" : "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", -"unpack" : true, -"setup" : "setup.sh", -"algorithm" : "sha512", -"filename" : "gtk3.tar.xz", -"size" : 12072532 +"size": 89319524, +"digest": "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", +"algorithm": "sha512", +"filename": "rustc.tar.xz", +"unpack": true }, { -"unpack" : true, -"digest" : "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", -"filename" : "rustc.tar.xz", -"algorithm" : "sha512", -"size" : 89319524 -}, -{ -"filename" : "sccache.tar.bz2", -"algorithm" : "sha512", -"digest" : "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", -"unpack" : true, -"size" : 167175 +"size": 167175, +"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", +"algorithm": "sha512", +"filename": "sccache.tar.bz2", +"unpack": true } ] diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index c95ab18b1ac0..150dce3ac0cc 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -551,7 +551,7 @@ class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertNoAlloc public: AutoSuppressGCAnalysis() : AutoAssertNoAlloc() {} explicit AutoSuppressGCAnalysis(JSRuntime* rt) : AutoAssertNoAlloc(rt) {} -} JS_HAZ_GC_SUPPRESSED; +}; /** * Assert that code is only ever called from a GC callback, disable the static diff --git a/js/public/GCAnnotations.h b/js/public/GCAnnotations.h index 366d787bf4dd..e73caf8d4da6 100644 --- a/js/public/GCAnnotations.h +++ b/js/public/GCAnnotations.h @@ -23,7 +23,7 @@ # define JS_HAZ_ROOTED __attribute__((tag("Rooted Pointer"))) // Mark a type as something that should not be held live across a GC, but which -// is not itself a GC pointer. +// is itself not a GC pointer. # define JS_HAZ_GC_INVALIDATED __attribute__((tag("Invalidated by GC"))) // Mark a type that would otherwise be considered a GC Pointer (eg because it @@ -39,9 +39,6 @@ // invalidating GC pointers. # define JS_HAZ_GC_CALL __attribute__((tag("GC Call"))) -// Mark an RAII class as suppressing GC within its scope. -# define JS_HAZ_GC_SUPPRESSED __attribute__((tag("Suppress GC"))) - #else # define JS_HAZ_GC_THING @@ -50,7 +47,6 @@ # define JS_HAZ_GC_INVALIDATED # define JS_HAZ_NON_GC_POINTER # define JS_HAZ_GC_CALL -# define JS_HAZ_GC_SUPPRESSED #endif diff --git a/js/src/devtools/rootAnalysis/CFG.js b/js/src/devtools/rootAnalysis/CFG.js index 6e9facaa1201..0b35e8914505 100644 --- a/js/src/devtools/rootAnalysis/CFG.js +++ b/js/src/devtools/rootAnalysis/CFG.js @@ -70,7 +70,7 @@ function allRAIIGuardedCallPoints(bodies, body, isConstructor) continue; var variable = callee.Variable; assert(variable.Kind == "Func"); - if (!isConstructor(edge.Type, variable.Name)) + if (!isConstructor(variable.Name)) continue; if (!("PEdgeCallInstance" in edge)) continue; diff --git a/js/src/devtools/rootAnalysis/analyze.py b/js/src/devtools/rootAnalysis/analyze.py index 69482dab7b71..3f2c34a44003 100755 --- a/js/src/devtools/rootAnalysis/analyze.py +++ b/js/src/devtools/rootAnalysis/analyze.py @@ -72,14 +72,12 @@ def generate_hazards(config, outfilename): '%(gcEdges)s', '%(suppressedFunctions_list)s', '%(gcTypes)s', - '%(typeInfo)s', str(i+1), '%(jobs)s', 'tmp.%s' % (i+1,)), config) outfile = 'rootingHazards.%s' % (i+1,) output = open(outfile, 'w') - if config['verbose']: - print_command(command, outfile=outfile, env=env(config)) + print_command(command, outfile=outfile, env=env(config)) jobs.append((command, Popen(command, stdout=output, env=env(config)))) final_status = 0 @@ -93,8 +91,7 @@ def generate_hazards(config, outfilename): with open(outfilename, 'w') as output: command = ['cat'] + [ 'rootingHazards.%s' % (i+1,) for i in range(int(config['jobs'])) ] - if config['verbose']: - print_command(command, outfile=outfilename) + print_command(command, outfile=outfilename) subprocess.call(command, stdout=output) JOBS = { 'dbs': @@ -114,7 +111,7 @@ JOBS = { 'dbs': ()), 'callgraph': - (('%(js)s', '%(analysis_scriptdir)s/computeCallgraph.js', '%(typeInfo)s'), + (('%(js)s', '%(analysis_scriptdir)s/computeCallgraph.js'), 'callgraph.txt'), 'gcFunctions': @@ -123,9 +120,8 @@ JOBS = { 'dbs': ('gcFunctions.txt', 'gcFunctions.lst', 'gcEdges.txt', 'suppressedFunctions.lst')), 'gcTypes': - (('%(js)s', '%(analysis_scriptdir)s/computeGCTypes.js', - '[gcTypes]', '[typeInfo]'), - ('gcTypes.txt', 'typeInfo.txt')), + (('%(js)s', '%(analysis_scriptdir)s/computeGCTypes.js',), + 'gcTypes.txt'), 'allFunctions': (('%(sixgill_bin)s/xdbkeys', 'src_body.xdb',), @@ -159,8 +155,7 @@ def run_job(name, config): if isinstance(outfiles, basestring): stdout_filename = '%s.tmp' % name temp_map[stdout_filename] = outfiles - if config['verbose']: - print_command(cmdspec, outfile=outfiles, env=env(config)) + print_command(cmdspec, outfile=outfiles, env=env(config)) else: stdout_filename = None pc = list(cmdspec) @@ -168,8 +163,7 @@ def run_job(name, config): for (i, name) in out_indexes(cmdspec): pc[i] = outfiles[outfile] outfile += 1 - if config['verbose']: - print_command(pc, env=env(config)) + print_command(pc, env=env(config)) command = list(cmdspec) outfile = 0 @@ -196,6 +190,15 @@ config = { 'ANALYSIS_SCRIPTDIR': os.path.dirname(__file__) } defaults = [ '%s/defaults.py' % config['ANALYSIS_SCRIPTDIR'], '%s/defaults.py' % os.getcwd() ] +for default in defaults: + try: + execfile(default, config) + print("Loaded %s" % default) + except: + pass + +data = config.copy() + parser = argparse.ArgumentParser(description='Statically analyze build tree for rooting hazards.') parser.add_argument('step', metavar='STEP', type=str, nargs='?', help='run starting from this step') @@ -217,21 +220,8 @@ parser.add_argument('--tag', '-t', type=str, nargs='?', help='name of job, also sets build command to "build."') parser.add_argument('--expect-file', type=str, nargs='?', help='deprecated option, temporarily still present for backwards compatibility') -parser.add_argument('--verbose', '-v', action='store_true', - help='Display cut & paste commands to run individual steps') args = parser.parse_args() - -for default in defaults: - try: - execfile(default, config) - if args.verbose: - print("Loaded %s" % default) - except: - pass - -data = config.copy() - for k,v in vars(args).items(): if v is not None: data[k] = v @@ -242,7 +232,7 @@ if args.tag and not args.buildcommand: if args.jobs is not None: data['jobs'] = args.jobs if not data.get('jobs'): - data['jobs'] = subprocess.check_output(['nproc', '--ignore=1']).strip() + data['jobs'] = subprocess.check_output(['nproc', '--ignore=1']) if args.buildcommand: data['buildcommand'] = args.buildcommand @@ -261,8 +251,8 @@ if not data.get('source') and data.get('sixgill_bin'): data['source'] = path.replace("/js/src/jsapi.cpp", "") steps = [ 'dbs', - 'gcTypes', 'callgraph', + 'gcTypes', 'gcFunctions', 'allFunctions', 'hazards', @@ -286,7 +276,7 @@ for step in steps: for (i, name) in out_indexes(command): data[name] = outfiles[outfile] outfile += 1 - assert len(outfiles) == outfile, 'step \'%s\': mismatched number of output files (%d) and params (%d)' % (step, outfile, len(outfiles)) + assert len(outfiles) == outfile, 'step \'%s\': mismatched number of output files and params' % step if args.step: steps = steps[steps.index(args.step):] diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index a0fa94e061c2..09b4de127a62 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -12,7 +12,7 @@ var functionName; var functionBodies; if (typeof scriptArgs[0] != 'string' || typeof scriptArgs[1] != 'string') - throw "Usage: analyzeRoots.js [-f function_name] [start end [tmpfile]]"; + throw "Usage: analyzeRoots.js [-f function_name] [start end [tmpfile]]"; var theFunctionNameToFind; if (scriptArgs[0] == '--function') { @@ -20,16 +20,13 @@ if (scriptArgs[0] == '--function') { scriptArgs = scriptArgs.slice(2); } -var gcFunctionsFile = scriptArgs[0] || "gcFunctions.lst"; -var gcEdgesFile = scriptArgs[1] || "gcEdges.txt"; -var suppressedFunctionsFile = scriptArgs[2] || "suppressedFunctions.lst"; -var gcTypesFile = scriptArgs[3] || "gcTypes.txt"; -var typeInfoFile = scriptArgs[4] || "typeInfo.txt"; -var batch = (scriptArgs[5]|0) || 1; -var numBatches = (scriptArgs[6]|0) || 1; -var tmpfile = scriptArgs[7] || "tmp.txt"; - -GCSuppressionTypes = loadTypeInfo(typeInfoFile)["Suppress GC"] || []; +var gcFunctionsFile = scriptArgs[0]; +var gcEdgesFile = scriptArgs[1]; +var suppressedFunctionsFile = scriptArgs[2]; +var gcTypesFile = scriptArgs[3]; +var batch = (scriptArgs[4]|0) || 1; +var numBatches = (scriptArgs[5]|0) || 1; +var tmpfile = scriptArgs[6] || "tmp.txt"; var gcFunctions = {}; var text = snarf("gcFunctions.lst").split("\n"); @@ -334,57 +331,31 @@ function edgeCanGC(edge) // // - 'gcInfo': a direct pointer to the GC call edge // -function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) +function findGCBeforeVariableUse(suppressed, variable, worklist) { // Scan through all edges preceding an unrooted variable use, using an // explicit worklist, looking for a GC call. A worklist contains an // incoming edge together with a description of where it or one of its // successors GC'd (if any). - var bodies_visited = new Map(); - - let worklist = [{body: start_body, ppoint: start_point, preGCLive: false, gcInfo: null, why: null}]; while (worklist.length) { - // Grab an entry off of the worklist, representing a point within the - // CFG identified by . If this point has a descendant - // later in the CFG that can GC, gcInfo will be set to the information - // about that GC call. - var entry = worklist.pop(); - var { body, ppoint, gcInfo, preGCLive } = entry; + var { body, ppoint, gcInfo } = entry; - // Handle the case where there are multiple ways to reach this point - // (traversing backwards). - var visited = bodies_visited.get(body); - if (!visited) - bodies_visited.set(body, visited = new Map()); - if (visited.has(ppoint)) { - var seenEntry = visited.get(ppoint); - - // This point already knows how to GC through some other path, so - // we have nothing new to learn. (The other path will consider the - // predecessors.) - if (seenEntry.gcInfo) - continue; - - // If this worklist's entry doesn't know of any way to GC, then - // there's no point in continuing the traversal through it. Perhaps - // another edge will be found that *can* GC; otherwise, the first - // route to the point will traverse through predecessors. - // - // Note that this means we may visit a point more than once, if the - // first time we visit we don't have a known reachable GC call and - // the second time we do. - if (!gcInfo) - continue; + if (body.seen) { + if (ppoint in body.seen) { + var seenEntry = body.seen[ppoint]; + if (!gcInfo || seenEntry.gcInfo) + continue; + } + } else { + body.seen = []; } - visited.set(ppoint, {body: body, gcInfo: gcInfo}); + body.seen[ppoint] = {body: body, gcInfo: gcInfo}; - // Check for hitting the entry point of the current body (which may be - // the outer function or a loop within it.) if (ppoint == body.Index[0]) { if (body.BlockId.Kind == "Loop") { - // Propagate to outer body parents that enter the loop body. + // propagate to parents that enter the loop body. if ("BlockPPoint" in body) { for (var parent of body.BlockPPoint) { var found = false; @@ -402,13 +373,7 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) } else if (variable.Kind == "Arg" && gcInfo) { // The scope of arguments starts at the beginning of the // function - return entry; - } else if (entry.preGCLive) { - // We didn't find a "good" explanation beginning of the live - // range, but we do know the variable was live across the GC. - // This can happen if the live range started when a variable is - // used as a retparam. - return entry; + return {gcInfo: gcInfo, why: entry}; } } @@ -434,52 +399,25 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) // to a use after the GC call that proves its live range // extends at least that far. if (gcInfo) - return {body: body, ppoint: source, gcInfo: gcInfo, why: entry }; + return {gcInfo: gcInfo, why: {body: body, ppoint: source, gcInfo: gcInfo, why: entry } } - // Otherwise, keep searching through the graph, but truncate - // this particular branch of the search at this edge. + // Otherwise, we want to continue searching for the true + // minimumUse, for use in reporting unnecessary rooting, but we + // truncate this particular branch of the search at this edge. continue; } - var src_gcInfo = gcInfo; - var src_preGCLive = preGCLive; if (!gcInfo && !(source in body.suppressed) && !suppressed) { var gcName = edgeCanGC(edge, body); if (gcName) - src_gcInfo = {name:gcName, body:body, ppoint:source}; + gcInfo = {name:gcName, body:body, ppoint:source}; } if (edge_uses) { // The live range starts at least this far back, so we're done - // for the same reason as with edge_kills. The only difference - // is that a GC on this edge indicates a hazard, whereas if - // we're killing a live range in the GC call then it's not live - // *across* the call. - // - // However, we may want to generate a longer usage chain for - // the variable than is minimally necessary. For example, - // consider: - // - // Value v = f(); - // if (v.isUndefined()) - // return false; - // gc(); - // return v; - // - // The call to .isUndefined() is considered to be a use and - // therefore indicates that v must be live at that point. But - // it's more helpful to the user to continue the 'why' path to - // include the ancestor where the value was generated. So we - // will only return here if edge.Kind is Assign; otherwise, - // we'll pass a "preGCLive" value up through the worklist to - // remember that the variable *is* alive before the GC and so - // this function should be returning a true value. - - if (src_gcInfo) { - src_preGCLive = true; - if (edge.Kind == 'Assign') - return {body:body, ppoint:source, gcInfo:src_gcInfo, why:entry}; - } + // for the same reason as with edge_kills. + if (gcInfo) + return {gcInfo:gcInfo, why:entry}; } if (edge.Kind == "Loop") { @@ -491,8 +429,7 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) assert(!found); found = true; worklist.push({body:xbody, ppoint:xbody.Index[1], - preGCLive: src_preGCLive, gcInfo:src_gcInfo, - why:entry}); + gcInfo:gcInfo, why:entry}); } } assert(found); @@ -500,9 +437,7 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) } // Propagate the search to the predecessors of this edge. - worklist.push({body:body, ppoint:source, - preGCLive: src_preGCLive, gcInfo:src_gcInfo, - why:entry}); + worklist.push({body:body, ppoint:source, gcInfo:gcInfo, why:entry}); } } @@ -511,12 +446,13 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) function variableLiveAcrossGC(suppressed, variable) { - // A variable is live across a GC if (1) it is used by an edge (as in, it - // was at least initialized), and (2) it is used after a GC in a successor - // edge. + // A variable is live across a GC if (1) it is used by an edge, and (2) it + // is used after a GC in a successor edge. - for (var body of functionBodies) + for (var body of functionBodies) { + body.seen = null; body.minimumUse = 0; + } for (var body of functionBodies) { if (!("PEdge" in body)) @@ -531,7 +467,8 @@ function variableLiveAcrossGC(suppressed, variable) // if (usePoint && !edgeKillsVariable(edge, variable)) { // Found a use, possibly after a GC. - var call = findGCBeforeVariableUse(body, usePoint, suppressed, variable); + var worklist = [{body:body, ppoint:usePoint, gcInfo:null, why:null}]; + var call = findGCBeforeVariableUse(suppressed, variable, worklist); if (!call) continue; @@ -564,9 +501,7 @@ function unsafeVariableAddressTaken(suppressed, variable) return null; } -// Read out the brief (non-JSON, semi-human-readable) CFG description for the -// given function and store it. -function loadPrintedLines(functionName) +function computePrintedLines(functionName) { assert(!os.system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile)); var lines = snarf(tmpfile).split('\n'); @@ -601,21 +536,13 @@ function loadPrintedLines(functionName) } } -function findLocation(body, ppoint, opts={brief: false}) +function findLocation(body, ppoint) { var location = body.PPoint[ppoint - 1].Location; - var file = location.CacheString; - - if (file.indexOf(sourceRoot) == 0) - file = file.substring(sourceRoot.length); - - if (opts.brief) { - var m = /.*\/(.*)/.exec(file); - if (m) - file = m[1]; - } - - return file + ":" + location.Line; + var text = location.CacheString + ":" + location.Line; + if (text.indexOf(sourceRoot) == 0) + return text.substring(sourceRoot.length); + return text; } function locationLine(text) @@ -630,11 +557,11 @@ function printEntryTrace(functionName, entry) var gcPoint = entry.gcInfo ? entry.gcInfo.ppoint : 0; if (!functionBodies[0].lines) - loadPrintedLines(functionName); + computePrintedLines(functionName); while (entry) { var ppoint = entry.ppoint; - var lineText = findLocation(entry.body, ppoint, {"brief": true}); + var lineText = findLocation(entry.body, ppoint); var edgeText = ""; if (entry.why && entry.why.body == entry.body) { @@ -645,8 +572,8 @@ function printEntryTrace(functionName, entry) var table = {}; entry.body.edgeTable = table; for (var line of entry.body.lines) { - if (match = /\((\d+,\d+),/.exec(line)) - table[match[1]] = line; // May be multiple? + if (match = /\((\d+),(\d+),/.exec(line)) + table[match[1] + "," + match[2]] = line; // May be multiple? } } @@ -732,7 +659,7 @@ function processBodies(functionName) " of type '" + typeDesc(variable.Type) + "'" + " live across GC call " + result.gcInfo.name + " at " + lineText); - printEntryTrace(functionName, result); + printEntryTrace(functionName, result.why); } result = unsafeVariableAddressTaken(suppressed, variable.Variable); if (result) { @@ -756,9 +683,9 @@ var minStream = xdb.min_data_stream()|0; var maxStream = xdb.max_data_stream()|0; var N = (maxStream - minStream) + 1; -var start = Math.floor((batch - 1) / numBatches * N) + minStream; -var start_next = Math.floor(batch / numBatches * N) + minStream; -var end = start_next - 1; +var each = Math.floor(N/numBatches); +var start = minStream + each * (batch - 1); +var end = Math.min(minStream + each * batch - 1, maxStream); function process(name, json) { functionName = name; diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index ea26539dc64d..b9072dbfb8f2 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -2,10 +2,6 @@ "use strict"; -// RAII types within which we should assume GC is suppressed, eg -// AutoSuppressGC. -var GCSuppressionTypes = []; - // Ignore calls made through these function pointers var ignoreIndirectCalls = { "mallocSizeOf" : true, @@ -85,7 +81,6 @@ var ignoreCallees = { "std::strstreambuf._M_alloc_fun" : true, "std::strstreambuf._M_free_fun" : true, "struct js::gc::Callback.op" : true, - "mozilla::ThreadSharedFloatArrayBufferList::Storage.mFree" : true, }; function fieldCallCannotGC(csu, fullfield) @@ -151,17 +146,10 @@ function ignoreEdgeAddressTaken(edge) return false; } -// Return whether csu.method is one that we claim can never GC. -function isSuppressedVirtualMethod(csu, method) -{ - return csu == "nsISupports" && (method == "AddRef" || method == "Release"); -} - // Ignore calls of these functions (so ignore any stack containing these) var ignoreFunctions = { "ptio.c:pt_MapError" : true, "je_malloc_printf" : true, - "vprintf_stderr" : true, "PR_ExplodeTime" : true, "PR_ErrorInstallTable" : true, "PR_SetThreadPrivate" : true, @@ -192,7 +180,6 @@ var ignoreFunctions = { // up wrapping a pending exception. See bug 898815 for the heavyweight fix. "void js::AutoCompartment::~AutoCompartment(int32)" : true, "void JSAutoCompartment::~JSAutoCompartment(int32)" : true, - "void js::AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()" : true, // Bug 948646 - the only thing AutoJSContext's constructor calls // is an Init() routine whose entire body is covered with an @@ -225,13 +212,6 @@ var ignoreFunctions = { "uint64 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true, "uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true, "void js::Nursery::freeMallocedBuffers()" : true, - - // It would be cool to somehow annotate that nsTHashtable will use - // nsTHashtable::s_MatchEntry for its matchEntry function pointer, but - // there is no mechanism for that. So we will just annotate a particularly - // troublesome logging-related usage. - "EntryType* nsTHashtable::PutEntry(nsTHashtable::KeyType) [with EntryType = nsBaseHashtableET >; nsTHashtable::KeyType = const char*]" : true, - "EntryType* nsTHashtable::GetEntry(nsTHashtable::KeyType) const [with EntryType = nsBaseHashtableET >; nsTHashtable::KeyType = const char*]" : true, }; function isProtobuf(name) @@ -333,31 +313,16 @@ function isUnsafeStorage(typeName) return typeName.startsWith('UniquePtr<'); } -function isSuppressConstructor(edgeType, varName) +function isSuppressConstructor(varName) { - // Check whether this could be a constructor - if (edgeType.Kind != 'Function') - return false; - if (!('TypeFunctionCSU' in edgeType)) - return false; - if (edgeType.Type.Kind != 'Void') - return false; - - // Check whether the type is a known suppression type. - var type = edgeType.TypeFunctionCSU.Type.Name; - if (GCSuppressionTypes.indexOf(type) == -1) - return false; - - // And now make sure this is the constructor, not some other method on a - // suppression type. varName[0] contains the qualified name. - var [ mangled, unmangled ] = splitFunction(varName[0]); - if (mangled.search(/C\dE/) == -1) - return false; // Mangled names of constructors have CE - var m = unmangled.match(/([~\w]+)(?:<.*>)?\(/); - if (!m) - return false; - var type_stem = type.replace(/\w+::/g, '').replace(/\<.*\>/g, ''); - return m[1] == type_stem; + // varName[1] contains the unqualified name + return [ + "AutoSuppressGC", + "AutoAssertGCCallback", + "AutoEnterAnalysis", + "AutoSuppressGCAnalysis", + "AutoIgnoreRootingHazards" + ].indexOf(varName[1]) != -1; } // nsISupports subclasses' methods may be scriptable (or overridden diff --git a/js/src/devtools/rootAnalysis/build/sixgill.manifest b/js/src/devtools/rootAnalysis/build/sixgill.manifest index d02bb1bf4226..de0d12c709c6 100644 --- a/js/src/devtools/rootAnalysis/build/sixgill.manifest +++ b/js/src/devtools/rootAnalysis/build/sixgill.manifest @@ -1,10 +1,10 @@ [ -{ -"digest" : "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029", -"size" : 2631908, -"hg_id" : "8cb9c3fb039a+ tip", -"unpack" : true, -"filename" : "sixgill.tar.xz", -"algorithm" : "sha512" -} + { + "hg_id" : "cd93f15a30ce", + "algorithm" : "sha512", + "digest" : "541eb3842ab6b91bd87223cad7a5e4387ef3e496e5b580c8047b8b586bc7eb69fecf3c9eb8c45a1e0deebb53554f0e8acedfe1b4ca64d93b6d008f3f2eb11389", + "filename" : "sixgill.tar.xz", + "size" : 2626640, + "unpack" : true + } ] diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index dab3f76216f6..5aeae6af843d 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -12,19 +12,25 @@ if (scriptArgs[0] == '--function') { scriptArgs = scriptArgs.slice(2); } -var typeInfo_filename = scriptArgs[0] || "typeInfo.txt"; +var subclasses = {}; +var superclasses = {}; +var classFunctions = {}; -var subclasses = new Map(); // Map from csu => set of immediate subclasses -var superclasses = new Map(); // Map from csu => set of immediate superclasses -var classFunctions = new Map(); // Map from "csu:name" => set of full method name +var fieldCallSeen = {}; -var virtualResolutionsSeen = new Set(); - -function addEntry(map, name, entry) +function addClassEntry(index, name, other) { - if (!map.has(name)) - map.set(name, new Set()); - map.get(name).add(entry); + if (!(name in index)) { + index[name] = [other]; + return; + } + + for (var entry of index[name]) { + if (entry == other) + return; + } + + index[name].push(other); } // CSU is "Class/Struct/Union" @@ -37,111 +43,86 @@ function processCSU(csuName, csu) var superclass = field.Field[1].Type.Name; var subclass = field.Field[1].FieldCSU.Type.Name; assert(subclass == csuName); - addEntry(subclasses, superclass, subclass); - addEntry(superclasses, subclass, superclass); + addClassEntry(subclasses, superclass, subclass); + addClassEntry(superclasses, subclass, superclass); } if ("Variable" in field) { // Note: not dealing with overloading correctly. var name = field.Variable.Name[0]; var key = csuName + ":" + field.Field[0].Name[0]; - addEntry(classFunctions, key, name); + if (!(key in classFunctions)) + classFunctions[key] = []; + classFunctions[key].push(name); } } } -// Return the nearest ancestor method definition, or all nearest definitions in -// the case of multiple inheritance. -function nearestAncestorMethods(csu, method) +function findVirtualFunctions(initialCSU, field, suppressed) { - var key = csu + ":" + method; + var worklist = [initialCSU]; + var functions = []; - if (classFunctions.has(key)) - return new Set(classFunctions.get(key)); + // Virtual call targets on subclasses of nsISupports may be incomplete, + // if the interface is scriptable. Just treat all indirect calls on + // nsISupports objects as potentially GC'ing, except AddRef/Release + // which should never enter the JS engine (even when calling dtors). + while (worklist.length) { + var csu = worklist.pop(); + if (csu == "nsISupports" && (field == "AddRef" || field == "Release")) { + suppressed[0] = true; + return []; + } + if (isOverridableField(initialCSU, csu, field)) { + // We will still resolve the virtual function call, because it's + // nice to have as complete a callgraph as possible for other uses. + // But push a token saying that we can run arbitrary code. + functions.push(null); + } - var functions = new Set(); - if (superclasses.has(csu)) { - for (var parent of superclasses.get(csu)) - functions.update(nearestAncestorMethods(parent, method)); + if (csu in superclasses) { + for (var superclass of superclasses[csu]) + worklist.push(superclass); + } + } + + worklist = [csu]; + while (worklist.length) { + var csu = worklist.pop(); + var key = csu + ":" + field; + + if (key in classFunctions) { + for (var name of classFunctions[key]) + functions.push(name); + } + + if (csu in subclasses) { + for (var subclass of subclasses[csu]) + worklist.push(subclass); + } } return functions; } -// Return [ instantations, suppressed ], where instantiations is a Set of all -// possible implementations of 'field' given static type 'initialCSU', plus -// null if arbitrary other implementations are possible, and suppressed is true -// if we the method is assumed to be non-GC'ing by annotation. -function findVirtualFunctions(initialCSU, field) -{ - var worklist = [initialCSU]; - var functions = new Set(); - - // Loop through all methods of initialCSU (by looking at all methods of ancestor csus). - // - // If field is nsISupports::AddRef or ::Release, return an empty list and a - // boolean that says we assert that it cannot GC. - // - // If this is a method that is annotated to be dangerous (eg, it could be - // overridden with an implementation that could GC), then use null as a - // signal value that it should be considered to GC, even though we'll also - // collect all of the instantiations for other purposes. - - while (worklist.length) { - var csu = worklist.pop(); - if (isSuppressedVirtualMethod(csu, field)) - return [ new Set(), true ]; - if (isOverridableField(initialCSU, csu, field)) { - // We will still resolve the virtual function call, because it's - // nice to have as complete a callgraph as possible for other uses. - // But push a token saying that we can run arbitrary code. - functions.add(null); - } - - if (superclasses.has(csu)) - worklist.push(...superclasses.get(csu)); - } - - // Now return a list of all the instantiations of the method named 'field' - // that could execute on an instance of initialCSU or a descendant class. - - // Start with the class itself, or if it doesn't define the method, all - // nearest ancestor definitions. - functions.update(nearestAncestorMethods(initialCSU, field)); - - // Then recurse through all descendants to add in their definitions. - var worklist = [initialCSU]; - while (worklist.length) { - var csu = worklist.pop(); - var key = csu + ":" + field; - - if (classFunctions.has(key)) - functions.update(classFunctions.get(key)); - - if (subclasses.has(csu)) - worklist.push(...subclasses.get(csu)); - } - - return [ functions, false ]; -} - -var memoized = new Map(); +var memoized = {}; var memoizedCount = 0; function memo(name) { - if (!memoized.has(name)) { - let id = memoized.size + 1; - memoized.set(name, "" + id); - print(`#${id} ${name}`); + if (!(name in memoized)) { + memoizedCount++; + memoized[name] = "" + memoizedCount; + print("#" + memoizedCount + " " + name); } - return memoized.get(name); + return memoized[name]; } +var seenCallees = null; +var seenSuppressedCallees = null; + // Return a list of all callees that the given edge might be a call to. Each // one is represented by an object with a 'kind' field that is one of -// ('direct', 'field', 'resolved-field', 'indirect', 'unknown'), though note -// that 'resolved-field' is really a global record of virtual method -// resolutions, indepedent of this particular edge. +// ('direct', 'field', 'indirect', 'unknown'). function getCallees(edge) { if (edge.Kind != "Call") @@ -158,42 +139,42 @@ function getCallees(edge) var field = callee.Exp[0].Field; var fieldName = field.Name[0]; var csuName = field.FieldCSU.Type.Name; - var functions; + var functions = null; if ("FieldInstanceFunction" in field) { - let suppressed; - [ functions, suppressed ] = findVirtualFunctions(csuName, fieldName, suppressed); - if (suppressed) { + var suppressed = [ false ]; + functions = findVirtualFunctions(csuName, fieldName, suppressed); + if (suppressed[0]) { // Field call known to not GC; mark it as suppressed so // direct invocations will be ignored callees.push({'kind': "field", 'csu': csuName, 'field': fieldName, 'suppressed': true}); } - } else { - functions = new Set([null]); // field call } - - // Known set of virtual call targets. Treat them as direct calls to - // all possible resolved types, but also record edges from this - // field call to each final callee. When the analysis is checking - // whether an edge can GC and it sees an unrooted pointer held live - // across this field call, it will know whether any of the direct - // callees can GC or not. - var targets = []; - var fullyResolved = true; - for (var name of functions) { - if (name === null) { - // Unknown set of call targets, meaning either a function - // pointer call ("field call") or a virtual method that can - // be overridden in extensions. - callees.push({'kind': "field", 'csu': csuName, 'field': fieldName}); - fullyResolved = false; - } else { - callees.push({'kind': "direct", 'name': name}); - targets.push({'kind': "direct", 'name': name}); + if (functions) { + // Known set of virtual call targets. Treat them as direct + // calls to all possible resolved types, but also record edges + // from this field call to each final callee. When the analysis + // is checking whether an edge can GC and it sees an unrooted + // pointer held live across this field call, it will know + // whether any of the direct callees can GC or not. + var targets = []; + var fullyResolved = true; + for (var name of functions) { + if (name === null) { + // virtual call on an nsISupports object + callees.push({'kind': "field", 'csu': csuName, 'field': fieldName}); + fullyResolved = false; + } else { + callees.push({'kind': "direct", 'name': name}); + targets.push({'kind': "direct", 'name': name}); + } } + if (fullyResolved) + callees.push({'kind': "resolved-field", 'csu': csuName, 'field': fieldName, 'callees': targets}); + } else { + // Unknown set of call targets. Non-virtual field call. + callees.push({'kind': "field", 'csu': csuName, 'field': fieldName}); } - if (fullyResolved) - callees.push({'kind': "resolved-field", 'csu': csuName, 'field': fieldName, 'callees': targets}); } else if (callee.Exp[0].Kind == "Var") { // indirect call through a variable. callees.push({'kind': "indirect", 'variable': callee.Exp[0].Variable.Name[0]}); @@ -238,6 +219,7 @@ function getTags(functionName, body) { var tags = new Set(); var annotations = getAnnotations(body); if (functionName in annotations) { + print("crawling through"); for (var [ annName, annValue ] of annotations[functionName]) { if (annName == 'Tag') tags.add(annValue); @@ -254,45 +236,39 @@ function processBody(functionName, body) for (var tag of getTags(functionName, body).values()) print("T " + memo(functionName) + " " + tag); - // Set of all callees that have been output so far, in order to suppress - // repeated callgraph edges from being recorded. Use a separate set for - // suppressed callees, since we don't want a suppressed edge (within one - // RAII scope) to prevent an unsuppressed edge from being recorded. The - // seen array is indexed by a boolean 'suppressed' variable. - var seen = [ new Set(), new Set() ]; - lastline = null; for (var edge of body.PEdge) { if (edge.Kind != "Call") continue; - - // Whether this call is within the RAII scope of a GC suppression class - var edgeSuppressed = (edge.Index[0] in body.suppressed); - + var edgeSuppressed = false; + var seen = seenCallees; + if (edge.Index[0] in body.suppressed) { + edgeSuppressed = true; + seen = seenSuppressedCallees; + } for (var callee of getCallees(edge)) { - var suppressed = Boolean(edgeSuppressed || callee.suppressed); - var prologue = suppressed ? "SUPPRESS_GC " : ""; + var prologue = (edgeSuppressed || callee.suppressed) ? "SUPPRESS_GC " : ""; prologue += memo(functionName) + " "; if (callee.kind == 'direct') { - if (!seen[+suppressed].has(callee.name)) { - seen[+suppressed].add(callee.name); + if (!(callee.name in seen)) { + seen[callee.name] = true; printOnce("D " + prologue + memo(callee.name)); } } else if (callee.kind == 'field') { var { csu, field } = callee; printOnce("F " + prologue + "CLASS " + csu + " FIELD " + field); } else if (callee.kind == 'resolved-field') { - // Fully-resolved field (virtual method) call. Record the - // callgraph edges. Do not consider suppression, since it is - // local to this callsite and we are writing out a global + // Fully-resolved field call (usually a virtual method). Record + // the callgraph edges. Do not consider suppression, since it + // is local to this callsite and we are writing out a global // record here. // // Any field call that does *not* have an R entry must be // assumed to call anything. var { csu, field, callees } = callee; var fullFieldName = csu + "." + field; - if (!virtualResolutionsSeen.has(fullFieldName)) { - virtualResolutionsSeen.add(fullFieldName); + if (!(fullFieldName in fieldCallSeen)) { + fieldCallSeen[fullFieldName] = true; for (var target of callees) printOnce("R " + memo(fullFieldName) + " " + memo(target.name)); } @@ -308,7 +284,7 @@ function processBody(functionName, body) } } -GCSuppressionTypes = loadTypeInfo(typeInfo_filename)["Suppress GC"] || []; +var callgraph = {}; var xdb = xdbLibrary(); xdb.open("src_comp.xdb"); @@ -346,12 +322,14 @@ function process(functionName, functionBodies) { for (var body of functionBodies) body.suppressed = []; - for (var body of functionBodies) { for (var [pbody, id] of allRAIIGuardedCallPoints(functionBodies, body, isSuppressConstructor)) pbody.suppressed[id] = true; } + seenCallees = {}; + seenSuppressedCallees = {}; + for (var body of functionBodies) processBody(functionName, body); @@ -373,7 +351,7 @@ function process(functionName, functionBodies) // Bug 1056410: Oh joy. GCC does something even funkier internally, // where it generates calls to ~Foo() but a body for ~Foo(int32) even // though it uses the same mangled name for both. So we need to add a - // synthetic edge from ~Foo() -> ~Foo(int32). + // synthetic edge from the former to the latter. // // inChargeXTor will have the (int32). if (functionName.indexOf("::~") > 0) { @@ -391,7 +369,7 @@ function process(functionName, functionBodies) // D1 # complete object destructor // D2 # base object destructor // - // In actual practice, I have observed C4 and D4 xtors generated by gcc + // In actual practice, I have observed a C4 constructor generated by gcc // 4.9.3 (but not 4.7.3). The gcc source code says: // // /* This is the old-style "[unified]" constructor. @@ -399,29 +377,23 @@ function process(functionName, functionBodies) // it from the clones in order to share code and save space. */ // // Unfortunately, that "call... from the clones" does not seem to appear in - // the CFG we get from GCC. So if we see a C4 constructor or D4 destructor, - // inject an edge to it from C1, C2, and C3 (or D1, D2, and D3). (Note that - // C3 isn't even used in current GCC, but add the edge anyway just in - // case.) - if (functionName.indexOf("C4E") != -1 || functionName.indexOf("D4Ev") != -1) { + // the CFG we get from GCC. So if we see a C4 constructor, inject an edge + // to it from C1, C2, and C3. (Note that C3 isn't even used in current GCC, + // but add the edge anyway just in case.) + if (functionName.indexOf("C4E") != -1) { var [ mangled, unmangled ] = splitFunction(functionName); // E terminates the method name (and precedes the method parameters). - // If eg "C4E" shows up in the mangled name for another reason, this - // will create bogus edges in the callgraph. But will affect little and - // is somewhat difficult to avoid, so we will live with it. - for (let [synthetic, variant] of [['C4E', 'C1E'], - ['C4E', 'C2E'], - ['C4E', 'C3E'], - ['D4Ev', 'D1Ev'], - ['D4Ev', 'D2Ev'], - ['D4Ev', 'D3Ev']]) - { - if (mangled.indexOf(synthetic) == -1) - continue; - - let variant_mangled = mangled.replace(synthetic, variant); - let variant_full = variant_mangled + "$" + unmangled; - print("D " + memo(variant_full) + " " + memo(functionName)); + if (mangled.indexOf("C4E") != -1) { + // If "C4E" shows up in the mangled name for another reason, this + // will create bogus edges in the callgraph. But that shouldn't + // matter too much, and is somewhat difficult to avoid, so we will + // live with it. + var C1 = mangled.replace("C4E", "C1E"); + var C2 = mangled.replace("C4E", "C2E"); + var C3 = mangled.replace("C4E", "C3E"); + print("D " + memo(C1) + " " + memo(mangled)); + print("D " + memo(C2) + " " + memo(mangled)); + print("D " + memo(C3) + " " + memo(mangled)); } } } diff --git a/js/src/devtools/rootAnalysis/computeGCFunctions.js b/js/src/devtools/rootAnalysis/computeGCFunctions.js index 97efcb38af22..086cfef35886 100644 --- a/js/src/devtools/rootAnalysis/computeGCFunctions.js +++ b/js/src/devtools/rootAnalysis/computeGCFunctions.js @@ -21,20 +21,16 @@ loadCallgraph(callgraph_filename); printErr("Writing " + gcFunctions_filename); redirect(gcFunctions_filename); - for (var name in gcFunctions) { - for (let readable of readableNames[name]) { - print(""); - print("GC Function: " + name + "$" + readable); - let current = name; - do { - current = gcFunctions[current]; - if (current in readableNames) - print(" " + readableNames[current][0]); - else - print(" " + current); - } while (current in gcFunctions); - } + print(""); + print("GC Function: " + name + "$" + readableNames[name][0]); + do { + name = gcFunctions[name]; + if (name in readableNames) + print(" " + readableNames[name][0]); + else + print(" " + name); + } while (name in gcFunctions); } printErr("Writing " + gcFunctionsList_filename); diff --git a/js/src/devtools/rootAnalysis/computeGCTypes.js b/js/src/devtools/rootAnalysis/computeGCTypes.js index af4d703896e1..f0712ecb47db 100644 --- a/js/src/devtools/rootAnalysis/computeGCTypes.js +++ b/js/src/devtools/rootAnalysis/computeGCTypes.js @@ -5,20 +5,14 @@ loadRelativeToScript('utility.js'); loadRelativeToScript('annotations.js'); -var gcTypes_filename = scriptArgs[0] || "gcTypes.txt"; -var typeInfo_filename = scriptArgs[1] || "typeInfo.txt"; - var annotations = { 'GCPointers': [], 'GCThings': [], 'NonGCTypes': {}, // unused 'NonGCPointers': {}, 'RootedPointers': {}, - 'GCSuppressors': {}, }; -var gDescriptors = new Map; // Map from descriptor string => Set of typeName - var structureParents = {}; // Map from field => list of var pointerParents = {}; // Map from field => list of var baseClasses = {}; // Map from struct name => list of base class name strings @@ -70,8 +64,6 @@ function processCSU(csu, body) annotations.NonGCPointers[csu] = true; else if (tag == 'Rooted Pointer') annotations.RootedPointers[csu] = true; - else if (tag == 'Suppress GC') - annotations.GCSuppressors[csu] = true; } } @@ -215,26 +207,6 @@ function addGCPointer(typeName) markGCType(typeName, '', '(annotation)', 1, 0, ""); } -// Add an arbitrary descriptor to a type, and apply it recursively to all base -// structs and structs that contain the given typeName as a field. -function addDescriptor(typeName, descriptor) -{ - if (!gDescriptors.has(descriptor)) - gDescriptors.set(descriptor, new Set); - let descriptorTypes = gDescriptors.get(descriptor); - if (!descriptorTypes.has(typeName)) { - descriptorTypes.add(typeName); - if (typeName in structureParents) { - for (let [holder, field] of structureParents[typeName]) - addDescriptor(holder, descriptor); - } - if (typeName in baseClasses) { - for (let base of baseClasses[typeName]) - addDescriptor(base, descriptor); - } - } -} - for (var type of listNonGCPointers()) annotations.NonGCPointers[type] = true; @@ -274,8 +246,6 @@ function explain(csu, indent, seen) { } } -var origOut = os.file.redirect(gcTypes_filename); - for (var csu in gcTypes) { print("GCThing: " + csu); explain(csu, " "); @@ -284,16 +254,3 @@ for (var csu in gcPointers) { print("GCPointer: " + csu); explain(csu, " "); } - -// Redirect output to the typeInfo file and close the gcTypes file. -os.file.close(os.file.redirect(typeInfo_filename)); - -for (let csu in annotations.GCSuppressors) - addDescriptor(csu, 'Suppress GC'); - -for (let [descriptor, types] of gDescriptors) { - for (let csu of types) - print(descriptor + "$$" + csu); -} - -os.file.close(os.file.redirect(origOut)); diff --git a/js/src/devtools/rootAnalysis/explain.py b/js/src/devtools/rootAnalysis/explain.py index dc8b76f5c687..ec7ef1949b1d 100755 --- a/js/src/devtools/rootAnalysis/explain.py +++ b/js/src/devtools/rootAnalysis/explain.py @@ -3,8 +3,6 @@ import re import argparse -from collections import defaultdict - parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument('rootingHazards', nargs='?', default='rootingHazards.txt') parser.add_argument('gcFunctions', nargs='?', default='gcFunctions.txt') @@ -24,7 +22,7 @@ try: # Map from a GC function name to the list of hazards resulting from # that GC function - hazardousGCFunctions = defaultdict(list) + hazardousGCFunctions = {} # List of tuples (gcFunction, index of hazard) used to maintain the # ordering of the hazards @@ -55,7 +53,7 @@ try: # Function names are surrounded by single quotes. Field calls # are unquoted. current_gcFunction = m.group(2) - hazardousGCFunctions[current_gcFunction].append(line) + hazardousGCFunctions.setdefault(current_gcFunction, []).append(line) hazardOrder.append((current_gcFunction, len(hazardousGCFunctions[current_gcFunction]) - 1)) num_hazards += 1 continue @@ -86,13 +84,12 @@ try: if current_func: gcExplanations[current_func] = explanation - for gcFunction, index in hazardOrder: - gcHazards = hazardousGCFunctions[gcFunction] - - if gcFunction in gcExplanations: - print >>hazards, (gcHazards[index] + gcExplanations[gcFunction]) - else: - print >>hazards, gcHazards[index] + for gcFunction, index in hazardOrder: + gcHazards = hazardousGCFunctions[gcFunction] + if gcFunction in gcExplanations: + print >>hazards, (gcHazards[index] + gcExplanations[gcFunction]) + else: + print >>hazards, gcHazards[index] except IOError as e: print 'Failed: %s' % str(e) diff --git a/js/src/devtools/rootAnalysis/loadCallgraph.js b/js/src/devtools/rootAnalysis/loadCallgraph.js index 7661aad8e53a..a221d7143764 100644 --- a/js/src/devtools/rootAnalysis/loadCallgraph.js +++ b/js/src/devtools/rootAnalysis/loadCallgraph.js @@ -53,8 +53,13 @@ function addGCFunction(caller, reason) function addCallEdge(caller, callee, suppressed) { - addToKeyedList(calleeGraph, caller, {callee:callee, suppressed:suppressed}); - addToKeyedList(callerGraph, callee, {caller:caller, suppressed:suppressed}); + if (!(caller in calleeGraph)) + calleeGraph[caller] = []; + calleeGraph[caller].push({callee:callee, suppressed:suppressed}); + + if (!(callee in callerGraph)) + callerGraph[callee] = []; + callerGraph[callee].push({caller:caller, suppressed:suppressed}); } // Map from identifier to full "mangled|readable" name. Or sometimes to a diff --git a/js/src/devtools/rootAnalysis/run-test.py b/js/src/devtools/rootAnalysis/run-test.py index 3bc9085a0aaf..a960b57d1399 100644 --- a/js/src/devtools/rootAnalysis/run-test.py +++ b/js/src/devtools/rootAnalysis/run-test.py @@ -3,87 +3,150 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +import sys import os -import site +import re +import json import subprocess -import argparse -testdir = os.path.abspath(os.path.join(os.path.dirname(__file__), 't')) -site.addsitedir(testdir) -from testlib import Test, equal +testdir = os.path.abspath(os.path.dirname(__file__)) -scriptdir = os.path.abspath(os.path.dirname(__file__)) - -parser = argparse.ArgumentParser(description='run hazard analysis tests') -parser.add_argument( - '--js', default=os.environ.get('JS'), - help='JS binary to run the tests with') -parser.add_argument( - '--sixgill', default=os.environ.get('SIXGILL', os.path.join(testdir, "sixgill")), - help='Path to root of sixgill installation') -parser.add_argument( - '--sixgill-bin', default=os.environ.get('SIXGILL_BIN'), - help='Path to sixgill binary dir') -parser.add_argument( - '--sixgill-plugin', default=os.environ.get('SIXGILL_PLUGIN'), - help='Full path to sixgill gcc plugin') -parser.add_argument( - '--gccdir', default=os.environ.get('GCCDIR'), - help='Path to GCC installation dir') -parser.add_argument( - '--cc', default=os.environ.get('CC'), - help='Path to gcc') -parser.add_argument( - '--cxx', default=os.environ.get('CXX'), - help='Path to g++') -parser.add_argument( - '--verbose', '-v', action='store_true', - help='Display verbose output, including commands executed') -parser.add_argument( - 'tests', nargs='*', default=['sixgill-tree', 'suppression', 'hazards', 'exceptions'], - help='tests to run') - -cfg = parser.parse_args() - -if not cfg.js: - exit('Must specify JS binary through environment variable or --js option') -if not cfg.cc: - if cfg.gccdir: - cfg.cc = os.path.join(cfg.gccdir, "bin", "gcc") - else: - cfg.cc = "gcc" -if not cfg.cxx: - if cfg.gccdir: - cfg.cxx = os.path.join(cfg.gccdir, "bin", "g++") - else: - cfg.cxx = "g++" -if not cfg.sixgill_bin: - cfg.sixgill_bin = os.path.join(cfg.sixgill, "usr", "bin") -if not cfg.sixgill_plugin: - cfg.sixgill_plugin = os.path.join(cfg.sixgill, "usr", "libexec", "sixgill", "gcc", "xgill.so") - -subprocess.check_call([cfg.js, '-e', 'if (!getBuildConfiguration()["has-ctypes"]) quit(1)']) +cfg = {} +cfg['SIXGILL_ROOT'] = os.environ.get('SIXGILL', + os.path.join(testdir, "sixgill")) +cfg['SIXGILL_BIN'] = os.environ.get('SIXGILL_BIN', + os.path.join(cfg['SIXGILL_ROOT'], "usr", "bin")) +cfg['SIXGILL_PLUGIN'] = os.environ.get('SIXGILL_PLUGIN', + os.path.join(cfg['SIXGILL_ROOT'], "usr", "libexec", "sixgill", "gcc", "xgill.so")) +cfg['CC'] = os.environ.get("CC", + "gcc") +cfg['CXX'] = os.environ.get("CXX", + cfg.get('CC', 'g++')) +cfg['JS_BIN'] = os.environ["JS"] def binpath(prog): - return os.path.join(cfg.sixgill_bin, prog) + return os.path.join(cfg['SIXGILL_BIN'], prog) -try: - os.mkdir(os.path.join('t', 'out')) -except OSError: - pass +if not os.path.exists("test-output"): + os.mkdir("test-output") -for name in cfg.tests: - name = os.path.basename(name) +# Simplified version of the body info. +class Body(dict): + def __init__(self, body): + self['BlockIdKind'] = body['BlockId']['Kind'] + if 'Variable' in body['BlockId']: + self['BlockName'] = body['BlockId']['Variable']['Name'][0] + self['LineRange'] = [ body['Location'][0]['Line'], body['Location'][1]['Line'] ] + self['Filename'] = body['Location'][0]['CacheString'] + self['Edges'] = body.get('PEdge', []) + self['Points'] = { i+1: body['PPoint'][i]['Location']['Line'] for i in range(len(body['PPoint'])) } + self['Index'] = body['Index'] + self['Variables'] = { x['Variable']['Name'][0]: x['Type'] for x in body['DefineVariable'] } + + # Indexes + self['Line2Points'] = {} + for point, line in self['Points'].items(): + self['Line2Points'].setdefault(line, []).append(point) + self['SrcPoint2Edges'] = {} + for edge in self['Edges']: + (src, dst) = edge['Index'] + self['SrcPoint2Edges'].setdefault(src, []).append(edge) + self['Line2Edges'] = {} + for (src, edges) in self['SrcPoint2Edges'].items(): + line = self['Points'][src] + self['Line2Edges'].setdefault(line, []).extend(edges) + + def edges_from_line(self, line): + return self['Line2Edges'][line] + + def edge_from_line(self, line): + edges = self.edges_from_line(line) + assert(len(edges) == 1) + return edges[0] + + def edges_from_point(self, point): + return self['SrcPoint2Edges'][point] + + def edge_from_point(self, point): + edges = self.edges_from_point(point) + assert(len(edges) == 1) + return edges[0] + + def assignment_point(self, varname): + for edge in self['Edges']: + if edge['Kind'] != 'Assign': + continue + dst = edge['Exp'][0] + if dst['Kind'] != 'Var': + continue + if dst['Variable']['Name'][0] == varname: + return edge['Index'][0] + raise Exception("assignment to variable %s not found" % varname) + + def assignment_line(self, varname): + return self['Points'][self.assignment_point(varname)] + +tests = ['test'] +for name in tests: indir = os.path.join(testdir, name) - outdir = os.path.join(testdir, 'out', name) - try: + outdir = os.path.join(testdir, "test-output", name) + if not os.path.exists(outdir): os.mkdir(outdir) - except OSError: - pass - test = Test(indir, outdir, cfg) + def compile(source): + cmd = "{CXX} -c {source} -fplugin={sixgill}".format(source=os.path.join(indir, source), + CXX=cfg['CXX'], sixgill=cfg['SIXGILL_PLUGIN']) + print("Running %s" % cmd) + subprocess.check_call(["sh", "-c", cmd]) + + def load_db_entry(dbname, pattern): + if not isinstance(pattern, basestring): + output = subprocess.check_output([binpath("xdbkeys"), dbname + ".xdb"]) + entries = output.splitlines() + matches = [f for f in entries if re.search(pattern, f)] + if len(matches) == 0: + raise Exception("entry not found") + if len(matches) > 1: + raise Exception("multiple entries found") + pattern = matches[0] + + output = subprocess.check_output([binpath("xdbfind"), "-json", dbname + ".xdb", pattern]) + return json.loads(output) + + def computeGCTypes(): + file("defaults.py", "w").write('''\ +analysis_scriptdir = '{testdir}' +sixgill_bin = '{bindir}' +'''.format(testdir=testdir, bindir=cfg['SIXGILL_BIN'])) + cmd = [ + os.path.join(testdir, "analyze.py"), + "gcTypes", "--upto", "gcTypes", + "--source=%s" % indir, + "--objdir=%s" % outdir, + "--js=%s" % cfg['JS_BIN'], + ] + print("Running " + " ".join(cmd)) + output = subprocess.check_call(cmd) + + def loadGCTypes(): + gctypes = {'GCThings': [], 'GCPointers': []} + for line in file(os.path.join(outdir, "gcTypes.txt")): + m = re.match(r'^(GC\w+): (.*)', line) + if m: + gctypes[m.group(1) + 's'].append(m.group(2)) + return gctypes + + def process_body(body): + return Body(body) + + def process_bodies(bodies): + return [ process_body(b) for b in bodies ] + + def equal(got, expected): + if got != expected: + print("Got '%s', expected '%s'" % (got, expected)) os.chdir(outdir) subprocess.call(["sh", "-c", "rm *.xdb"]) - execfile(os.path.join(indir, "test.py"), {'test': test, 'equal': equal}) + execfile(os.path.join(indir, "test.py")) print("TEST-PASSED: %s" % name) diff --git a/js/src/devtools/rootAnalysis/t/exceptions/source.cpp b/js/src/devtools/rootAnalysis/t/exceptions/source.cpp deleted file mode 100644 index 14169740e8f8..000000000000 --- a/js/src/devtools/rootAnalysis/t/exceptions/source.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#define ANNOTATE(property) __attribute__((tag(property))) - -struct Cell { int f; } ANNOTATE("GC Thing"); - -extern void GC() ANNOTATE("GC Call"); - -void GC() -{ - // If the implementation is too trivial, the function body won't be emitted at all. - asm(""); -} - -class RAII_GC { - public: - RAII_GC() {} - ~RAII_GC() { GC(); } -}; - -// ~AutoSomething calls GC because of the RAII_GC field. The constructor, -// though, should *not* GC -- unless it throws an exception. Which is not -// possible when compiled with -fno-exceptions. -class AutoSomething { - RAII_GC gc; - public: - AutoSomething() : gc() { - asm(""); // Ooh, scary, this might throw an exception - } - ~AutoSomething() { - asm(""); - } -}; - -extern void usevar(Cell* cell); - -void f() { - Cell* thing = nullptr; // Live range starts here - - { - AutoSomething smth; // Constructor can GC only if exceptions are enabled - usevar(thing); // Live range ends here - } // In particular, 'thing' is dead at the destructor, so no hazard -} diff --git a/js/src/devtools/rootAnalysis/t/exceptions/test.py b/js/src/devtools/rootAnalysis/t/exceptions/test.py deleted file mode 100644 index f6d7f5e353f2..000000000000 --- a/js/src/devtools/rootAnalysis/t/exceptions/test.py +++ /dev/null @@ -1,19 +0,0 @@ -test.compile("source.cpp", '-fno-exceptions') -test.run_analysis_script('gcTypes') - -hazards = test.load_hazards() -assert(len(hazards) == 0) - -# If we compile with exceptions, then there *should* be a hazard because -# AutoSomething::AutoSomething might throw an exception, which would cause the -# partially-constructed value to be torn down, which will call ~RAII_GC. - -test.compile("source.cpp", '-fexceptions') -test.run_analysis_script('gcTypes') - -hazards = test.load_hazards() -assert(len(hazards) == 1) -hazard = hazards[0] -assert(hazard.function == 'void f()') -assert(hazard.variable == 'thing') -assert("AutoSomething::AutoSomething" in hazard.GCFunction) diff --git a/js/src/devtools/rootAnalysis/t/hazards/source.cpp b/js/src/devtools/rootAnalysis/t/hazards/source.cpp deleted file mode 100644 index 1e5191bc9445..000000000000 --- a/js/src/devtools/rootAnalysis/t/hazards/source.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#define ANNOTATE(property) __attribute__((tag(property))) - -struct Cell { int f; } ANNOTATE("GC Thing"); - -class AutoSuppressGC_Base { - public: - AutoSuppressGC_Base() {} - ~AutoSuppressGC_Base() {} -} ANNOTATE("Suppress GC"); - -class AutoSuppressGC_Child : public AutoSuppressGC_Base { - public: - AutoSuppressGC_Child() : AutoSuppressGC_Base() {} -}; - -class AutoSuppressGC { - AutoSuppressGC_Child helpImBeingSuppressed; - - public: - AutoSuppressGC() {} -}; - -extern void GC() ANNOTATE("GC Call"); -extern void invisible(); - -void GC() -{ - // If the implementation is too trivial, the function body won't be emitted at all. - asm(""); - invisible(); -} - -extern void foo(Cell*); - -void suppressedFunction() { - GC(); // Calls GC, but is always called within AutoSuppressGC -} - -void halfSuppressedFunction() { - GC(); // Calls GC, but is sometimes called within AutoSuppressGC -} - -void unsuppressedFunction() { - GC(); // Calls GC, never within AutoSuppressGC -} - -volatile static int x = 3; -volatile static int* xp = &x; -struct GCInDestructor { - ~GCInDestructor() { - invisible(); - asm(""); - *xp = 4; - GC(); - } -}; - -Cell* -f() -{ - GCInDestructor kaboom; - - Cell cell; - Cell* cell1 = &cell; - Cell* cell2 = &cell; - Cell* cell3 = &cell; - Cell* cell4 = &cell; - { - AutoSuppressGC nogc; - suppressedFunction(); - halfSuppressedFunction(); - } - foo(cell1); - halfSuppressedFunction(); - foo(cell2); - unsuppressedFunction(); - { - // Old bug: it would look from the first AutoSuppressGC constructor it - // found to the last destructor. This statement *should* have no effect. - AutoSuppressGC nogc; - } - foo(cell3); - Cell* cell5 = &cell; - foo(cell5); - - // Hazard in return value due to ~GCInDestructor - Cell* cell6 = &cell; - return cell6; -} diff --git a/js/src/devtools/rootAnalysis/t/hazards/test.py b/js/src/devtools/rootAnalysis/t/hazards/test.py deleted file mode 100644 index 5e234d18061d..000000000000 --- a/js/src/devtools/rootAnalysis/t/hazards/test.py +++ /dev/null @@ -1,36 +0,0 @@ -test.compile("source.cpp") -test.run_analysis_script('gcTypes') - -# gcFunctions should be the inverse, but we get to rely on unmangled names here. -gcFunctions = test.load_gcFunctions() -print(gcFunctions) -assert('void GC()' in gcFunctions) -assert('void suppressedFunction()' not in gcFunctions) -assert('void halfSuppressedFunction()' in gcFunctions) -assert('void unsuppressedFunction()' in gcFunctions) -assert('Cell* f()' in gcFunctions) - -hazards = test.load_hazards() -hazmap = {haz.variable: haz for haz in hazards} -assert('cell1' not in hazmap) -assert('cell2' in hazmap) -assert('cell3' in hazmap) -assert('cell4' not in hazmap) -assert('cell5' not in hazmap) -assert('cell6' not in hazmap) -assert('' in hazmap) - -# All hazards should be in f() -assert(hazmap['cell2'].function == 'Cell* f()') -assert(len(set(haz.function for haz in hazards)) == 1) - -# Check that the correct GC call is reported for each hazard. (cell3 has a -# hazard from two different GC calls; it doesn't really matter which is -# reported.) -assert(hazmap['cell2'].GCFunction == 'void halfSuppressedFunction()') -assert(hazmap['cell3'].GCFunction in ('void halfSuppressedFunction()', 'void unsuppressedFunction()')) -assert(hazmap[''].GCFunction == 'void GCInDestructor::~GCInDestructor()') - -# Type names are handy to have in the report. -assert(hazmap['cell2'].type == 'Cell*') -assert(hazmap[''].type == 'Cell*') diff --git a/js/src/devtools/rootAnalysis/t/sixgill.py b/js/src/devtools/rootAnalysis/t/sixgill.py deleted file mode 100644 index 2bdf76a49b44..000000000000 --- a/js/src/devtools/rootAnalysis/t/sixgill.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -from collections import defaultdict - -# Simplified version of the body info. -class Body(dict): - def __init__(self, body): - self['BlockIdKind'] = body['BlockId']['Kind'] - if 'Variable' in body['BlockId']: - self['BlockName'] = body['BlockId']['Variable']['Name'][0].split("$")[-1] - loc = body['Location'] - self['LineRange'] = (loc[0]['Line'], loc[1]['Line']) - self['Filename'] = loc[0]['CacheString'] - self['Edges'] = body.get('PEdge', []) - self['Points'] = { i: p['Location']['Line'] for i, p in enumerate(body['PPoint'], 1) } - self['Index'] = body['Index'] - self['Variables'] = { x['Variable']['Name'][0].split("$")[-1]: x['Type'] for x in body['DefineVariable'] } - - # Indexes - self['Line2Points'] = defaultdict(list) - for point, line in self['Points'].items(): - self['Line2Points'][line].append(point) - self['SrcPoint2Edges'] = defaultdict(list) - for edge in self['Edges']: - src, dst = edge['Index'] - self['SrcPoint2Edges'][src].append(edge) - self['Line2Edges'] = defaultdict(list) - for (src, edges) in self['SrcPoint2Edges'].items(): - line = self['Points'][src] - self['Line2Edges'][line].extend(edges) - - def edges_from_line(self, line): - return self['Line2Edges'][line] - - def edge_from_line(self, line): - edges = self.edges_from_line(line) - assert(len(edges) == 1) - return edges[0] - - def edges_from_point(self, point): - return self['SrcPoint2Edges'][point] - - def edge_from_point(self, point): - edges = self.edges_from_point(point) - assert(len(edges) == 1) - return edges[0] - - def assignment_point(self, varname): - for edge in self['Edges']: - if edge['Kind'] != 'Assign': - continue - dst = edge['Exp'][0] - if dst['Kind'] != 'Var': - continue - if dst['Variable']['Name'][0] == varname: - return edge['Index'][0] - raise Exception("assignment to variable %s not found" % varname) - - def assignment_line(self, varname): - return self['Points'][self.assignment_point(varname)] diff --git a/js/src/devtools/rootAnalysis/t/suppression/source.cpp b/js/src/devtools/rootAnalysis/t/suppression/source.cpp deleted file mode 100644 index e7b41b4cb326..000000000000 --- a/js/src/devtools/rootAnalysis/t/suppression/source.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#define ANNOTATE(property) __attribute__((tag(property))) - -struct Cell { int f; } ANNOTATE("GC Thing"); - -class AutoSuppressGC_Base { - public: - AutoSuppressGC_Base() {} - ~AutoSuppressGC_Base() {} -} ANNOTATE("Suppress GC"); - -class AutoSuppressGC_Child : public AutoSuppressGC_Base { - public: - AutoSuppressGC_Child() : AutoSuppressGC_Base() {} -}; - -class AutoSuppressGC { - AutoSuppressGC_Child helpImBeingSuppressed; - - public: - AutoSuppressGC() {} -}; - -extern void GC() ANNOTATE("GC Call"); - -void GC() -{ - // If the implementation is too trivial, the function body won't be emitted at all. - asm(""); -} - -extern void foo(Cell*); - -void suppressedFunction() { - GC(); // Calls GC, but is always called within AutoSuppressGC -} - -void halfSuppressedFunction() { - GC(); // Calls GC, but is sometimes called within AutoSuppressGC -} - -void unsuppressedFunction() { - GC(); // Calls GC, never within AutoSuppressGC -} - -void f() { - Cell* cell1 = nullptr; - Cell* cell2 = nullptr; - Cell* cell3 = nullptr; - { - AutoSuppressGC nogc; - suppressedFunction(); - halfSuppressedFunction(); - } - foo(cell1); - halfSuppressedFunction(); - foo(cell2); - unsuppressedFunction(); - { - // Old bug: it would look from the first AutoSuppressGC constructor it - // found to the last destructor. This statement *should* have no effect. - AutoSuppressGC nogc; - } - foo(cell3); -} diff --git a/js/src/devtools/rootAnalysis/t/suppression/test.py b/js/src/devtools/rootAnalysis/t/suppression/test.py deleted file mode 100644 index 65974cc3369a..000000000000 --- a/js/src/devtools/rootAnalysis/t/suppression/test.py +++ /dev/null @@ -1,23 +0,0 @@ -test.compile("source.cpp") -test.run_analysis_script('gcTypes', upto='gcFunctions') - -# The suppressions file uses only mangled names since it's for internal use, -# though I may change that soon given (1) the unfortunate non-uniqueness of -# mangled constructor names, and (2) the usefulness of this file for -# mrgiggles's reporting. -suppressed = test.load_suppressed_functions() - -# Only one of these is fully suppressed (ie, *always* called within the scope -# of an AutoSuppressGC). -assert(len(filter(lambda f: 'suppressedFunction' in f, suppressed)) == 1) -assert(len(filter(lambda f: 'halfSuppressedFunction' in f, suppressed)) == 0) -assert(len(filter(lambda f: 'unsuppressedFunction' in f, suppressed)) == 0) - -# gcFunctions should be the inverse, but we get to rely on unmangled names here. -gcFunctions = test.load_gcFunctions() -print(gcFunctions) -assert('void GC()' in gcFunctions) -assert('void suppressedFunction()' not in gcFunctions) -assert('void halfSuppressedFunction()' in gcFunctions) -assert('void unsuppressedFunction()' in gcFunctions) -assert('void f()' in gcFunctions) diff --git a/js/src/devtools/rootAnalysis/t/testlib.py b/js/src/devtools/rootAnalysis/t/testlib.py deleted file mode 100644 index 438398f1ed66..000000000000 --- a/js/src/devtools/rootAnalysis/t/testlib.py +++ /dev/null @@ -1,120 +0,0 @@ -import json -import os -import re -import subprocess - -from sixgill import Body -from collections import defaultdict, namedtuple - -scriptdir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) - -HazardSummary = namedtuple('HazardSummary', ['function', 'variable', 'type', 'GCFunction', 'location']) - - -def equal(got, expected): - if got != expected: - print("Got '%s', expected '%s'" % (got, expected)) - -def extract_unmangled(func): - return func.split('$')[-1] - - -class Test(object): - def __init__(self, indir, outdir, cfg): - self.indir = indir - self.outdir = outdir - self.cfg = cfg - - def infile(self, path): - return os.path.join(self.indir, path) - - def binpath(self, prog): - return os.path.join(self.cfg.sixgill_bin, prog) - - def compile(self, source, options = ''): - cmd = "{CXX} -c {source} -O3 -std=c++11 -fplugin={sixgill} -fplugin-arg-xgill-mangle=1 {options}".format( - source=self.infile(source), - CXX=self.cfg.cxx, sixgill=self.cfg.sixgill_plugin, - options=options) - if self.cfg.verbose: - print("Running %s" % cmd) - subprocess.check_call(["sh", "-c", cmd]) - - def load_db_entry(self, dbname, pattern): - '''Look up an entry from an XDB database file, 'pattern' may be an exact - matching string, or an re pattern object matching a single entry.''' - - if not isinstance(pattern, basestring): - output = subprocess.check_output([self.binpath("xdbkeys"), dbname + ".xdb"]) - matches = filter(lambda _: re.search(pattern, _), output.splitlines()) - if len(matches) == 0: - raise Exception("entry not found") - if len(matches) > 1: - raise Exception("multiple entries found") - pattern = matches[0] - - output = subprocess.check_output([self.binpath("xdbfind"), "-json", dbname + ".xdb", pattern]) - return json.loads(output) - - def run_analysis_script(self, phase, upto=None): - file("defaults.py", "w").write('''\ -analysis_scriptdir = '{scriptdir}' -sixgill_bin = '{bindir}' -'''.format(scriptdir=scriptdir, bindir=self.cfg.sixgill_bin)) - cmd = [os.path.join(scriptdir, "analyze.py"), phase] - if upto: - cmd += ["--upto", upto] - cmd.append("--source=%s" % self.indir) - cmd.append("--objdir=%s" % self.outdir) - cmd.append("--js=%s" % self.cfg.js) - if self.cfg.verbose: - cmd.append("--verbose") - print("Running " + " ".join(cmd)) - subprocess.check_call(cmd) - - def computeGCTypes(self): - self.run_analysis_script("gcTypes", upto="gcTypes") - - def computeHazards(self): - self.run_analysis_script("gcTypes") - - def load_text_file(self, filename, extract=lambda l: l): - fullpath = os.path.join(self.outdir, filename) - values = (extract(line.strip()) for line in file(fullpath)) - return filter(lambda _: _ is not None, values) - - def load_suppressed_functions(self): - return set(self.load_text_file("suppressedFunctions.lst")) - - def load_gcTypes(self): - def grab_type(line): - m = re.match(r'^(GC\w+): (.*)', line) - if m: - return (m.group(1) + 's', m.group(2)) - return None - - gctypes = defaultdict(list) - for collection, typename in self.load_text_file('gcTypes.txt', extract=grab_type): - gctypes[collection].append(typename) - return gctypes - - def load_gcFunctions(self): - return self.load_text_file('gcFunctions.lst', extract=extract_unmangled) - - def load_hazards(self): - def grab_hazard(line): - m = re.match(r"Function '(.*?)' has unrooted '(.*?)' of type '(.*?)' live across GC call '(.*?)' at (.*)", line) - if m: - info = list(m.groups()) - info[0] = info[0].split("$")[-1] - info[3] = info[3].split("$")[-1] - return HazardSummary(*info) - return None - - return self.load_text_file('rootingHazards.txt', extract=grab_hazard) - - def process_body(self, body): - return Body(body) - - def process_bodies(self, bodies): - return [self.process_body(b) for b in bodies] diff --git a/js/src/devtools/rootAnalysis/t/sixgill-tree/source.cpp b/js/src/devtools/rootAnalysis/test/source.cpp similarity index 100% rename from js/src/devtools/rootAnalysis/t/sixgill-tree/source.cpp rename to js/src/devtools/rootAnalysis/test/source.cpp diff --git a/js/src/devtools/rootAnalysis/t/sixgill-tree/test.py b/js/src/devtools/rootAnalysis/test/test.py similarity index 82% rename from js/src/devtools/rootAnalysis/t/sixgill-tree/test.py rename to js/src/devtools/rootAnalysis/test/test.py index c0c0263cd024..59281570999d 100644 --- a/js/src/devtools/rootAnalysis/t/sixgill-tree/test.py +++ b/js/src/devtools/rootAnalysis/test/test.py @@ -1,8 +1,6 @@ -import re - -test.compile("source.cpp") -test.computeGCTypes() -body = test.process_body(test.load_db_entry("src_body", re.compile(r'root_arg'))[0]) +compile("source.cpp") +computeGCTypes() +body = process_body(load_db_entry("src_body", re.compile(r'root_arg'))[0]) # Rendering positive and negative integers marker1 = body.assignment_line('MARKER1') @@ -18,7 +16,7 @@ assert('other1' in body['Variables']) assert('other2' in body['Variables']) # Test function annotations -js_GC = test.process_body(test.load_db_entry("src_body", re.compile(r'js_GC'))[0]) +js_GC = process_body(load_db_entry("src_body", re.compile(r'js_GC'))[0]) annotations = js_GC['Variables']['void js_GC()']['Annotation'] assert(annotations) found_call_tag = False @@ -31,7 +29,7 @@ assert(found_call_tag) # Test type annotations # js::gc::Cell first -cell = test.load_db_entry("src_comp", 'js::gc::Cell')[0] +cell = load_db_entry("src_comp", 'js::gc::Cell')[0] assert(cell['Kind'] == 'Struct') annotations = cell['Annotation'] assert(len(annotations) == 1) @@ -40,14 +38,14 @@ assert(tag == 'Tag') assert(value == 'GC Thing') # Check JSObject inheritance. -JSObject = test.load_db_entry("src_comp", 'JSObject')[0] +JSObject = load_db_entry("src_comp", 'JSObject')[0] bases = [ b['Base'] for b in JSObject['CSUBaseClass'] ] assert('js::gc::Cell' in bases) assert('Bogon' in bases) assert(len(bases) == 2) # Check type analysis -gctypes = test.load_gcTypes() +gctypes = loadGCTypes() assert('js::gc::Cell' in gctypes['GCThings']) assert('JustACell' in gctypes['GCThings']) assert('JSObject' in gctypes['GCThings']) diff --git a/js/src/devtools/rootAnalysis/utility.js b/js/src/devtools/rootAnalysis/utility.js index 06c18804f362..7303c7b81fd9 100644 --- a/js/src/devtools/rootAnalysis/utility.js +++ b/js/src/devtools/rootAnalysis/utility.js @@ -6,15 +6,6 @@ // constructors/destructors. var internalMarker = " *INTERNAL* "; -if (! Set.prototype.hasOwnProperty("update")) { - Object.defineProperty(Set.prototype, "update", { - value: function (collection) { - for (let elt of collection) - this.add(elt); - } - }); -} - function assert(x, msg) { if (x) @@ -191,21 +182,3 @@ function* readFileLines_gen(filename) libc.fclose(fp); libc.free(ctypes.void_t.ptr(linebuf)); } - -function addToKeyedList(collection, key, entry) -{ - if (!(key in collection)) - collection[key] = []; - collection[key].push(entry); -} - -function loadTypeInfo(filename) -{ - var info = {}; - for (var line of readFileLines_gen(filename)) { - line = line.replace(/\n/, ""); - let [property, name] = line.split("$$"); - addToKeyedList(info, property, name); - } - return info; -} diff --git a/js/src/gc/Iteration.cpp b/js/src/gc/Iteration.cpp index 6c76f326954a..daa8c741a8ef 100644 --- a/js/src/gc/Iteration.cpp +++ b/js/src/gc/Iteration.cpp @@ -95,19 +95,21 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment, void* data, IterateScriptCallback scriptCallback) { MOZ_ASSERT(!rt->mainThread.suppressGC); - AutoEmptyNursery empty(rt); + rt->gc.evictNursery(); + MOZ_ASSERT(rt->gc.nursery.isEmpty()); + AutoPrepareForTracing prep(rt, SkipAtoms); if (compartment) { - Zone* zone = compartment->zone(); - for (auto script = zone->cellIter(empty); !script.done(); script.next()) { + for (ZoneCellIterUnderGC i(compartment->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); if (script->compartment() == compartment) scriptCallback(rt, data, script); } } else { for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { - for (auto script = zone->cellIter(empty); !script.done(); script.next()) - scriptCallback(rt, data, script); + for (ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) + scriptCallback(rt, data, i.get()); } } } @@ -115,14 +117,14 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment, void js::IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data) { - JSRuntime* rt = zone->runtimeFromMainThread(); - AutoEmptyNursery empty(rt); - AutoPrepareForTracing prep(rt, SkipAtoms); + zone->runtimeFromMainThread()->gc.evictNursery(); + AutoPrepareForTracing prep(zone->runtimeFromMainThread(), SkipAtoms); for (auto thingKind : ObjectAllocKinds()) { - for (auto obj = zone->cellIter(thingKind, empty); !obj.done(); obj.next()) { + for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) { + JSObject* obj = i.get(); if (obj->asTenured().isMarked(GRAY)) - cellCallback(data, JS::GCCellPtr(obj.get())); + cellCallback(data, JS::GCCellPtr(obj)); } } } diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index b5cc36eabef7..7dedbb8a8894 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -761,7 +761,7 @@ Statistics::Statistics(JSRuntime* rt) maxPauseInInterval(0), phaseNestingDepth(0), activeDagSlot(PHASE_DAG_NONE), - suspended(0), + suspendedPhaseNestingDepth(0), sliceCallback(nullptr), nurseryCollectionCallback(nullptr), aborted(false) @@ -1069,7 +1069,7 @@ Statistics::startTimingMutator() return false; } - MOZ_ASSERT(suspended == 0); + MOZ_ASSERT(suspendedPhaseNestingDepth == 0); timedGCTime = 0; phaseStartTimes[PHASE_MUTATOR] = 0; @@ -1094,37 +1094,6 @@ Statistics::stopTimingMutator(double& mutator_ms, double& gc_ms) return true; } -void -Statistics::suspendPhases(Phase suspension) -{ - MOZ_ASSERT(suspension == PHASE_EXPLICIT_SUSPENSION || suspension == PHASE_IMPLICIT_SUSPENSION); - while (phaseNestingDepth) { - MOZ_ASSERT(suspended < mozilla::ArrayLength(suspendedPhases)); - Phase parent = phaseNesting[phaseNestingDepth - 1]; - suspendedPhases[suspended++] = parent; - recordPhaseEnd(parent); - } - suspendedPhases[suspended++] = suspension; -} - -void -Statistics::resumePhases() -{ -#ifdef DEBUG - Phase popped = suspendedPhases[--suspended]; - MOZ_ASSERT(popped == PHASE_EXPLICIT_SUSPENSION || popped == PHASE_IMPLICIT_SUSPENSION); -#endif - while (suspended && - suspendedPhases[suspended - 1] != PHASE_EXPLICIT_SUSPENSION && - suspendedPhases[suspended - 1] != PHASE_IMPLICIT_SUSPENSION) - { - Phase resumePhase = suspendedPhases[--suspended]; - if (resumePhase == PHASE_MUTATOR) - timedGCTime += PRMJ_Now() - timedGCStart; - beginPhase(resumePhase); - } -} - void Statistics::beginPhase(Phase phase) { @@ -1136,7 +1105,9 @@ Statistics::beginPhase(Phase phase) // // Reuse this mechanism for managing PHASE_MUTATOR. if (parent == PHASE_GC_BEGIN || parent == PHASE_GC_END || parent == PHASE_MUTATOR) { - suspendPhases(PHASE_IMPLICIT_SUSPENSION); + MOZ_ASSERT(suspendedPhaseNestingDepth < mozilla::ArrayLength(suspendedPhases)); + suspendedPhases[suspendedPhaseNestingDepth++] = parent; + recordPhaseEnd(parent); parent = phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : PHASE_NO_PARENT; } @@ -1183,8 +1154,12 @@ Statistics::endPhase(Phase phase) // When emptying the stack, we may need to resume a callback phase // (PHASE_GC_BEGIN/END) or return to timing the mutator (PHASE_MUTATOR). - if (phaseNestingDepth == 0 && suspended > 0 && suspendedPhases[suspended - 1] == PHASE_IMPLICIT_SUSPENSION) - resumePhases(); + if (phaseNestingDepth == 0 && suspendedPhaseNestingDepth > 0) { + Phase resumePhase = suspendedPhases[--suspendedPhaseNestingDepth]; + if (resumePhase == PHASE_MUTATOR) + timedGCTime += PRMJ_Now() - timedGCStart; + beginPhase(resumePhase); + } } void diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 4de504e226e7..5fa4748a94c6 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -86,8 +86,6 @@ enum Phase : uint8_t { PHASE_LIMIT, PHASE_NONE = PHASE_LIMIT, - PHASE_EXPLICIT_SUSPENSION = PHASE_LIMIT, - PHASE_IMPLICIT_SUSPENSION, PHASE_MULTI_PARENTS }; @@ -171,22 +169,6 @@ struct Statistics void endPhase(Phase phase); void endParallelPhase(Phase phase, const GCParallelTask* task); - // Occasionally, we may be in the middle of something that is tracked by - // this class, and we need to do something unusual (eg evict the nursery) - // that doesn't normally nest within the current phase. Suspend the - // currently tracked phase stack, at which time the caller is free to do - // other tracked operations. - // - // This also happens internally with PHASE_GC_BEGIN and other "non-GC" - // phases. While in these phases, any beginPhase will automatically suspend - // the non-GC phase, until that inner stack is complete, at which time it - // will automatically resume the non-GC phase. Explicit suspensions do not - // get auto-resumed. - void suspendPhases(Phase suspension = PHASE_EXPLICIT_SUSPENSION); - - // Resume a suspended stack of phases. - void resumePhases(); - void beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind, SliceBudget budget, JS::gcreason::Reason reason); void endSlice(); @@ -336,14 +318,13 @@ struct Statistics size_t activeDagSlot; /* - * Certain phases can interrupt the phase stack, eg callback phases. When - * this happens, we move the suspended phases over to a sepearate list, - * terminated by a dummy PHASE_SUSPENSION phase (so that we can nest - * suspensions by suspending multiple stacks with a PHASE_SUSPENSION in - * between). + * To avoid recursive nesting, we discontinue a callback phase when any + * other phases are started. Remember what phase to resume when the inner + * phases are complete. (And because GCs can nest within the callbacks any + * number of times, we need a whole stack of of phases to resume.) */ - Phase suspendedPhases[MAX_NESTING * 3]; - size_t suspended; + Phase suspendedPhases[MAX_NESTING]; + size_t suspendedPhaseNestingDepth; /* Sweep times for SCCs of compartments. */ Vector sccTimes; diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index 19d80af1551d..1f4c6dee3b37 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -153,13 +153,13 @@ Zone::sweepBreakpoints(FreeOp* fop) */ MOZ_ASSERT(isGCSweepingOrCompacting()); - for (auto iter = cellIter(); !iter.done(); iter.next()) { - JSScript* script = iter; + for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); if (!script->hasAnyBreakpointsOrStepMode()) continue; bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script); - MOZ_ASSERT(script == iter); + MOZ_ASSERT(script == i.get()); for (unsigned i = 0; i < script->length(); i++) { BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i)); if (!site) @@ -207,8 +207,10 @@ Zone::discardJitCode(FreeOp* fop) #ifdef DEBUG /* Assert no baseline scripts are marked as active. */ - for (auto script = cellIter(); !script.done(); script.next()) + for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active()); + } #endif /* Mark baseline scripts on the stack as active. */ @@ -217,7 +219,8 @@ Zone::discardJitCode(FreeOp* fop) /* Only mark OSI points if code is being discarded. */ jit::InvalidateAll(fop, this); - for (auto script = cellIter(); !script.done(); script.next()) { + for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); jit::FinishInvalidation(fop, script); /* diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 17e53425be57..1d019e4a426a 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -84,9 +84,6 @@ using UniqueIdMap = GCHashMap -class ZoneCellIter; - } // namespace gc } // namespace js @@ -160,13 +157,6 @@ struct Zone : public JS::shadow::Zone, onTooMuchMalloc(); } - // Iterate over all cells in the zone. See the definition of ZoneCellIter - // in jsgcinlines.h for the possible arguments and documentation. - template - js::gc::ZoneCellIter cellIter(Args... args) { - return js::gc::ZoneCellIter(const_cast(this), mozilla::Forward(args)...); - } - bool isTooMuchMalloc() const { return gcMallocBytes <= 0; } void onTooMuchMalloc(); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 06529002441e..d649a5fb9ec7 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -488,7 +488,6 @@ BaselineScript::Trace(JSTracer* trc, BaselineScript* script) void BaselineScript::Destroy(FreeOp* fop, BaselineScript* script) { - MOZ_ASSERT(!script->hasPendingIonBuilder()); script->unlinkDependentWasmModules(fop); @@ -1172,7 +1171,8 @@ jit::ToggleBaselineProfiling(JSRuntime* runtime, bool enable) return; for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { - for (auto script = zone->cellIter(); !script.done(); script.next()) { + for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); if (!script->hasBaselineScript()) continue; AutoWritableJitCode awjc(script->baselineScript()->method()); @@ -1186,7 +1186,8 @@ void jit::ToggleBaselineTraceLoggerScripts(JSRuntime* runtime, bool enable) { for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { - for (auto script = zone->cellIter(); !script.done(); script.next()) { + for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); if (!script->hasBaselineScript()) continue; script->baselineScript()->toggleTraceLoggerScripts(runtime, script, enable); @@ -1198,7 +1199,8 @@ void jit::ToggleBaselineTraceLoggerEngine(JSRuntime* runtime, bool enable) { for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { - for (auto script = zone->cellIter(); !script.done(); script.next()) { + for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); if (!script->hasBaselineScript()) continue; script->baselineScript()->toggleTraceLoggerEngine(enable); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 78d616f5e1db..ff012116a5f2 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -11,7 +11,6 @@ #include "mozilla/ThreadLocal.h" #include "jscompartment.h" -#include "jsgc.h" #include "jsprf.h" #include "gc/Marking.h" @@ -590,8 +589,8 @@ JitRuntime::Mark(JSTracer* trc, AutoLockForExclusiveAccess& lock) { MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting()); Zone* zone = trc->runtime()->atomsCompartment(lock)->zone(); - for (auto i = zone->cellIter(); !i.done(); i.next()) { - JitCode* code = i; + for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::JITCODE); !i.done(); i.next()) { + JitCode* code = i.get(); TraceRoot(trc, &code, "wrapper"); } } @@ -1352,7 +1351,8 @@ jit::ToggleBarriers(JS::Zone* zone, bool needs) if (!rt->hasJitRuntime()) return; - for (auto script = zone->cellIter(); !script.done(); script.next()) { + for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); if (script->hasIonScript()) script->ionScript()->toggleBarriers(needs); if (script->hasBaselineScript()) diff --git a/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp b/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp index abf5bf00198f..c3bcb1d642c4 100644 --- a/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp +++ b/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp @@ -16,7 +16,7 @@ struct AutoIgnoreRootingHazards { static volatile int depth; AutoIgnoreRootingHazards() { depth++; } ~AutoIgnoreRootingHazards() { depth--; } -} JS_HAZ_GC_SUPPRESSED; +}; volatile int AutoIgnoreRootingHazards::depth = 0; BEGIN_TEST(testGCStoreBufferRemoval) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 537cfc4eb06c..be7d6213e26c 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -984,8 +984,8 @@ AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, A // want to delazify in that case: this pointer is weak so the JSScript // could be destroyed at the next GC. - for (auto i = cx->zone()->cellIter(kind); !i.done(); i.next()) { - JSFunction* fun = &i->as(); + for (gc::ZoneCellIter i(cx->zone(), kind); !i.done(); i.next()) { + JSFunction* fun = &i.get()->as(); // Sweeping is incremental; take care to not delazify functions that // are about to be finalized. GC things referenced by objects that are @@ -1157,7 +1157,8 @@ JSCompartment::clearScriptCounts() void JSCompartment::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler) { - for (auto script = zone()->cellIter(); !script.done(); script.next()) { + for (gc::ZoneCellIter i(zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode()) script->clearBreakpointsIn(fop, dbg, handler); } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index c37269f1b4a4..d884798c079d 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2381,10 +2381,15 @@ GCRuntime::sweepTypesAfterCompacting(Zone* zone) AutoClearTypeInferenceStateOnOOM oom(zone); - for (auto script = zone->cellIter(); !script.done(); script.next()) + for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); script->maybeSweepTypes(&oom); - for (auto group = zone->cellIter(); !group.done(); group.next()) + } + + for (ZoneCellIterUnderGC i(zone, AllocKind::OBJECT_GROUP); !i.done(); i.next()) { + ObjectGroup* group = i.get(); group->maybeSweep(&oom); + } zone->types.endSweep(rt); } @@ -3976,11 +3981,10 @@ GCRuntime::checkForCompartmentMismatches() return; CompartmentCheckTracer trc(rt); - AutoAssertEmptyNursery empty(rt); for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { trc.zone = zone; for (auto thingKind : AllAllocKinds()) { - for (auto i = zone->cellIter(thingKind, empty); !i.done(); i.next()) { + for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) { trc.src = i.getCell(); trc.srcKind = MapAllocToTraceKind(thingKind); trc.compartment = DispatchTraceKindTyped(MaybeCompartmentFunctor(), @@ -3999,10 +4003,9 @@ RelazifyFunctions(Zone* zone, AllocKind kind) kind == AllocKind::FUNCTION_EXTENDED); JSRuntime* rt = zone->runtimeFromMainThread(); - AutoAssertEmptyNursery empty(rt); - for (auto i = zone->cellIter(kind, empty); !i.done(); i.next()) { - JSFunction* fun = &i->as(); + for (ZoneCellIterUnderGC i(zone, kind); !i.done(); i.next()) { + JSFunction* fun = &i.get()->as(); if (fun->hasScript()) fun->maybeRelazify(rt); } @@ -6916,7 +6919,8 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) RootedObject targetStaticGlobalLexicalScope(rt); targetStaticGlobalLexicalScope = &target->maybeGlobal()->lexicalScope().staticBlock(); - for (auto script = source->zone()->cellIter(); !script.done(); script.next()) { + for (ZoneCellIter iter(source->zone(), AllocKind::SCRIPT); !iter.done(); iter.next()) { + JSScript* script = iter.get(); MOZ_ASSERT(script->compartment() == source); script->compartment_ = target; script->setTypesGeneration(target->zone()->types.generation); @@ -6948,12 +6952,14 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) } } - for (auto base = source->zone()->cellIter(); !base.done(); base.next()) { + for (ZoneCellIter iter(source->zone(), AllocKind::BASE_SHAPE); !iter.done(); iter.next()) { + BaseShape* base = iter.get(); MOZ_ASSERT(base->compartment() == source); base->compartment_ = target; } - for (auto group = source->zone()->cellIter(); !group.done(); group.next()) { + for (ZoneCellIter iter(source->zone(), AllocKind::OBJECT_GROUP); !iter.done(); iter.next()) { + ObjectGroup* group = iter.get(); group->setGeneration(target->zone()->types.generation); group->compartment_ = target; @@ -6975,7 +6981,8 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) // After fixing JSFunctions' compartments, we can fix LazyScripts' // enclosing scopes. - for (auto lazy = source->zone()->cellIter(); !lazy.done(); lazy.next()) { + for (ZoneCellIter iter(source->zone(), AllocKind::LAZY_SCRIPT); !iter.done(); iter.next()) { + LazyScript* lazy = iter.get(); MOZ_ASSERT(lazy->functionNonDelazifying()->compartment() == target); // See warning in handleParseWorkload. If we start optimizing global @@ -7109,9 +7116,12 @@ js::ReleaseAllJITCode(FreeOp* fop) void js::PurgeJITCaches(Zone* zone) { - /* Discard Ion caches. */ - for (auto script = zone->cellIter(); !script.done(); script.next()) + for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); + + /* Discard Ion caches. */ jit::PurgeCaches(script); + } } void @@ -7381,7 +7391,8 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt) for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { zone->checkUniqueIdTableAfterMovingGC(); - for (auto baseShape = zone->cellIter(); !baseShape.done(); baseShape.next()) { + for (ZoneCellIterUnderGC i(zone, AllocKind::BASE_SHAPE); !i.done(); i.next()) { + BaseShape* baseShape = i.get(); if (baseShape->hasTable()) baseShape->table().checkAfterMovingGC(); } @@ -7859,28 +7870,5 @@ StateName(State state) return names[state]; } -void -AutoAssertHeapBusy::checkCondition(JSRuntime *rt) -{ - this->rt = rt; - MOZ_ASSERT(rt->isHeapBusy()); -} - -void -AutoAssertEmptyNursery::checkCondition(JSRuntime *rt) { - this->rt = rt; - MOZ_ASSERT(rt->gc.nursery.isEmpty()); -} - -AutoEmptyNursery::AutoEmptyNursery(JSRuntime *rt) - : AutoAssertEmptyNursery() -{ - MOZ_ASSERT(!rt->mainThread.suppressGC); - rt->gc.stats.suspendPhases(); - rt->gc.evictNursery(); - rt->gc.stats.resumePhases(); - checkCondition(rt); -} - } /* namespace gc */ } /* namespace js */ diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 9ebc3c4ff249..409acd22dde6 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1269,7 +1269,7 @@ MaybeVerifyBarriers(JSContext* cx, bool always = false) * read the comment in vm/Runtime.h above |suppressGC| and take all appropriate * precautions before instantiating this class. */ -class MOZ_RAII JS_HAZ_GC_SUPPRESSED AutoSuppressGC +class MOZ_RAII AutoSuppressGC { int32_t& suppressGC_; @@ -1328,95 +1328,6 @@ struct MOZ_RAII AutoAssertNoNurseryAlloc #endif }; -/* - * There are a couple of classes here that serve mostly as "tokens" indicating - * that a condition holds. Some functions force the caller to possess such a - * token because they would misbehave if the condition were false, and it is - * far more clear to make the condition visible at the point where it can be - * affected rather than just crashing in an assertion down in the place where - * it is relied upon. - */ - -/* - * Token meaning that the heap is busy and no allocations will be made. - * - * This class may be instantiated directly if it is known that the condition is - * already true, or it can be used as a base class for another RAII class that - * causes the condition to become true. Such base classes will use the no-arg - * constructor, establish the condition, then call checkCondition() to assert - * it and possibly record data needed to re-check the condition during - * destruction. - * - * Ordinarily, you would do something like this with a Maybe<> member that is - * emplaced during the constructor, but token-requiring functions want to - * require a reference to a base class instance. That said, you can always pass - * in the Maybe<> field as the token. - */ -class MOZ_RAII AutoAssertHeapBusy { - protected: - JSRuntime* rt; - - // Check that the heap really is busy, and record the rt for the check in - // the destructor. - void checkCondition(JSRuntime *rt); - - AutoAssertHeapBusy() : rt(nullptr) { - } - - public: - explicit AutoAssertHeapBusy(JSRuntime* rt) { - checkCondition(rt); - } - - ~AutoAssertHeapBusy() { - MOZ_ASSERT(rt); // checkCondition must always be called. - checkCondition(rt); - } -}; - -/* - * A class that serves as a token that the nursery is empty. It descends from - * AutoAssertHeapBusy, which means that it additionally requires the heap to be - * busy (which is not necessarily linked, but turns out to be true in practice - * for all users and simplifies the usage of these classes.) - */ -class MOZ_RAII AutoAssertEmptyNursery -{ - protected: - JSRuntime* rt; - - // Check that the nursery is empty. - void checkCondition(JSRuntime *rt); - - // For subclasses that need to empty the nursery in their constructors. - AutoAssertEmptyNursery() : rt(nullptr) { - } - - public: - explicit AutoAssertEmptyNursery(JSRuntime* rt) { - checkCondition(rt); - } - - ~AutoAssertEmptyNursery() { - checkCondition(rt); - } -}; - -/* - * Evict the nursery upon construction. Serves as a token indicating that the - * nursery is empty. (See AutoAssertEmptyNursery, above.) - * - * Note that this is very improper subclass of AutoAssertHeapBusy, in that the - * heap is *not* busy within the scope of an AutoEmptyNursery. I will most - * likely fix this by removing AutoAssertHeapBusy, but that is currently - * waiting on jonco's review. - */ -class MOZ_RAII AutoEmptyNursery : public AutoAssertEmptyNursery -{ - public: - explicit AutoEmptyNursery(JSRuntime *rt); -}; - const char* StateName(State state); diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index 6b710a85f5bf..e9687ea71cc9 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -195,71 +195,39 @@ class ArenaCellIterUnderFinalize : public ArenaCellIterImpl explicit ArenaCellIterUnderFinalize(Arena* arena) : ArenaCellIterImpl(arena) {} }; -template -class ZoneCellIter; - -template <> -class ZoneCellIter { +class ZoneCellIterImpl +{ ArenaIter arenaIter; ArenaCellIterImpl cellIter; - JS::AutoAssertNoAlloc noAlloc; - protected: - // For use when a subclass wants to insert some setup before init(). - ZoneCellIter() {} - - void init(JS::Zone* zone, AllocKind kind) { + public: + ZoneCellIterImpl(JS::Zone* zone, AllocKind kind) { JSRuntime* rt = zone->runtimeFromAnyThread(); MOZ_ASSERT(zone); MOZ_ASSERT_IF(IsNurseryAllocable(kind), rt->gc.nursery.isEmpty()); - // If called from outside a GC, ensure that the heap is in a state - // that allows us to iterate. - if (!rt->isHeapBusy()) { - // Assert that no GCs can occur while a ZoneCellIter is live. - noAlloc.disallowAlloc(rt); - } - // We have a single-threaded runtime, so there's no need to protect // against other threads iterating or allocating. However, we do have // background finalization; we may have to wait for this to finish if // it's currently active. if (IsBackgroundFinalized(kind) && zone->arenas.needBackgroundFinalizeWait(kind)) rt->gc.waitBackgroundSweepEnd(); + arenaIter.init(zone, kind); if (!arenaIter.done()) cellIter.init(arenaIter.get()); } - public: - ZoneCellIter(JS::Zone* zone, AllocKind kind) { - // If we are iterating a nursery-allocated kind then we need to - // evict first so that we can see all things. - if (IsNurseryAllocable(kind)) { - JSRuntime* rt = zone->runtimeFromMainThread(); - rt->gc.evictNursery(); - } - - init(zone, kind); - } - - ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery&) { - // No need to evict the nursery. (This constructor is known statically - // to not GC.) - init(zone, kind); - } - bool done() const { return arenaIter.done(); } - template - T* get() const { + template T* get() const { MOZ_ASSERT(!done()); return cellIter.get(); } - TenuredCell* getCell() const { + Cell* getCell() const { MOZ_ASSERT(!done()); return cellIter.getCell(); } @@ -276,74 +244,44 @@ class ZoneCellIter { } }; -// Iterator over the cells in a Zone, where the GC type (JSString, JSObject) is -// known, for a single AllocKind. Example usages: -// -// for (auto obj = zone->cellIter(AllocKind::OBJECT0); !obj.done(); obj.next()) -// ... -// -// for (auto script = zone->cellIter(); !script.done(); script.next()) -// f(script->code()); -// -// As this code demonstrates, you can use 'script' as if it were a JSScript*. -// Its actual type is ZoneCellIter, but for most purposes it will -// autoconvert to JSScript*. -// -// Note that in the JSScript case, ZoneCellIter is able to infer the AllocKind -// from the type 'JSScript', whereas in the JSObject case, the kind must be -// given (because there are multiple AllocKinds for objects). -// -// Also, the static rooting hazard analysis knows that the JSScript case will -// not GC during construction. The JSObject case needs to GC, or more precisely -// to empty the nursery and clear out the store buffer, so that it can see all -// objects to iterate over (the nursery is not iterable) and remove the -// possibility of having pointers from the store buffer to data hanging off -// stuff we're iterating over that we are going to delete. (The latter should -// not be a problem, since such instances should be using RelocatablePtr do -// remove themselves from the store buffer on deletion, but currently for -// subtle reasons that isn't good enough.) -// -// If the iterator is used within a GC, then there is no need to evict the -// nursery (again). You may select a variant that will skip the eviction either -// by specializing on a GCType that is never allocated in the nursery, or -// explicitly by passing in a trailing AutoAssertEmptyNursery argument. -// -template -class ZoneCellIter : public ZoneCellIter { +class ZoneCellIterUnderGC : public ZoneCellIterImpl +{ public: - // Non-nursery allocated (equivalent to having an entry in - // MapTypeToFinalizeKind). The template declaration here is to discard this - // constructor overload if MapTypeToFinalizeKind::kind does not - // exist. Note that there will be no remaining overloads that will work, - // which makes sense given that you haven't specified which of the - // AllocKinds to use for GCType. - // - // If we later add a nursery allocable GCType with a single AllocKind, we - // will want to add an overload of this constructor that does the right - // thing (ie, it empties the nursery before iterating.) - explicit ZoneCellIter(JS::Zone* zone) : ZoneCellIter() { - init(zone, MapTypeToFinalizeKind::kind); - } - - // Non-nursery allocated, nursery is known to be empty: same behavior as above. - ZoneCellIter(JS::Zone* zone, const js::gc::AutoAssertEmptyNursery&) : ZoneCellIter(zone) { - } - - // Arbitrary kind, which will be assumed to be nursery allocable (and - // therefore the nursery will be emptied before iterating.) - ZoneCellIter(JS::Zone* zone, AllocKind kind) : ZoneCellIter(zone, kind) { - } - - // Arbitrary kind, which will be assumed to be nursery allocable, but the - // nursery is known to be empty already: same behavior as non-nursery types. - ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery& empty) - : ZoneCellIter(zone, kind, empty) + ZoneCellIterUnderGC(JS::Zone* zone, AllocKind kind) + : ZoneCellIterImpl(zone, kind) { + MOZ_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy()); + } +}; + +class ZoneCellIter +{ + mozilla::Maybe impl; + JS::AutoAssertNoAlloc noAlloc; + + public: + ZoneCellIter(JS::Zone* zone, AllocKind kind) { + // If called from outside a GC, ensure that the heap is in a state + // that allows us to iterate. + JSRuntime* rt = zone->runtimeFromMainThread(); + if (!rt->isHeapBusy()) { + // If we are iterating a nursery-allocated kind then we need to + // evict first so that we can see all things. + if (IsNurseryAllocable(kind)) + rt->gc.evictNursery(); + + // Assert that no GCs can occur while a ZoneCellIter is live. + noAlloc.disallowAlloc(rt); + } + + impl.emplace(zone, kind); } - GCType* get() const { return ZoneCellIter::get(); } - operator GCType*() const { return get(); } - GCType* operator ->() const { return get(); } + bool done() const { return impl->done(); } + template + T* get() const { return impl->get(); } + Cell* getCell() const { return impl->getCell(); } + void next() { impl->next(); } }; class GCZonesIter diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index aee66c49f59a..4514a3b73ca4 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -201,9 +201,8 @@ js::DumpPCCounts(JSContext* cx, HandleScript script, Sprinter* sp) void js::DumpCompartmentPCCounts(JSContext* cx) { - RootedScript script(cx); - for (auto iter = cx->zone()->cellIter(); !iter.done(); iter.next()) { - script = iter; + for (ZoneCellIter i(cx->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) { + RootedScript script(cx, i.get()); if (script->compartment() != cx->compartment()) continue; @@ -1658,7 +1657,8 @@ js::StopPCCountProfiling(JSContext* cx) return; for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { - for (auto script = zone->cellIter(); !script.done(); script.next()) { + for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); if (script->hasScriptCounts() && script->types()) { if (!vec->append(script)) return; @@ -2025,7 +2025,8 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out) } Rooted topScripts(cx, ScriptVector(cx)); for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { - for (auto script = zone->cellIter(); !script.done(); script.next()) { + for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { + JSScript* script = i.get(); if (script->compartment() != comp || !script->isTopLevel() || !script->filename()) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 642e4e363bfd..a97e9ef89a89 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -2352,8 +2352,8 @@ UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone, return false; } } else { - for (auto iter = zone->cellIter(); !iter.done(); iter.next()) { - JSScript* script = iter; + for (gc::ZoneCellIter iter(zone, gc::AllocKind::SCRIPT); !iter.done(); iter.next()) { + JSScript* script = iter.get(); if (obs.shouldRecompileOrInvalidate(script) && !gc::IsAboutToBeFinalizedUnbarriered(&script)) { diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index c82c2ba82246..5a02869a35dd 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -1271,6 +1271,8 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par LeaveParseTaskZone(rt, parseTask); { + gc::ZoneCellIter iter(parseTask->cx->zone(), gc::AllocKind::OBJECT_GROUP); + // Generator functions don't have Function.prototype as prototype but a // different function object, so the IdentifyStandardPrototype trick // below won't work. Just special-case it. @@ -1286,7 +1288,8 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par // to the corresponding prototype in the new compartment. This will briefly // create cross compartment pointers, which will be fixed by the // MergeCompartments call below. - for (auto group = parseTask->cx->zone()->cellIter(); !group.done(); group.next()) { + for (; !iter.done(); iter.next()) { + ObjectGroup* group = iter.get(); TaggedProto proto(group->proto()); if (!proto.isObject()) continue; diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 5d33a0224a8d..ffa472c6f538 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1049,7 +1049,7 @@ CallAddPropertyHookDense(ExclusiveContext* cx, HandleNativeObject obj, uint32_t } static bool -UpdateShapeTypeAndValue(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape, const Value& value) +UpdateShapeTypeAndValue(ExclusiveContext* cx, NativeObject* obj, Shape* shape, const Value& value) { jsid id = shape->propid(); if (shape->hasSlot()) { diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index c78e363fc74c..fe6780b03f9f 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -2545,15 +2545,16 @@ js::PrintTypes(JSContext* cx, JSCompartment* comp, bool force) if (!force && !InferSpewActive(ISpewResult)) return; - RootedScript script(cx); - for (auto iter = zone->cellIter(); !iter.done(); iter.next()) { - script = iter; + for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { + RootedScript script(cx, i.get()); if (script->types()) script->types()->printTypes(cx, script); } - for (auto group = zone->cellIter(); !group.done(); group.next()) + for (gc::ZoneCellIter i(zone, gc::AllocKind::OBJECT_GROUP); !i.done(); i.next()) { + ObjectGroup* group = i.get(); group->print(); + } #endif } @@ -4424,8 +4425,10 @@ TypeZone::endSweep(JSRuntime* rt) void TypeZone::clearAllNewScriptsOnOOM() { - for (auto iter = zone()->cellIter(); !iter.done(); iter.next()) { - ObjectGroup* group = iter; + for (gc::ZoneCellIter iter(zone(), gc::AllocKind::OBJECT_GROUP); + !iter.done(); iter.next()) + { + ObjectGroup* group = iter.get(); if (!IsAboutToBeFinalizedUnbarriered(&group)) group->maybeClearNewScriptOnOOM(); } @@ -4434,9 +4437,8 @@ TypeZone::clearAllNewScriptsOnOOM() AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM() { if (oom) { - JSRuntime* rt = zone->runtimeFromMainThread(); zone->setPreservingCode(false); - zone->discardJitCode(rt->defaultFreeOp()); + zone->discardJitCode(zone->runtimeFromMainThread()->defaultFreeOp()); zone->types.clearAllNewScriptsOnOOM(); } } From 043b2fdeddb11dc9e091905534df281d7a5dd756 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 31 May 2016 11:31:29 -0700 Subject: [PATCH 100/199] Backed out changeset e5f0088f8ca2 (bug 1276887) for WorkerPrivate assertions CLOSED TREE --- dom/bindings/BindingUtils.cpp | 118 +--------------------------------- dom/bindings/Exceptions.cpp | 2 +- 2 files changed, 4 insertions(+), 116 deletions(-) diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 2ad824fe4f08..ec2ac207c49a 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -26,8 +26,6 @@ #include "nsIPrincipal.h" #include "nsIXPConnect.h" #include "nsUTF8Utils.h" -#include "WorkerPrivate.h" -#include "WorkerRunnable.h" #include "WrapperFactory.h" #include "xpcprivate.h" #include "XrayWrapper.h" @@ -58,8 +56,6 @@ namespace mozilla { namespace dom { -using namespace workers; - const JSErrorFormatString ErrorFormatString[] = { #define MSG_DEF(_name, _argc, _exn, _str) \ { #_name, _str, _argc, _exn }, @@ -3289,101 +3285,6 @@ SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject, } } -namespace { - -// This runnable is used to write a deprecation message from a worker to the -// console running on the main-thread. -class DeprecationWarningRunnable final : public Runnable - , public WorkerFeature -{ - WorkerPrivate* mWorkerPrivate; - nsIDocument::DeprecatedOperations mOperation; - -public: - DeprecationWarningRunnable(WorkerPrivate* aWorkerPrivate, - nsIDocument::DeprecatedOperations aOperation) - : mWorkerPrivate(aWorkerPrivate) - , mOperation(aOperation) - { - MOZ_ASSERT(aWorkerPrivate); - } - - void - Dispatch() - { - if (NS_WARN_IF(!mWorkerPrivate->AddFeature(this))) { - return; - } - - if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) { - mWorkerPrivate->RemoveFeature(this); - return; - } - } - - virtual bool - Notify(workers::Status aStatus) override - { - // We don't care about the notification. We just want to keep the - // mWorkerPrivate alive. - return true; - } - -private: - - NS_IMETHOD - Run() override - { - MOZ_ASSERT(NS_IsMainThread()); - - // Walk up to our containing page - WorkerPrivate* wp = mWorkerPrivate; - while (wp->GetParent()) { - wp = wp->GetParent(); - } - - nsPIDOMWindowInner* window = wp->GetWindow(); - if (window && window->GetExtantDoc()) { - window->GetExtantDoc()->WarnOnceAbout(mOperation); - } - - ReleaseWorker(); - return NS_OK; - } - - void - ReleaseWorker() - { - class ReleaseRunnable final : public WorkerRunnable - { - RefPtr mRunnable; - - public: - ReleaseRunnable(WorkerPrivate* aWorkerPrivate, - DeprecationWarningRunnable* aRunnable) - : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) - , mRunnable(aRunnable) - {} - - virtual bool - WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override - { - MOZ_ASSERT(aWorkerPrivate); - aWorkerPrivate->AssertIsOnWorkerThread(); - - aWorkerPrivate->RemoveFeature(mRunnable); - return true; - } - }; - - RefPtr runnable = - new ReleaseRunnable(mWorkerPrivate, this); - NS_WARN_IF(!runnable->Dispatch()); - } -}; - -} // anonymous namespace - void DeprecationWarning(JSContext* aCx, JSObject* aObject, nsIDocument::DeprecatedOperations aOperation) @@ -3394,23 +3295,10 @@ DeprecationWarning(JSContext* aCx, JSObject* aObject, return; } - if (NS_IsMainThread()) { - nsCOMPtr window = do_QueryInterface(global.GetAsSupports()); - if (window && window->GetExtantDoc()) { - window->GetExtantDoc()->WarnOnceAbout(aOperation); - } - - return; + nsCOMPtr window = do_QueryInterface(global.GetAsSupports()); + if (window && window->GetExtantDoc()) { + window->GetExtantDoc()->WarnOnceAbout(aOperation); } - - WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); - if (!workerPrivate) { - return; - } - - RefPtr runnable = - new DeprecationWarningRunnable(workerPrivate, aOperation); - runnable->Dispatch(); } namespace binding_detail { diff --git a/dom/bindings/Exceptions.cpp b/dom/bindings/Exceptions.cpp index b85a9ee2995b..6b495aa30c25 100644 --- a/dom/bindings/Exceptions.cpp +++ b/dom/bindings/Exceptions.cpp @@ -203,7 +203,7 @@ GetCurrentJSStack(int32_t aMaxDepth) return nullptr; } - return dom::exceptions::CreateStack(cx, aMaxDepth); + return exceptions::CreateStack(cx, aMaxDepth); } AutoForceSetExceptionOnContext::AutoForceSetExceptionOnContext(JSContext* aCx) From 87c08b5281617ed8cc77fc2447cb6f4c388e9f27 Mon Sep 17 00:00:00 2001 From: James Willcox Date: Wed, 4 May 2016 14:15:32 -0500 Subject: [PATCH 101/199] Bug 1270241 - Remove MOZ_USING_ANDROID_JAVA_WIDGETS r=kats --- gfx/thebes/gfxAndroidPlatform.cpp | 10 +++++----- gfx/thebes/moz.build | 5 ----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index 43120d595b04..41eadd1fbec9 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -313,15 +313,15 @@ gfxAndroidPlatform::FontHintingEnabled() // In "mobile" builds, we sometimes use non-reflow-zoom, so we // might not want hinting. Let's see. -#ifdef MOZ_USING_ANDROID_JAVA_WIDGETS - // On android-java, we currently only use gecko to render web +#ifdef MOZ_WIDGET_ANDROID + // On Android, we currently only use gecko to render web // content that can always be be non-reflow-zoomed. So turn off // hinting. // // XXX when gecko-android-java is used as an "app runtime", we may // want to re-enable hinting for non-browser processes there. return false; -#endif // MOZ_USING_ANDROID_JAVA_WIDGETS +#endif // MOZ_WIDGET_ANDROID #ifdef MOZ_WIDGET_GONK // On B2G, the UX preference is currently to keep hinting disabled @@ -339,8 +339,8 @@ gfxAndroidPlatform::FontHintingEnabled() bool gfxAndroidPlatform::RequiresLinearZoom() { -#ifdef MOZ_USING_ANDROID_JAVA_WIDGETS - // On android-java, we currently only use gecko to render web +#ifdef MOZ_WIDGET_ANDROID + // On Android, we currently only use gecko to render web // content that can always be be non-reflow-zoomed. // // XXX when gecko-android-java is used as an "app runtime", we may diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build index cc42e820bf33..fb24a362bec8 100644 --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -289,11 +289,6 @@ CFLAGS += CONFIG['TK_CFLAGS'] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk', 'qt'): CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS'] -if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': - # This is set for "normal Android", that is, when Gecko is running on - # top of the android java runtime. - DEFINES['MOZ_USING_ANDROID_JAVA_WIDGETS'] = True - if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'qt'): CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS'] From fa3557e686bc66ecea57eb8c812736e3766f8bb9 Mon Sep 17 00:00:00 2001 From: James Willcox Date: Tue, 31 May 2016 14:48:38 -0500 Subject: [PATCH 102/199] No bug, disable fading tiles experiment for Fennec in Nightly r=me --- mobile/android/app/mobile.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index eeebb9ebf2a8..f9c5d7b22f79 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -582,11 +582,7 @@ pref("apz.overscroll.enabled", true); #endif pref("layers.progressive-paint", true); -#ifdef NIGHTLY_BUILD -pref("layers.low-precision-buffer", false); -#else pref("layers.low-precision-buffer", true); -#endif pref("layers.low-precision-resolution", "0.25"); pref("layers.low-precision-opacity", "1.0"); // We want to limit layers for two reasons: @@ -744,10 +740,6 @@ pref("layout.framevisibility.numscrollportwidths", 1); pref("layout.framevisibility.numscrollportheights", 1); pref("layers.enable-tiles", true); -#ifdef NIGHTLY_BUILD -pref("layers.tiles.fade-in.enabled", true); -pref("layers.tiles.fade-in.duration-ms", 250); -#endif // Enable the dynamic toolbar pref("browser.chrome.dynamictoolbar", true); From 50c92f7548ee6ed882e97f81d79a23810bbcb87c Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Sun, 29 May 2016 12:01:18 -0400 Subject: [PATCH 103/199] Bug 1276966 - Fix tests that were accidentally resetting browser.startup.page. r=mikedeboer MozReview-Commit-ID: 3E4xVfdGDIi --HG-- extra : rebase_source : ecb6d86c9d045160ad6b0484a0c28bb75bb13440 --- .../content/test/general/browser_bug882977.js | 59 +++++++------- .../sessionstore/test/browser_480893.js | 78 +++++++++---------- 2 files changed, 65 insertions(+), 72 deletions(-) diff --git a/browser/base/content/test/general/browser_bug882977.js b/browser/base/content/test/general/browser_bug882977.js index ebdac6d53ca9..cf64e1f6972e 100644 --- a/browser/base/content/test/general/browser_bug882977.js +++ b/browser/base/content/test/general/browser_bug882977.js @@ -1,39 +1,36 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ +"use strict"; + +/** + * Tests that the identity-box shows the chromeUI styling + * when viewing about:home in a new window. */ - -function test() { - waitForExplicitFinish(); - - registerCleanupFunction(function() { - Services.prefs.clearUserPref("browser.startup.homepage"); - Services.prefs.clearUserPref("browser.startup.page"); - win.close(); - }); - +add_task(function*(){ let homepage = "about:home"; - Services.prefs.setCharPref("browser.startup.homepage", homepage); - Services.prefs.setIntPref("browser.startup.page", 1); - let win = OpenBrowserWindow(); - whenDelayedStartupFinished(win, function() { - let browser = win.gBrowser.selectedBrowser; - if (browser.contentDocument.readyState == "complete" && - browser.currentURI.spec == homepage) { - checkIdentityMode(win); - return; - } - - browser.addEventListener("load", function onLoad() { - if (browser.currentURI.spec != homepage) - return; - browser.removeEventListener("load", onLoad, true); - checkIdentityMode(win); - }, true); + yield SpecialPowers.pushPrefEnv({ + "set": [ + ["browser.startup.homepage", homepage], + ["browser.startup.page", 1], + ] }); -} + + let win = OpenBrowserWindow(); + yield BrowserTestUtils.waitForEvent(win, "load"); + + let browser = win.gBrowser.selectedBrowser; + // If we've finished loading about:home already, we can check + // right away - otherwise, we need to wait. + if (browser.contentDocument.readyState == "complete" && + browser.currentURI.spec == homepage) { + checkIdentityMode(win); + } else { + yield BrowserTestUtils.browserLoaded(browser, false, homepage); + checkIdentityMode(win); + } + + yield BrowserTestUtils.closeWindow(win); +}); function checkIdentityMode(win) { let identityMode = win.document.getElementById("identity-box").className; is(identityMode, "chromeUI", "Identity state should be chromeUI for about:home in a new window"); - finish(); } diff --git a/browser/components/sessionstore/test/browser_480893.js b/browser/components/sessionstore/test/browser_480893.js index c4c4c32494c6..e3ddb39b71db 100644 --- a/browser/components/sessionstore/test/browser_480893.js +++ b/browser/components/sessionstore/test/browser_480893.js @@ -1,51 +1,47 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; -function test() { - /** Test for Bug 480893 **/ +/** + * Tests that we get sent to the right page when the user clicks + * the "Close" button in about:sessionrestore + */ +add_task(function*() { + yield SpecialPowers.pushPrefEnv({ + "set": [ + ["browser.startup.page", 0], + ] + }); - waitForExplicitFinish(); - - // Test that starting a new session loads a blank page if Firefox is - // configured to display a blank page at startup (browser.startup.page = 0) - gPrefService.setIntPref("browser.startup.page", 0); let tab = gBrowser.addTab("about:sessionrestore"); gBrowser.selectedTab = tab; let browser = tab.linkedBrowser; - promiseBrowserLoaded(browser).then(() => { - let doc = browser.contentDocument; + yield BrowserTestUtils.browserLoaded(browser, false, "about:sessionrestore"); - // click on the "Start New Session" button after about:sessionrestore is loaded - doc.getElementById("errorCancel").click(); - promiseBrowserLoaded(browser).then(() => { - let doc = browser.contentDocument; + let doc = browser.contentDocument; - is(doc.URL, "about:blank", "loaded page is about:blank"); + // Click on the "Close" button after about:sessionrestore is loaded. + doc.getElementById("errorCancel").click(); - // Test that starting a new session loads the homepage (set to http://mochi.test:8888) - // if Firefox is configured to display a homepage at startup (browser.startup.page = 1) - let homepage = "http://mochi.test:8888/"; - gPrefService.setCharPref("browser.startup.homepage", homepage); - gPrefService.setIntPref("browser.startup.page", 1); - gBrowser.loadURI("about:sessionrestore"); - promiseBrowserLoaded(browser).then(() => { - let doc = browser.contentDocument; + yield BrowserTestUtils.browserLoaded(browser, false, "about:blank"); - // click on the "Start New Session" button after about:sessionrestore is loaded - doc.getElementById("errorCancel").click(); - promiseBrowserLoaded(browser).then(() => { - let doc = browser.contentDocument; - - is(doc.URL, homepage, "loaded page is the homepage"); - - // close tab, restore default values and finish the test - gBrowser.removeTab(tab); - gPrefService.clearUserPref("browser.startup.page"); - gPrefService.clearUserPref("browser.startup.homepage"); - finish(); - }); - }); - }); + // Test that starting a new session loads the homepage (set to http://mochi.test:8888) + // if Firefox is configured to display a homepage at startup (browser.startup.page = 1) + let homepage = "http://mochi.test:8888/"; + yield SpecialPowers.pushPrefEnv({ + "set": [ + ["browser.startup.homepage", homepage], + ["browser.startup.page", 1], + ] }); -} + + browser.loadURI("about:sessionrestore"); + yield BrowserTestUtils.browserLoaded(browser, false, "about:sessionrestore"); + doc = browser.contentDocument; + + // Click on the "Close" button after about:sessionrestore is loaded. + doc.getElementById("errorCancel").click(); + yield BrowserTestUtils.browserLoaded(browser); + + is(browser.currentURI.spec, homepage, "loaded page is the homepage"); + + yield BrowserTestUtils.removeTab(tab); +}); From 89b93668b8a7059c5eeb172a4a954d3cb71eb9f5 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 27 May 2016 13:28:26 -0400 Subject: [PATCH 104/199] Bug 1276112. Stop using GetScriptContextFromJSContext in CycleCollectedJSRuntime::UsefulToMergeZones. r=mccr8 --- js/xpconnect/src/XPCJSRuntime.cpp | 16 ++++++++++++++ js/xpconnect/src/xpcprivate.h | 1 + xpcom/base/CycleCollectedJSRuntime.cpp | 30 -------------------------- xpcom/base/CycleCollectedJSRuntime.h | 2 +- 4 files changed, 18 insertions(+), 31 deletions(-) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 32a37b2f1785..92873005c58b 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -596,6 +596,22 @@ CompartmentSizeOfIncludingThisCallback(MallocSizeOf mallocSizeOf, JSCompartment* return priv ? priv->SizeOfIncludingThis(mallocSizeOf) : 0; } +/* + * Return true if there exists a non-system inner window which is a current + * inner window and whose reflector is gray. We don't merge system + * compartments, so we don't use them to trigger merging CCs. + */ +bool XPCJSRuntime::UsefulToMergeZones() const +{ + MOZ_ASSERT(NS_IsMainThread()); + + // Turns out, actually making this return true often enough makes Windows + // mochitest-gl OOM a lot. Need to figure out what's going on there; see + // bug 1277036. + + return false; +} + void XPCJSRuntime::TraceNativeBlackRoots(JSTracer* trc) { // Skip this part if XPConnect is shutting down. We get into diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 24a91134b4bd..ed5ea2ec57cc 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -543,6 +543,7 @@ public: return mStrings[index]; } + virtual bool UsefulToMergeZones() const override; void TraceNativeBlackRoots(JSTracer* trc) override; void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) override; void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb) override; diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 45acdc6d7752..2fc5c32e9de4 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1176,39 +1176,9 @@ CycleCollectedJSRuntime::TraverseRoots(nsCycleCollectionNoteRootCallback& aCb) return NS_OK; } -/* - * Return true if there exists a JSContext with a default global whose current - * inner is gray. The intent is to look for JS Object windows. We don't merge - * system compartments, so we don't use them to trigger merging CCs. - */ bool CycleCollectedJSRuntime::UsefulToMergeZones() const { - MOZ_ASSERT(mJSRuntime); - - if (!NS_IsMainThread()) { - return false; - } - - JSContext* iter = nullptr; - JSContext* cx; - JSAutoRequest ar(nsContentUtils::GetSafeJSContext()); - while ((cx = JS_ContextIterator(mJSRuntime, &iter))) { - // Skip anything without an nsIScriptContext. - nsIScriptContext* scx = GetScriptContextFromJSContext(cx); - JS::RootedObject obj(cx, scx ? scx->GetWindowProxyPreserveColor() : nullptr); - if (!obj) { - continue; - } - MOZ_ASSERT(js::IsWindowProxy(obj)); - // Grab the global from the WindowProxy. - obj = js::ToWindowIfWindowProxy(obj); - MOZ_ASSERT(JS_IsGlobalObject(obj)); - if (JS::ObjectIsMarkedGray(obj) && - !js::IsSystemCompartment(js::GetObjectCompartment(obj))) { - return true; - } - } return false; } diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index eb2ab71fd35c..008f0bc45f3f 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -299,7 +299,7 @@ public: nsCycleCollectionParticipant* ZoneParticipant(); nsresult TraverseRoots(nsCycleCollectionNoteRootCallback& aCb); - bool UsefulToMergeZones() const; + virtual bool UsefulToMergeZones() const; void FixWeakMappingGrayBits() const; bool AreGCGrayBitsValid() const; void GarbageCollect(uint32_t aReason) const; From ceff37e7d029970e0b69dae6a9d6b984bde498f7 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Tue, 31 May 2016 22:38:44 +0300 Subject: [PATCH 105/199] bug 1276013, fix regression in e10s window name handling, r=mconley --HG-- extra : rebase_source : 0ed994af42ae9f070fb31ec4a1e82914dbf7e48e --- dom/ipc/ContentParent.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 45cb646f44ea..18fb6fd7ae65 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5539,10 +5539,13 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab, nsCOMPtr window; TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad); - const char* name = aName.IsVoid() ? nullptr : NS_ConvertUTF16toUTF8(aName).get(); const char* features = aFeatures.IsVoid() ? nullptr : aFeatures.get(); - *aResult = pwwatch->OpenWindow2(parent, nullptr, name, features, aCalledFromJS, + *aResult = pwwatch->OpenWindow2(parent, nullptr, + aName.IsVoid() ? + nullptr : + NS_ConvertUTF16toUTF8(aName).get(), + features, aCalledFromJS, false, false, thisTabParent, nullptr, aFullZoom, 1, getter_AddRefs(window)); From b6345324c6a84ffa4a4278ab686b7d2a0962d582 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Tue, 31 May 2016 23:05:55 +0300 Subject: [PATCH 106/199] Bug 1276424 - EventListenerWasAdded/Remove doesn't work in JS implemented webidl because of missing 'override', r=bz --HG-- extra : rebase_source : 9bc0109e898b462b500fb40da836c0fb8660b18e --- dom/bindings/Codegen.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index c452d3e141d5..8d142cfc8854 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -13656,7 +13656,9 @@ class CGNativeMember(ClassMethod): def __init__(self, descriptorProvider, member, name, signature, extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True, visibility="public", typedArraysAreStructs=True, variadicIsSequence=False, - resultNotAddRefed=False): + resultNotAddRefed=False, + virtual=False, + override=False): """ If typedArraysAreStructs is false, typed arrays will be passed as JS::Handle. If it's true they will be passed as one of the @@ -13684,7 +13686,9 @@ class CGNativeMember(ClassMethod): not signature[0].isVoid()), breakAfterReturnDecl=" ", breakAfterSelf=breakAfterSelf, - visibility=visibility) + visibility=visibility, + virtual=virtual, + override=override) def getReturnType(self, type, isMember): return self.getRetvalInfo(type, isMember)[0] @@ -14448,12 +14452,15 @@ class CGJSImplMember(CGNativeMember): """ def __init__(self, descriptorProvider, member, name, signature, extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True, - visibility="public", variadicIsSequence=False): + visibility="public", variadicIsSequence=False, + virtual=False, override=False): CGNativeMember.__init__(self, descriptorProvider, member, name, signature, extendedAttrs, breakAfter=breakAfter, passJSBitsAsNeeded=passJSBitsAsNeeded, visibility=visibility, - variadicIsSequence=variadicIsSequence) + variadicIsSequence=variadicIsSequence, + virtual=virtual, + override=override) self.body = self.getImpl() def getArgs(self, returnType, argList): @@ -14468,6 +14475,13 @@ class CGJSImplMethod(CGJSImplMember): interface. """ def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): + virtual = False + override = False + if (method.identifier.name == "eventListenerWasAdded" or + method.identifier.name == "eventListenerWasRemoved"): + virtual = True + override = True + self.signature = signature self.descriptor = descriptor self.isConstructor = isConstructor @@ -14478,7 +14492,9 @@ class CGJSImplMethod(CGJSImplMember): descriptor.getExtendedAttributes(method), breakAfter=breakAfter, variadicIsSequence=True, - passJSBitsAsNeeded=False) + passJSBitsAsNeeded=False, + virtual=virtual, + override=override) def getArgs(self, returnType, argList): if self.isConstructor: From ec8fe953e8ad39a0b6a52b88a31a1e7e6d175c25 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Tue, 31 May 2016 16:15:01 -0400 Subject: [PATCH 107/199] Bug 1270962 - move additional tests to the clipboard job. r=RyanVM MozReview-Commit-ID: CYoapJ3zb8P --- browser/base/content/test/general/browser.ini | 3 +++ dom/base/test/mochitest.ini | 1 + dom/browser-element/mochitest/mochitest-oop.ini | 3 ++- dom/media/tests/mochitest/mochitest.ini | 1 - dom/tests/mochitest/general/mochitest.ini | 4 ++++ 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 5d80e2c4c49d..5c3a9d63f1ea 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -167,6 +167,7 @@ skip-if = true # browser_bug321000.js is disabled because newline handling is sh [browser_bug406216.js] [browser_bug408415.js] [browser_bug409481.js] +subsuite = clipboard [browser_bug409624.js] [browser_bug413915.js] [browser_bug416661.js] @@ -284,6 +285,7 @@ skip-if = os == 'win' [browser_clipboard.js] subsuite = clipboard [browser_clipboard_pastefile.js] +subsuite = clipboard [browser_contentAreaClick.js] [browser_contextmenu.js] tags = fullscreen @@ -506,6 +508,7 @@ tags = mcb [browser_contextmenu_childprocess.js] [browser_bug963945.js] [browser_readerMode.js] +subsuite = clipboard support-files = readerModeArticle.html readerModeArticleHiddenNodes.html diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 7425462e8aed..ecadff8ca8df 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -337,6 +337,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec [test_bug330925.xhtml] [test_bug331959.html] [test_bug333064.html] +subsuite = clipboard skip-if = toolkit == 'android' [test_bug333198.html] [test_bug333673.html] diff --git a/dom/browser-element/mochitest/mochitest-oop.ini b/dom/browser-element/mochitest/mochitest-oop.ini index f0d5dcc1ff78..b191a127df56 100644 --- a/dom/browser-element/mochitest/mochitest-oop.ini +++ b/dom/browser-element/mochitest/mochitest-oop.ini @@ -40,6 +40,7 @@ skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_oop_Close.html] [test_browserElement_oop_CookiesNotThirdParty.html] [test_browserElement_oop_CopyPaste.html] +subsuite = clipboard [test_browserElement_oop_DOMRequestError.html] [test_browserElement_oop_DataURI.html] [test_browserElement_oop_DisallowEmbedAppsInOOP.html] @@ -131,4 +132,4 @@ tags = audiochannel [test_browserElement_oop_OpenWindowEmpty.html] skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator [test_browserElement_oop_ActiveStateChangeOnChangingMutedOrVolume.html] -tags = audiochannel \ No newline at end of file +tags = audiochannel diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index cebe42781efd..266f6c631766 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -129,7 +129,6 @@ skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio skip-if = buildapp == 'b2g' || buildapp == 'mulet' || os == 'android' # bug 1043403 # Bug 1141029 Mulet parity with B2G Desktop for TC [test_peerConnection_bug1064223.html] [test_peerConnection_capturedVideo.html] -subsuite = clipboard tags=capturestream skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) [test_peerConnection_captureStream_canvas_2d.html] diff --git a/dom/tests/mochitest/general/mochitest.ini b/dom/tests/mochitest/general/mochitest.ini index 06f382902133..4c5a227242a6 100644 --- a/dom/tests/mochitest/general/mochitest.ini +++ b/dom/tests/mochitest/general/mochitest.ini @@ -65,6 +65,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec [test_bug861217.html] [test_clientRects.html] [test_clipboard_disallowed.html] +subsuite = clipboard [test_clipboard_events.html] subsuite = clipboard skip-if = buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined) @@ -87,6 +88,7 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIME skip-if = buildapp == 'b2g' || buildapp == 'mulet' [test_img_mutations.html] [test_interfaces.html] +subsuite = clipboard skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage # [test_network_events.html] # Disable this test until bug 795711 is fixed. @@ -96,6 +98,7 @@ support-files = test_offsets.js [test_outerHTML.xhtml] skip-if = buildapp == 'mulet' [test_paste_selection.html] +subsuite = clipboard skip-if = buildapp == 'mulet' [test_performance_timeline.html] [test_picture_mutations.html] @@ -127,6 +130,7 @@ subsuite = clipboard skip-if = (toolkit == 'android') # Disabled on Android, see bug 1230231 [test_bug1161721.html] [test_bug1170911.html] +subsuite = clipboard [test_storagePermissionsAccept.html] skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g [test_storagePermissionsRejectForeign.html] From cf9da8546b62b4eb0e1876c014e50248fc9e0aab Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Tue, 31 May 2016 16:15:03 -0400 Subject: [PATCH 108/199] Bug 1276672 - disable browser_mozLoop_telemetry.js on win7_opt for zombieleak failures. r=RyanVM MozReview-Commit-ID: 3jcb2vCKpP6 --- browser/extensions/loop/chrome/test/mochitest/browser.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/extensions/loop/chrome/test/mochitest/browser.ini b/browser/extensions/loop/chrome/test/mochitest/browser.ini index 95a3d9cac7f0..e4daedcabe53 100644 --- a/browser/extensions/loop/chrome/test/mochitest/browser.ini +++ b/browser/extensions/loop/chrome/test/mochitest/browser.ini @@ -18,6 +18,7 @@ support-files = [browser_panel_privateBrowsing.js] [browser_mozLoop_sharingListeners.js] [browser_mozLoop_telemetry.js] +skip-if = os == win && !debug # Bug 1267562 zombiecheck | child process 1228 still alive after shutdown (on win7-vm specifically) [browser_sharingTitleListeners.js] [browser_throttler.js] [browser_toolbarbutton.js] From bc7d8b91bd525308cbb6eac7334b999763db4128 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 29 Sep 2015 13:39:38 -0700 Subject: [PATCH 109/199] Bug 1259850 - Switch to using a Map for visited points within a function, and heavily comment, r=terrence MozReview-Commit-ID: LvU4upaHt8b --HG-- extra : rebase_source : 2f6d0a05594047dd505e1c63e0b52a94e1d3f5da extra : source : 15de0d1d0b05457c18dcf70e4e81d9bfe5ad5f79 --- js/src/devtools/rootAnalysis/analyzeRoots.js | 74 +++++++++++++------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index 09b4de127a62..d757c2319640 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -331,31 +331,57 @@ function edgeCanGC(edge) // // - 'gcInfo': a direct pointer to the GC call edge // -function findGCBeforeVariableUse(suppressed, variable, worklist) +function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) { // Scan through all edges preceding an unrooted variable use, using an // explicit worklist, looking for a GC call. A worklist contains an // incoming edge together with a description of where it or one of its // successors GC'd (if any). + var bodies_visited = new Map(); + + let worklist = [{body: start_body, ppoint: start_point, gcInfo: null, why: null}]; while (worklist.length) { + // Grab an entry off of the worklist, representing a point within the + // CFG identified by . If this point has a descendant + // later in the CFG that can GC, gcInfo will be set to the information + // about that GC call. + var entry = worklist.pop(); var { body, ppoint, gcInfo } = entry; - if (body.seen) { - if (ppoint in body.seen) { - var seenEntry = body.seen[ppoint]; - if (!gcInfo || seenEntry.gcInfo) - continue; - } - } else { - body.seen = []; - } - body.seen[ppoint] = {body: body, gcInfo: gcInfo}; + // Handle the case where there are multiple ways to reach this point + // (traversing backwards). + var visited = bodies_visited.get(body); + if (!visited) + bodies_visited.set(body, visited = new Map()); + if (visited.has(ppoint)) { + var seenEntry = visited.get(ppoint); + // This point already knows how to GC through some other path, so + // we have nothing new to learn. (The other path will consider the + // predecessors.) + if (seenEntry.gcInfo) + continue; + + // If this worklist's entry doesn't know of any way to GC, then + // there's no point in continuing the traversal through it. Perhaps + // another edge will be found that *can* GC; otherwise, the first + // route to the point will traverse through predecessors. + // + // Note that this means we may visit a point more than once, if the + // first time we visit we don't have a known reachable GC call and + // the second time we do. + if (!gcInfo) + continue; + } + visited.set(ppoint, {body: body, gcInfo: gcInfo}); + + // Check for hitting the entry point of the current body (which may be + // the outer function or a loop within it.) if (ppoint == body.Index[0]) { if (body.BlockId.Kind == "Loop") { - // propagate to parents that enter the loop body. + // Propagate to outer body parents that enter the loop body. if ("BlockPPoint" in body) { for (var parent of body.BlockPPoint) { var found = false; @@ -399,11 +425,10 @@ function findGCBeforeVariableUse(suppressed, variable, worklist) // to a use after the GC call that proves its live range // extends at least that far. if (gcInfo) - return {gcInfo: gcInfo, why: {body: body, ppoint: source, gcInfo: gcInfo, why: entry } } + return {gcInfo: gcInfo, why: {body: body, ppoint: source, gcInfo: gcInfo, why: entry } }; - // Otherwise, we want to continue searching for the true - // minimumUse, for use in reporting unnecessary rooting, but we - // truncate this particular branch of the search at this edge. + // Otherwise, keep searching through the graph, but truncate + // this particular branch of the search at this edge. continue; } @@ -415,7 +440,10 @@ function findGCBeforeVariableUse(suppressed, variable, worklist) if (edge_uses) { // The live range starts at least this far back, so we're done - // for the same reason as with edge_kills. + // for the same reason as with edge_kills. The only difference + // is that a GC on this edge indicates a hazard, whereas if + // we're killing a live range in the GC call then it's not live + // *across* the call. if (gcInfo) return {gcInfo:gcInfo, why:entry}; } @@ -446,13 +474,12 @@ function findGCBeforeVariableUse(suppressed, variable, worklist) function variableLiveAcrossGC(suppressed, variable) { - // A variable is live across a GC if (1) it is used by an edge, and (2) it - // is used after a GC in a successor edge. + // A variable is live across a GC if (1) it is used by an edge (as in, it + // was at least initialized), and (2) it is used after a GC in a successor + // edge. - for (var body of functionBodies) { - body.seen = null; + for (var body of functionBodies) body.minimumUse = 0; - } for (var body of functionBodies) { if (!("PEdge" in body)) @@ -467,8 +494,7 @@ function variableLiveAcrossGC(suppressed, variable) // if (usePoint && !edgeKillsVariable(edge, variable)) { // Found a use, possibly after a GC. - var worklist = [{body:body, ppoint:usePoint, gcInfo:null, why:null}]; - var call = findGCBeforeVariableUse(suppressed, variable, worklist); + var call = findGCBeforeVariableUse(body, usePoint, suppressed, variable); if (!call) continue; From 13d4ab4e8501801f067c74831be9eed015f24996 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 29 Sep 2015 13:39:38 -0700 Subject: [PATCH 110/199] Bug 1259850 - Rename function, add a few comments, r=terrence MozReview-Commit-ID: Fyk1zzbWGnh --HG-- extra : rebase_source : 61c6900bac4bea0e2c645334d3984614f2ba4a2e extra : source : e8544b072ee6b0055984ad7307216d61d4d3a26f --- js/src/devtools/rootAnalysis/analyzeRoots.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index d757c2319640..82132789d000 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -527,7 +527,9 @@ function unsafeVariableAddressTaken(suppressed, variable) return null; } -function computePrintedLines(functionName) +// Read out the brief (non-JSON, semi-human-readable) CFG description for the +// given function and store it. +function loadPrintedLines(functionName) { assert(!os.system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile)); var lines = snarf(tmpfile).split('\n'); @@ -583,7 +585,7 @@ function printEntryTrace(functionName, entry) var gcPoint = entry.gcInfo ? entry.gcInfo.ppoint : 0; if (!functionBodies[0].lines) - computePrintedLines(functionName); + loadPrintedLines(functionName); while (entry) { var ppoint = entry.ppoint; @@ -598,8 +600,8 @@ function printEntryTrace(functionName, entry) var table = {}; entry.body.edgeTable = table; for (var line of entry.body.lines) { - if (match = /\((\d+),(\d+),/.exec(line)) - table[match[1] + "," + match[2]] = line; // May be multiple? + if (match = /\((\d+,\d+),/.exec(line)) + table[match[1]] = line; // May be multiple? } } From 6907840a2ba7feafdba80060fd5014f6ffbcf1be Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 15:12:31 -0700 Subject: [PATCH 111/199] Bug 1259850 - The explanations of why something is a hazard went up to just before the initial use of a variable. Extend them one further, r=terrence MozReview-Commit-ID: 9l8ftRv3yjS --HG-- extra : rebase_source : 98f0261e331518d92a8174513afad5be327a4407 extra : source : a8d2730ada95c70059ed6314f5eb3f7b28501f70 --- js/src/devtools/rootAnalysis/analyzeRoots.js | 74 +++++++++++++++----- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index 82132789d000..ff03912fabaf 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -340,7 +340,7 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) var bodies_visited = new Map(); - let worklist = [{body: start_body, ppoint: start_point, gcInfo: null, why: null}]; + let worklist = [{body: start_body, ppoint: start_point, preGCLive: false, gcInfo: null, why: null}]; while (worklist.length) { // Grab an entry off of the worklist, representing a point within the // CFG identified by . If this point has a descendant @@ -348,7 +348,7 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) // about that GC call. var entry = worklist.pop(); - var { body, ppoint, gcInfo } = entry; + var { body, ppoint, gcInfo, preGCLive } = entry; // Handle the case where there are multiple ways to reach this point // (traversing backwards). @@ -399,7 +399,13 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) } else if (variable.Kind == "Arg" && gcInfo) { // The scope of arguments starts at the beginning of the // function - return {gcInfo: gcInfo, why: entry}; + return entry; + } else if (entry.preGCLive) { + // We didn't find a "good" explanation beginning of the live + // range, but we do know the variable was live across the GC. + // This can happen if the live range started when a variable is + // used as a retparam. + return entry; } } @@ -425,17 +431,19 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) // to a use after the GC call that proves its live range // extends at least that far. if (gcInfo) - return {gcInfo: gcInfo, why: {body: body, ppoint: source, gcInfo: gcInfo, why: entry } }; + return {body: body, ppoint: source, gcInfo: gcInfo, why: entry }; // Otherwise, keep searching through the graph, but truncate // this particular branch of the search at this edge. continue; } + var src_gcInfo = gcInfo; + var src_preGCLive = preGCLive; if (!gcInfo && !(source in body.suppressed) && !suppressed) { var gcName = edgeCanGC(edge, body); if (gcName) - gcInfo = {name:gcName, body:body, ppoint:source}; + src_gcInfo = {name:gcName, body:body, ppoint:source}; } if (edge_uses) { @@ -444,8 +452,31 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) // is that a GC on this edge indicates a hazard, whereas if // we're killing a live range in the GC call then it's not live // *across* the call. - if (gcInfo) - return {gcInfo:gcInfo, why:entry}; + // + // However, we may want to generate a longer usage chain for + // the variable than is minimally necessary. For example, + // consider: + // + // Value v = f(); + // if (v.isUndefined()) + // return false; + // gc(); + // return v; + // + // The call to .isUndefined() is considered to be a use and + // therefore indicates that v must be live at that point. But + // it's more helpful to the user to continue the 'why' path to + // include the ancestor where the value was generated. So we + // will only return here if edge.Kind is Assign; otherwise, + // we'll pass a "preGCLive" value up through the worklist to + // remember that the variable *is* alive before the GC and so + // this function should be returning a true value. + + if (src_gcInfo) { + src_preGCLive = true; + if (edge.Kind == 'Assign') + return {body:body, ppoint:source, gcInfo:src_gcInfo, why:entry}; + } } if (edge.Kind == "Loop") { @@ -457,7 +488,8 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) assert(!found); found = true; worklist.push({body:xbody, ppoint:xbody.Index[1], - gcInfo:gcInfo, why:entry}); + preGCLive: src_preGCLive, gcInfo:src_gcInfo, + why:entry}); } } assert(found); @@ -465,7 +497,9 @@ function findGCBeforeVariableUse(start_body, start_point, suppressed, variable) } // Propagate the search to the predecessors of this edge. - worklist.push({body:body, ppoint:source, gcInfo:gcInfo, why:entry}); + worklist.push({body:body, ppoint:source, + preGCLive: src_preGCLive, gcInfo:src_gcInfo, + why:entry}); } } @@ -564,13 +598,21 @@ function loadPrintedLines(functionName) } } -function findLocation(body, ppoint) +function findLocation(body, ppoint, opts={brief: false}) { var location = body.PPoint[ppoint - 1].Location; - var text = location.CacheString + ":" + location.Line; - if (text.indexOf(sourceRoot) == 0) - return text.substring(sourceRoot.length); - return text; + var file = location.CacheString; + + if (file.indexOf(sourceRoot) == 0) + file = file.substring(sourceRoot.length); + + if (opts.brief) { + var m = /.*\/(.*)/.exec(file); + if (m) + file = m[1]; + } + + return file + ":" + location.Line; } function locationLine(text) @@ -589,7 +631,7 @@ function printEntryTrace(functionName, entry) while (entry) { var ppoint = entry.ppoint; - var lineText = findLocation(entry.body, ppoint); + var lineText = findLocation(entry.body, ppoint, {"brief": true}); var edgeText = ""; if (entry.why && entry.why.body == entry.body) { @@ -687,7 +729,7 @@ function processBodies(functionName) " of type '" + typeDesc(variable.Type) + "'" + " live across GC call " + result.gcInfo.name + " at " + lineText); - printEntryTrace(functionName, result.why); + printEntryTrace(functionName, result); } result = unsafeVariableAddressTaken(suppressed, variable.Variable); if (result) { From 024c4b0d1a816b76da60bf217778ecef8f734434 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 15:18:57 -0700 Subject: [PATCH 112/199] Bug 1259850 - Convert memoizer to Map, r=terrence MozReview-Commit-ID: 2Ps8gJpztw2 --HG-- extra : rebase_source : 1ac0eeb48f468d05cb71eb88c9b75f1b31b7d5f2 extra : source : 0d58f8529b86a129f178788bd056d2a60cc73319 --- js/src/devtools/rootAnalysis/computeCallgraph.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index 5aeae6af843d..d7962b669e8e 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -104,17 +104,17 @@ function findVirtualFunctions(initialCSU, field, suppressed) return functions; } -var memoized = {}; +var memoized = new Map(); var memoizedCount = 0; function memo(name) { - if (!(name in memoized)) { - memoizedCount++; - memoized[name] = "" + memoizedCount; - print("#" + memoizedCount + " " + name); + if (!memoized.has(name)) { + let id = memoized.size + 1; + memoized.set(name, "" + id); + print(`#${id} ${name}`); } - return memoized[name]; + return memoized.get(name); } var seenCallees = null; From 64a388b576c05c5e4561a11660154e1aadd8bcb0 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 15:24:57 -0700 Subject: [PATCH 113/199] Bug 1259850 - Refactoring: more Set/Map usage, r=terrence MozReview-Commit-ID: 2Ps8gJpztw2 --HG-- extra : rebase_source : 8cdfda64ecae7d68df700a8275744af4c06bcf94 extra : source : b6108a65dc38d298be19b74a30dc683bec69a3b3 --- js/src/devtools/rootAnalysis/annotations.js | 6 + .../devtools/rootAnalysis/computeCallgraph.js | 104 ++++++++---------- js/src/devtools/rootAnalysis/utility.js | 9 ++ 3 files changed, 59 insertions(+), 60 deletions(-) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index b9072dbfb8f2..fd4dac78e09e 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -146,6 +146,12 @@ function ignoreEdgeAddressTaken(edge) return false; } +// Return whether csu.method is one that we claim can never GC. +function isSuppressedVirtualMethod(csu, method) +{ + return csu == "nsISupports" && (method == "AddRef" || method == "Release"); +} + // Ignore calls of these functions (so ignore any stack containing these) var ignoreFunctions = { "ptio.c:pt_MapError" : true, diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index d7962b669e8e..02ede9af9e29 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -12,25 +12,17 @@ if (scriptArgs[0] == '--function') { scriptArgs = scriptArgs.slice(2); } -var subclasses = {}; -var superclasses = {}; -var classFunctions = {}; +var subclasses = new Map(); // Map from csu => set of immediate subclasses +var superclasses = new Map(); // Map from csu => set of immediate superclasses +var classFunctions = new Map(); // Map from "csu:name" => set of full method name -var fieldCallSeen = {}; +var virtualResolutionsSeen = new Set(); -function addClassEntry(index, name, other) +function addEntry(map, name, entry) { - if (!(name in index)) { - index[name] = [other]; - return; - } - - for (var entry of index[name]) { - if (entry == other) - return; - } - - index[name].push(other); + if (!map.has(name)) + map.set(name, new Set()); + map.get(name).add(entry); } // CSU is "Class/Struct/Union" @@ -43,16 +35,14 @@ function processCSU(csuName, csu) var superclass = field.Field[1].Type.Name; var subclass = field.Field[1].FieldCSU.Type.Name; assert(subclass == csuName); - addClassEntry(subclasses, superclass, subclass); - addClassEntry(superclasses, subclass, superclass); + addEntry(subclasses, superclass, subclass); + addEntry(superclasses, subclass, superclass); } if ("Variable" in field) { // Note: not dealing with overloading correctly. var name = field.Variable.Name[0]; var key = csuName + ":" + field.Field[0].Name[0]; - if (!(key in classFunctions)) - classFunctions[key] = []; - classFunctions[key].push(name); + addEntry(classFunctions, key, name); } } } @@ -60,7 +50,7 @@ function processCSU(csuName, csu) function findVirtualFunctions(initialCSU, field, suppressed) { var worklist = [initialCSU]; - var functions = []; + var functions = new Set(); // Virtual call targets on subclasses of nsISupports may be incomplete, // if the interface is scriptable. Just treat all indirect calls on @@ -68,21 +58,19 @@ function findVirtualFunctions(initialCSU, field, suppressed) // which should never enter the JS engine (even when calling dtors). while (worklist.length) { var csu = worklist.pop(); - if (csu == "nsISupports" && (field == "AddRef" || field == "Release")) { + if (isSuppressedVirtualMethod(csu, field)) { suppressed[0] = true; - return []; + return new Set(); } if (isOverridableField(initialCSU, csu, field)) { // We will still resolve the virtual function call, because it's // nice to have as complete a callgraph as possible for other uses. // But push a token saying that we can run arbitrary code. - functions.push(null); + functions.add(null); } - if (csu in superclasses) { - for (var superclass of superclasses[csu]) - worklist.push(superclass); - } + if (superclasses.has(csu)) + worklist.push(...superclasses.get(csu)); } worklist = [csu]; @@ -90,15 +78,11 @@ function findVirtualFunctions(initialCSU, field, suppressed) var csu = worklist.pop(); var key = csu + ":" + field; - if (key in classFunctions) { - for (var name of classFunctions[key]) - functions.push(name); - } + if (classFunctions.has(key)) + functions.update(classFunctions.get(key)); - if (csu in subclasses) { - for (var subclass of subclasses[csu]) - worklist.push(subclass); - } + if (subclasses.has(csu)) + worklist.push(...subclasses.get(csu)); } return functions; @@ -117,12 +101,11 @@ function memo(name) return memoized.get(name); } -var seenCallees = null; -var seenSuppressedCallees = null; - // Return a list of all callees that the given edge might be a call to. Each // one is represented by an object with a 'kind' field that is one of -// ('direct', 'field', 'indirect', 'unknown'). +// ('direct', 'field', 'resolved-field', 'indirect', 'unknown'), though note +// that 'resolved-field' is really a global record of virtual method +// resolutions, indepedent of this particular edge. function getCallees(edge) { if (edge.Kind != "Call") @@ -236,39 +219,45 @@ function processBody(functionName, body) for (var tag of getTags(functionName, body).values()) print("T " + memo(functionName) + " " + tag); + // Set of all callees that have been output so far, in order to suppress + // repeated callgraph edges from being recorded. Use a separate set for + // suppressed callees, since we don't want a suppressed edge (within one + // RAII scope) to prevent an unsuppressed edge from being recorded. The + // seen array is indexed by a boolean 'suppressed' variable. + var seen = [ new Set(), new Set() ]; + lastline = null; for (var edge of body.PEdge) { if (edge.Kind != "Call") continue; - var edgeSuppressed = false; - var seen = seenCallees; - if (edge.Index[0] in body.suppressed) { - edgeSuppressed = true; - seen = seenSuppressedCallees; - } + + // Whether this call is within the RAII scope of a GC suppression class + var edgeSuppressed = (edge.Index[0] in body.suppressed); + for (var callee of getCallees(edge)) { - var prologue = (edgeSuppressed || callee.suppressed) ? "SUPPRESS_GC " : ""; + var suppressed = Boolean(edgeSuppressed || callee.suppressed); + var prologue = suppressed ? "SUPPRESS_GC " : ""; prologue += memo(functionName) + " "; if (callee.kind == 'direct') { - if (!(callee.name in seen)) { - seen[callee.name] = true; + if (!seen[+suppressed].has(callee.name)) { + seen[+suppressed].add(callee.name); printOnce("D " + prologue + memo(callee.name)); } } else if (callee.kind == 'field') { var { csu, field } = callee; printOnce("F " + prologue + "CLASS " + csu + " FIELD " + field); } else if (callee.kind == 'resolved-field') { - // Fully-resolved field call (usually a virtual method). Record - // the callgraph edges. Do not consider suppression, since it - // is local to this callsite and we are writing out a global + // Fully-resolved field (virtual method) call. Record the + // callgraph edges. Do not consider suppression, since it is + // local to this callsite and we are writing out a global // record here. // // Any field call that does *not* have an R entry must be // assumed to call anything. var { csu, field, callees } = callee; var fullFieldName = csu + "." + field; - if (!(fullFieldName in fieldCallSeen)) { - fieldCallSeen[fullFieldName] = true; + if (!virtualResolutionsSeen.has(fullFieldName)) { + virtualResolutionsSeen.add(fullFieldName); for (var target of callees) printOnce("R " + memo(fullFieldName) + " " + memo(target.name)); } @@ -284,8 +273,6 @@ function processBody(functionName, body) } } -var callgraph = {}; - var xdb = xdbLibrary(); xdb.open("src_comp.xdb"); @@ -327,9 +314,6 @@ function process(functionName, functionBodies) pbody.suppressed[id] = true; } - seenCallees = {}; - seenSuppressedCallees = {}; - for (var body of functionBodies) processBody(functionName, body); diff --git a/js/src/devtools/rootAnalysis/utility.js b/js/src/devtools/rootAnalysis/utility.js index 7303c7b81fd9..e116a07cd8e3 100644 --- a/js/src/devtools/rootAnalysis/utility.js +++ b/js/src/devtools/rootAnalysis/utility.js @@ -6,6 +6,15 @@ // constructors/destructors. var internalMarker = " *INTERNAL* "; +if (! Set.prototype.hasOwnProperty("update")) { + Object.defineProperty(Set.prototype, "update", { + value: function (collection) { + for (let elt of collection) + this.add(elt); + } + }); +} + function assert(x, msg) { if (x) From 1cde976b7c15ee25ca945a63e0eae853ac427561 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 15:25:46 -0700 Subject: [PATCH 114/199] Bug 1259850 - Start searching from the most specialized csu, r=terrence Previously, this mostly "worked" purely by chance -- it started gathering method definitions at the 'csu' variable, which JS helpfully hoisted up to the toplevel. It had the last value assigned to csu within the loop, which would have been the basest base class (don't think too hard about the case of multiple inheritance, it was wrong). Then we find all descendants, which was *too* much, but it ended up just making the analysis conservative. MozReview-Commit-ID: 2Ps8gJpztw2 --HG-- extra : rebase_source : c12e58594e7d21dc035b2e669c53808629d5beae extra : source : 196ac1f813f9a9489d24ba0c70c1dfb20b404489 --- .../devtools/rootAnalysis/computeCallgraph.js | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index 02ede9af9e29..c9fe0c9cd65b 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -47,6 +47,24 @@ function processCSU(csuName, csu) } } +// Return the nearest ancestor method definition, or all nearest definitions in +// the case of multiple inheritance. +function nearestAncestorMethods(csu, method) +{ + var key = csu + ":" + method; + + if (classFunctions.has(key)) + return new Set(classFunctions.get(key)); + + var functions = new Set(); + if (superclasses.has(csu)) { + for (var parent of superclasses.get(csu)) + functions.update(nearestAncestorMethods(parent, method)); + } + + return functions; +} + function findVirtualFunctions(initialCSU, field, suppressed) { var worklist = [initialCSU]; @@ -73,7 +91,15 @@ function findVirtualFunctions(initialCSU, field, suppressed) worklist.push(...superclasses.get(csu)); } - worklist = [csu]; + // Now return a list of all the instantiations of the method named 'field' + // that could execute on an instance of initialCSU or a descendant class. + + // Start with the class itself, or if it doesn't define the method, all + // nearest ancestor definitions. + functions.update(nearestAncestorMethods(initialCSU, field)); + + // Then recurse through all descendants to add in their definitions. + var worklist = [initialCSU]; while (worklist.length) { var csu = worklist.pop(); var key = csu + ":" + field; From 0392f5cdb27f41ee3c42dfe894760ca98fd93a42 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 15:25:56 -0700 Subject: [PATCH 115/199] Bug 1259850 - Refactor findVirtualFunctions and improve comments, r=terrence No more passing a 2nd return value back through a mutable 1-element array. MozReview-Commit-ID: IUcyrq93KXT --HG-- extra : rebase_source : 2f822b79ae81af55c0bdd372a8cf8e087bba2b7a extra : source : c6615c7b00838e1d212c6007b4a54b20d71f7ae0 --- .../devtools/rootAnalysis/computeCallgraph.js | 85 ++++++++++--------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index c9fe0c9cd65b..e72c67313c85 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -65,21 +65,29 @@ function nearestAncestorMethods(csu, method) return functions; } -function findVirtualFunctions(initialCSU, field, suppressed) +// Return [ instantations, suppressed ], where instantiations is a Set of all +// possible implementations of 'field' given static type 'initialCSU', plus +// null if arbitrary other implementations are possible, and suppressed is true +// if we the method is assumed to be non-GC'ing by annotation. +function findVirtualFunctions(initialCSU, field) { var worklist = [initialCSU]; var functions = new Set(); - // Virtual call targets on subclasses of nsISupports may be incomplete, - // if the interface is scriptable. Just treat all indirect calls on - // nsISupports objects as potentially GC'ing, except AddRef/Release - // which should never enter the JS engine (even when calling dtors). + // Loop through all methods of initialCSU (by looking at all methods of ancestor csus). + // + // If field is nsISupports::AddRef or ::Release, return an empty list and a + // boolean that says we assert that it cannot GC. + // + // If this is a method that is annotated to be dangerous (eg, it could be + // overridden with an implementation that could GC), then use null as a + // signal value that it should be considered to GC, even though we'll also + // collect all of the instantiations for other purposes. + while (worklist.length) { var csu = worklist.pop(); - if (isSuppressedVirtualMethod(csu, field)) { - suppressed[0] = true; - return new Set(); - } + if (isSuppressedVirtualMethod(csu, field)) + return [ new Set(), true ]; if (isOverridableField(initialCSU, csu, field)) { // We will still resolve the virtual function call, because it's // nice to have as complete a callgraph as possible for other uses. @@ -111,7 +119,7 @@ function findVirtualFunctions(initialCSU, field, suppressed) worklist.push(...subclasses.get(csu)); } - return functions; + return [ functions, false ]; } var memoized = new Map(); @@ -148,42 +156,42 @@ function getCallees(edge) var field = callee.Exp[0].Field; var fieldName = field.Name[0]; var csuName = field.FieldCSU.Type.Name; - var functions = null; + var functions; if ("FieldInstanceFunction" in field) { - var suppressed = [ false ]; - functions = findVirtualFunctions(csuName, fieldName, suppressed); - if (suppressed[0]) { + let suppressed; + [ functions, suppressed ] = findVirtualFunctions(csuName, fieldName, suppressed); + if (suppressed) { // Field call known to not GC; mark it as suppressed so // direct invocations will be ignored callees.push({'kind': "field", 'csu': csuName, 'field': fieldName, 'suppressed': true}); } - } - if (functions) { - // Known set of virtual call targets. Treat them as direct - // calls to all possible resolved types, but also record edges - // from this field call to each final callee. When the analysis - // is checking whether an edge can GC and it sees an unrooted - // pointer held live across this field call, it will know - // whether any of the direct callees can GC or not. - var targets = []; - var fullyResolved = true; - for (var name of functions) { - if (name === null) { - // virtual call on an nsISupports object - callees.push({'kind': "field", 'csu': csuName, 'field': fieldName}); - fullyResolved = false; - } else { - callees.push({'kind': "direct", 'name': name}); - targets.push({'kind': "direct", 'name': name}); - } - } - if (fullyResolved) - callees.push({'kind': "resolved-field", 'csu': csuName, 'field': fieldName, 'callees': targets}); } else { - // Unknown set of call targets. Non-virtual field call. - callees.push({'kind': "field", 'csu': csuName, 'field': fieldName}); + functions = new Set([null]); // field call } + + // Known set of virtual call targets. Treat them as direct calls to + // all possible resolved types, but also record edges from this + // field call to each final callee. When the analysis is checking + // whether an edge can GC and it sees an unrooted pointer held live + // across this field call, it will know whether any of the direct + // callees can GC or not. + var targets = []; + var fullyResolved = true; + for (var name of functions) { + if (name === null) { + // Unknown set of call targets, meaning either a function + // pointer call ("field call") or a virtual method that can + // be overridden in extensions. + callees.push({'kind': "field", 'csu': csuName, 'field': fieldName}); + fullyResolved = false; + } else { + callees.push({'kind': "direct", 'name': name}); + targets.push({'kind': "direct", 'name': name}); + } + } + if (fullyResolved) + callees.push({'kind': "resolved-field", 'csu': csuName, 'field': fieldName, 'callees': targets}); } else if (callee.Exp[0].Kind == "Var") { // indirect call through a variable. callees.push({'kind': "indirect", 'variable': callee.Exp[0].Variable.Name[0]}); @@ -228,7 +236,6 @@ function getTags(functionName, body) { var tags = new Set(); var annotations = getAnnotations(body); if (functionName in annotations) { - print("crawling through"); for (var [ annName, annValue ] of annotations[functionName]) { if (annName == 'Tag') tags.add(annValue); From 491767ff1e54f7454de3bc7fc76ba7c04a2b269f Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 11:07:04 -0700 Subject: [PATCH 116/199] Bug 1259850 - Hide command output behind --verbose flag, r=terrence MozReview-Commit-ID: ERQmFqLoyGw --HG-- extra : rebase_source : 05dbb3fa398122e2b224bc137ce644de78b70294 extra : source : 266befcb8acd57bc12a5949d6c873521d7ea6167 --- js/src/devtools/rootAnalysis/analyze.py | 34 +++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/js/src/devtools/rootAnalysis/analyze.py b/js/src/devtools/rootAnalysis/analyze.py index 3f2c34a44003..dca1461fc70d 100755 --- a/js/src/devtools/rootAnalysis/analyze.py +++ b/js/src/devtools/rootAnalysis/analyze.py @@ -77,7 +77,8 @@ def generate_hazards(config, outfilename): config) outfile = 'rootingHazards.%s' % (i+1,) output = open(outfile, 'w') - print_command(command, outfile=outfile, env=env(config)) + if config['verbose']: + print_command(command, outfile=outfile, env=env(config)) jobs.append((command, Popen(command, stdout=output, env=env(config)))) final_status = 0 @@ -91,7 +92,8 @@ def generate_hazards(config, outfilename): with open(outfilename, 'w') as output: command = ['cat'] + [ 'rootingHazards.%s' % (i+1,) for i in range(int(config['jobs'])) ] - print_command(command, outfile=outfilename) + if config['verbose']: + print_command(command, outfile=outfilename) subprocess.call(command, stdout=output) JOBS = { 'dbs': @@ -155,7 +157,8 @@ def run_job(name, config): if isinstance(outfiles, basestring): stdout_filename = '%s.tmp' % name temp_map[stdout_filename] = outfiles - print_command(cmdspec, outfile=outfiles, env=env(config)) + if config['verbose']: + print_command(cmdspec, outfile=outfiles, env=env(config)) else: stdout_filename = None pc = list(cmdspec) @@ -163,7 +166,8 @@ def run_job(name, config): for (i, name) in out_indexes(cmdspec): pc[i] = outfiles[outfile] outfile += 1 - print_command(pc, env=env(config)) + if config['verbose']: + print_command(pc, env=env(config)) command = list(cmdspec) outfile = 0 @@ -190,15 +194,6 @@ config = { 'ANALYSIS_SCRIPTDIR': os.path.dirname(__file__) } defaults = [ '%s/defaults.py' % config['ANALYSIS_SCRIPTDIR'], '%s/defaults.py' % os.getcwd() ] -for default in defaults: - try: - execfile(default, config) - print("Loaded %s" % default) - except: - pass - -data = config.copy() - parser = argparse.ArgumentParser(description='Statically analyze build tree for rooting hazards.') parser.add_argument('step', metavar='STEP', type=str, nargs='?', help='run starting from this step') @@ -220,8 +215,21 @@ parser.add_argument('--tag', '-t', type=str, nargs='?', help='name of job, also sets build command to "build."') parser.add_argument('--expect-file', type=str, nargs='?', help='deprecated option, temporarily still present for backwards compatibility') +parser.add_argument('--verbose', '-v', action='store_true', + help='Display cut & paste commands to run individual steps') args = parser.parse_args() + +for default in defaults: + try: + execfile(default, config) + if args.verbose: + print("Loaded %s" % default) + except: + pass + +data = config.copy() + for k,v in vars(args).items(): if v is not None: data[k] = v From 75c752be1ec810454ba1c608dd00e5ae6b323c08 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 10:56:32 -0700 Subject: [PATCH 117/199] Bug 1259850 - Fix chunking implementation, r=terrence MozReview-Commit-ID: F58OBZ4tCXw --HG-- extra : rebase_source : c47879121161e5016d35f8cb925d44247604d81c extra : source : d00b9c8a7984a8e117eb191280b08b1aed879710 --- js/src/devtools/rootAnalysis/analyzeRoots.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index ff03912fabaf..a95a208fb73e 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -753,9 +753,9 @@ var minStream = xdb.min_data_stream()|0; var maxStream = xdb.max_data_stream()|0; var N = (maxStream - minStream) + 1; -var each = Math.floor(N/numBatches); -var start = minStream + each * (batch - 1); -var end = Math.min(minStream + each * batch - 1, maxStream); +var start = Math.floor((batch - 1) / numBatches * N) + minStream; +var start_next = Math.floor(batch / numBatches * N) + minStream; +var end = start_next - 1; function process(name, json) { functionName = name; From f1abc86304edc10bc370c8980d52853aa44d7a58 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 19 May 2016 12:53:29 -0700 Subject: [PATCH 118/199] Bug 1259850 - In-source annotations for GC suppression, r=terrence MozReview-Commit-ID: HaSt3RVV6CM --HG-- extra : rebase_source : f607cee411ac438e9f17095cd7b5732ccf325443 extra : source : 129559d4ac621b3801e41ce10db1cb4b1a6786da --- js/public/GCAPI.h | 2 +- js/public/GCAnnotations.h | 3 ++ js/src/devtools/rootAnalysis/analyze.py | 10 +++-- js/src/devtools/rootAnalysis/analyzeRoots.js | 19 ++++---- js/src/devtools/rootAnalysis/annotations.js | 13 +++--- .../devtools/rootAnalysis/computeCallgraph.js | 4 ++ .../devtools/rootAnalysis/computeGCTypes.js | 43 +++++++++++++++++++ js/src/devtools/rootAnalysis/utility.js | 18 ++++++++ .../jsapi-tests/testGCStoreBufferRemoval.cpp | 2 +- js/src/jsgc.h | 2 +- 10 files changed, 94 insertions(+), 22 deletions(-) diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 150dce3ac0cc..c95ab18b1ac0 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -551,7 +551,7 @@ class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertNoAlloc public: AutoSuppressGCAnalysis() : AutoAssertNoAlloc() {} explicit AutoSuppressGCAnalysis(JSRuntime* rt) : AutoAssertNoAlloc(rt) {} -}; +} JS_HAZ_GC_SUPPRESSED; /** * Assert that code is only ever called from a GC callback, disable the static diff --git a/js/public/GCAnnotations.h b/js/public/GCAnnotations.h index e73caf8d4da6..28813a4db991 100644 --- a/js/public/GCAnnotations.h +++ b/js/public/GCAnnotations.h @@ -39,6 +39,8 @@ // invalidating GC pointers. # define JS_HAZ_GC_CALL __attribute__((tag("GC Call"))) +# define JS_HAZ_GC_SUPPRESSED __attribute__((tag("Suppress GC"))) + #else # define JS_HAZ_GC_THING @@ -47,6 +49,7 @@ # define JS_HAZ_GC_INVALIDATED # define JS_HAZ_NON_GC_POINTER # define JS_HAZ_GC_CALL +# define JS_HAZ_GC_SUPPRESSED #endif diff --git a/js/src/devtools/rootAnalysis/analyze.py b/js/src/devtools/rootAnalysis/analyze.py index dca1461fc70d..5d41828ca02c 100755 --- a/js/src/devtools/rootAnalysis/analyze.py +++ b/js/src/devtools/rootAnalysis/analyze.py @@ -72,6 +72,7 @@ def generate_hazards(config, outfilename): '%(gcEdges)s', '%(suppressedFunctions_list)s', '%(gcTypes)s', + '%(typeInfo)s', str(i+1), '%(jobs)s', 'tmp.%s' % (i+1,)), config) @@ -113,7 +114,7 @@ JOBS = { 'dbs': ()), 'callgraph': - (('%(js)s', '%(analysis_scriptdir)s/computeCallgraph.js'), + (('%(js)s', '%(analysis_scriptdir)s/computeCallgraph.js', '%(typeInfo)s'), 'callgraph.txt'), 'gcFunctions': @@ -122,8 +123,9 @@ JOBS = { 'dbs': ('gcFunctions.txt', 'gcFunctions.lst', 'gcEdges.txt', 'suppressedFunctions.lst')), 'gcTypes': - (('%(js)s', '%(analysis_scriptdir)s/computeGCTypes.js',), - 'gcTypes.txt'), + (('%(js)s', '%(analysis_scriptdir)s/computeGCTypes.js', + '[gcTypes]', '[typeInfo]'), + ('gcTypes.txt', 'typeInfo.txt')), 'allFunctions': (('%(sixgill_bin)s/xdbkeys', 'src_body.xdb',), @@ -259,8 +261,8 @@ if not data.get('source') and data.get('sixgill_bin'): data['source'] = path.replace("/js/src/jsapi.cpp", "") steps = [ 'dbs', - 'callgraph', 'gcTypes', + 'callgraph', 'gcFunctions', 'allFunctions', 'hazards', diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index a95a208fb73e..a10f63d0cadd 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -12,7 +12,7 @@ var functionName; var functionBodies; if (typeof scriptArgs[0] != 'string' || typeof scriptArgs[1] != 'string') - throw "Usage: analyzeRoots.js [-f function_name] [start end [tmpfile]]"; + throw "Usage: analyzeRoots.js [-f function_name] [start end [tmpfile]]"; var theFunctionNameToFind; if (scriptArgs[0] == '--function') { @@ -20,13 +20,16 @@ if (scriptArgs[0] == '--function') { scriptArgs = scriptArgs.slice(2); } -var gcFunctionsFile = scriptArgs[0]; -var gcEdgesFile = scriptArgs[1]; -var suppressedFunctionsFile = scriptArgs[2]; -var gcTypesFile = scriptArgs[3]; -var batch = (scriptArgs[4]|0) || 1; -var numBatches = (scriptArgs[5]|0) || 1; -var tmpfile = scriptArgs[6] || "tmp.txt"; +var gcFunctionsFile = scriptArgs[0] || "gcFunctions.lst"; +var gcEdgesFile = scriptArgs[1] || "gcEdges.txt"; +var suppressedFunctionsFile = scriptArgs[2] || "suppressedFunctions.lst"; +var gcTypesFile = scriptArgs[3] || "gcTypes.txt"; +var typeInfoFile = scriptArgs[4] || "typeInfo.txt"; +var batch = (scriptArgs[5]|0) || 1; +var numBatches = (scriptArgs[6]|0) || 1; +var tmpfile = scriptArgs[7] || "tmp.txt"; + +GCSuppressionTypes = loadTypeInfo(typeInfoFile)["Suppress GC"]; var gcFunctions = {}; var text = snarf("gcFunctions.lst").split("\n"); diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index fd4dac78e09e..cb50fc61b8dd 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -2,6 +2,10 @@ "use strict"; +// RAII types within which we should assume GC is suppressed, eg +// AutoSuppressGC. +var GCSuppressionTypes = []; + // Ignore calls made through these function pointers var ignoreIndirectCalls = { "mallocSizeOf" : true, @@ -186,6 +190,7 @@ var ignoreFunctions = { // up wrapping a pending exception. See bug 898815 for the heavyweight fix. "void js::AutoCompartment::~AutoCompartment(int32)" : true, "void JSAutoCompartment::~JSAutoCompartment(int32)" : true, + "void js::AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM()" : true, // Bug 948646 - the only thing AutoJSContext's constructor calls // is an Init() routine whose entire body is covered with an @@ -322,13 +327,7 @@ function isUnsafeStorage(typeName) function isSuppressConstructor(varName) { // varName[1] contains the unqualified name - return [ - "AutoSuppressGC", - "AutoAssertGCCallback", - "AutoEnterAnalysis", - "AutoSuppressGCAnalysis", - "AutoIgnoreRootingHazards" - ].indexOf(varName[1]) != -1; + return GCSuppressionTypes.indexOf(varName[1]) != -1; } // nsISupports subclasses' methods may be scriptable (or overridden diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index e72c67313c85..59c7944fc708 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -12,6 +12,8 @@ if (scriptArgs[0] == '--function') { scriptArgs = scriptArgs.slice(2); } +var typeInfo_filename = scriptArgs[0] || "typeInfo.txt"; + var subclasses = new Map(); // Map from csu => set of immediate subclasses var superclasses = new Map(); // Map from csu => set of immediate superclasses var classFunctions = new Map(); // Map from "csu:name" => set of full method name @@ -306,6 +308,8 @@ function processBody(functionName, body) } } +GCSuppressionTypes = loadTypeInfo(typeInfo_filename)["Suppress GC"]; + var xdb = xdbLibrary(); xdb.open("src_comp.xdb"); diff --git a/js/src/devtools/rootAnalysis/computeGCTypes.js b/js/src/devtools/rootAnalysis/computeGCTypes.js index f0712ecb47db..af4d703896e1 100644 --- a/js/src/devtools/rootAnalysis/computeGCTypes.js +++ b/js/src/devtools/rootAnalysis/computeGCTypes.js @@ -5,14 +5,20 @@ loadRelativeToScript('utility.js'); loadRelativeToScript('annotations.js'); +var gcTypes_filename = scriptArgs[0] || "gcTypes.txt"; +var typeInfo_filename = scriptArgs[1] || "typeInfo.txt"; + var annotations = { 'GCPointers': [], 'GCThings': [], 'NonGCTypes': {}, // unused 'NonGCPointers': {}, 'RootedPointers': {}, + 'GCSuppressors': {}, }; +var gDescriptors = new Map; // Map from descriptor string => Set of typeName + var structureParents = {}; // Map from field => list of var pointerParents = {}; // Map from field => list of var baseClasses = {}; // Map from struct name => list of base class name strings @@ -64,6 +70,8 @@ function processCSU(csu, body) annotations.NonGCPointers[csu] = true; else if (tag == 'Rooted Pointer') annotations.RootedPointers[csu] = true; + else if (tag == 'Suppress GC') + annotations.GCSuppressors[csu] = true; } } @@ -207,6 +215,26 @@ function addGCPointer(typeName) markGCType(typeName, '', '(annotation)', 1, 0, ""); } +// Add an arbitrary descriptor to a type, and apply it recursively to all base +// structs and structs that contain the given typeName as a field. +function addDescriptor(typeName, descriptor) +{ + if (!gDescriptors.has(descriptor)) + gDescriptors.set(descriptor, new Set); + let descriptorTypes = gDescriptors.get(descriptor); + if (!descriptorTypes.has(typeName)) { + descriptorTypes.add(typeName); + if (typeName in structureParents) { + for (let [holder, field] of structureParents[typeName]) + addDescriptor(holder, descriptor); + } + if (typeName in baseClasses) { + for (let base of baseClasses[typeName]) + addDescriptor(base, descriptor); + } + } +} + for (var type of listNonGCPointers()) annotations.NonGCPointers[type] = true; @@ -246,6 +274,8 @@ function explain(csu, indent, seen) { } } +var origOut = os.file.redirect(gcTypes_filename); + for (var csu in gcTypes) { print("GCThing: " + csu); explain(csu, " "); @@ -254,3 +284,16 @@ for (var csu in gcPointers) { print("GCPointer: " + csu); explain(csu, " "); } + +// Redirect output to the typeInfo file and close the gcTypes file. +os.file.close(os.file.redirect(typeInfo_filename)); + +for (let csu in annotations.GCSuppressors) + addDescriptor(csu, 'Suppress GC'); + +for (let [descriptor, types] of gDescriptors) { + for (let csu of types) + print(descriptor + "$$" + csu); +} + +os.file.close(os.file.redirect(origOut)); diff --git a/js/src/devtools/rootAnalysis/utility.js b/js/src/devtools/rootAnalysis/utility.js index e116a07cd8e3..06c18804f362 100644 --- a/js/src/devtools/rootAnalysis/utility.js +++ b/js/src/devtools/rootAnalysis/utility.js @@ -191,3 +191,21 @@ function* readFileLines_gen(filename) libc.fclose(fp); libc.free(ctypes.void_t.ptr(linebuf)); } + +function addToKeyedList(collection, key, entry) +{ + if (!(key in collection)) + collection[key] = []; + collection[key].push(entry); +} + +function loadTypeInfo(filename) +{ + var info = {}; + for (var line of readFileLines_gen(filename)) { + line = line.replace(/\n/, ""); + let [property, name] = line.split("$$"); + addToKeyedList(info, property, name); + } + return info; +} diff --git a/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp b/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp index c3bcb1d642c4..abf5bf00198f 100644 --- a/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp +++ b/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp @@ -16,7 +16,7 @@ struct AutoIgnoreRootingHazards { static volatile int depth; AutoIgnoreRootingHazards() { depth++; } ~AutoIgnoreRootingHazards() { depth--; } -}; +} JS_HAZ_GC_SUPPRESSED; volatile int AutoIgnoreRootingHazards::depth = 0; BEGIN_TEST(testGCStoreBufferRemoval) diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 409acd22dde6..9e2a8b5a7aca 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1269,7 +1269,7 @@ MaybeVerifyBarriers(JSContext* cx, bool always = false) * read the comment in vm/Runtime.h above |suppressGC| and take all appropriate * precautions before instantiating this class. */ -class MOZ_RAII AutoSuppressGC +class MOZ_RAII JS_HAZ_GC_SUPPRESSED AutoSuppressGC { int32_t& suppressGC_; From b6c538d64877376d3805697e9dbd1053d857ce59 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 11:41:54 -0700 Subject: [PATCH 119/199] Bug 1259850 - Various refactorings, r=terrence MozReview-Commit-ID: GYrqbzK3U8W --HG-- extra : rebase_source : a230b7106750b47356162f490fc1bee43df2f29c extra : source : 5762a8fba027bb667a621deb50540ddd5a884193 --- .hgignore | 1 + js/src/devtools/rootAnalysis/analyze.py | 4 ++-- js/src/devtools/rootAnalysis/explain.py | 19 +++++++++++-------- js/src/devtools/rootAnalysis/loadCallgraph.js | 9 ++------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.hgignore b/.hgignore index 895885254eab..7aa79886a2da 100644 --- a/.hgignore +++ b/.hgignore @@ -43,6 +43,7 @@ _OPT\.OBJ/ ^js/src/autom4te.cache$ # SpiderMonkey test result logs ^js/src/tests/results-.*\.(html|txt)$ +^js/src/devtools/rootAnalysis/t/out # Java HTML5 parser classes ^parser/html/java/(html|java)parser/ diff --git a/js/src/devtools/rootAnalysis/analyze.py b/js/src/devtools/rootAnalysis/analyze.py index 5d41828ca02c..69482dab7b71 100755 --- a/js/src/devtools/rootAnalysis/analyze.py +++ b/js/src/devtools/rootAnalysis/analyze.py @@ -242,7 +242,7 @@ if args.tag and not args.buildcommand: if args.jobs is not None: data['jobs'] = args.jobs if not data.get('jobs'): - data['jobs'] = subprocess.check_output(['nproc', '--ignore=1']) + data['jobs'] = subprocess.check_output(['nproc', '--ignore=1']).strip() if args.buildcommand: data['buildcommand'] = args.buildcommand @@ -286,7 +286,7 @@ for step in steps: for (i, name) in out_indexes(command): data[name] = outfiles[outfile] outfile += 1 - assert len(outfiles) == outfile, 'step \'%s\': mismatched number of output files and params' % step + assert len(outfiles) == outfile, 'step \'%s\': mismatched number of output files (%d) and params (%d)' % (step, outfile, len(outfiles)) if args.step: steps = steps[steps.index(args.step):] diff --git a/js/src/devtools/rootAnalysis/explain.py b/js/src/devtools/rootAnalysis/explain.py index ec7ef1949b1d..dc8b76f5c687 100755 --- a/js/src/devtools/rootAnalysis/explain.py +++ b/js/src/devtools/rootAnalysis/explain.py @@ -3,6 +3,8 @@ import re import argparse +from collections import defaultdict + parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument('rootingHazards', nargs='?', default='rootingHazards.txt') parser.add_argument('gcFunctions', nargs='?', default='gcFunctions.txt') @@ -22,7 +24,7 @@ try: # Map from a GC function name to the list of hazards resulting from # that GC function - hazardousGCFunctions = {} + hazardousGCFunctions = defaultdict(list) # List of tuples (gcFunction, index of hazard) used to maintain the # ordering of the hazards @@ -53,7 +55,7 @@ try: # Function names are surrounded by single quotes. Field calls # are unquoted. current_gcFunction = m.group(2) - hazardousGCFunctions.setdefault(current_gcFunction, []).append(line) + hazardousGCFunctions[current_gcFunction].append(line) hazardOrder.append((current_gcFunction, len(hazardousGCFunctions[current_gcFunction]) - 1)) num_hazards += 1 continue @@ -84,12 +86,13 @@ try: if current_func: gcExplanations[current_func] = explanation - for gcFunction, index in hazardOrder: - gcHazards = hazardousGCFunctions[gcFunction] - if gcFunction in gcExplanations: - print >>hazards, (gcHazards[index] + gcExplanations[gcFunction]) - else: - print >>hazards, gcHazards[index] + for gcFunction, index in hazardOrder: + gcHazards = hazardousGCFunctions[gcFunction] + + if gcFunction in gcExplanations: + print >>hazards, (gcHazards[index] + gcExplanations[gcFunction]) + else: + print >>hazards, gcHazards[index] except IOError as e: print 'Failed: %s' % str(e) diff --git a/js/src/devtools/rootAnalysis/loadCallgraph.js b/js/src/devtools/rootAnalysis/loadCallgraph.js index a221d7143764..7661aad8e53a 100644 --- a/js/src/devtools/rootAnalysis/loadCallgraph.js +++ b/js/src/devtools/rootAnalysis/loadCallgraph.js @@ -53,13 +53,8 @@ function addGCFunction(caller, reason) function addCallEdge(caller, callee, suppressed) { - if (!(caller in calleeGraph)) - calleeGraph[caller] = []; - calleeGraph[caller].push({callee:callee, suppressed:suppressed}); - - if (!(callee in callerGraph)) - callerGraph[callee] = []; - callerGraph[callee].push({caller:caller, suppressed:suppressed}); + addToKeyedList(calleeGraph, caller, {callee:callee, suppressed:suppressed}); + addToKeyedList(callerGraph, callee, {caller:caller, suppressed:suppressed}); } // Map from identifier to full "mangled|readable" name. Or sometimes to a From 4aceeb4bb11bf183a8b1933862003bdaca97f412 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 11:44:21 -0700 Subject: [PATCH 120/199] Bug 1259850 - Handle D4 destructors, r=terrence MozReview-Commit-ID: 1hArAcAxvZV --HG-- extra : rebase_source : d48fc7ea0405792f9bdd65feb40db788cf2f9f9a extra : source : 457cb29cad5556b8289deb94c67590d9c2b23c9f --- .../devtools/rootAnalysis/computeCallgraph.js | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index 59c7944fc708..ade5f26f0600 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -372,7 +372,7 @@ function process(functionName, functionBodies) // Bug 1056410: Oh joy. GCC does something even funkier internally, // where it generates calls to ~Foo() but a body for ~Foo(int32) even // though it uses the same mangled name for both. So we need to add a - // synthetic edge from the former to the latter. + // synthetic edge from ~Foo() -> ~Foo(int32). // // inChargeXTor will have the (int32). if (functionName.indexOf("::~") > 0) { @@ -390,7 +390,7 @@ function process(functionName, functionBodies) // D1 # complete object destructor // D2 # base object destructor // - // In actual practice, I have observed a C4 constructor generated by gcc + // In actual practice, I have observed C4 and D4 xtors generated by gcc // 4.9.3 (but not 4.7.3). The gcc source code says: // // /* This is the old-style "[unified]" constructor. @@ -398,23 +398,29 @@ function process(functionName, functionBodies) // it from the clones in order to share code and save space. */ // // Unfortunately, that "call... from the clones" does not seem to appear in - // the CFG we get from GCC. So if we see a C4 constructor, inject an edge - // to it from C1, C2, and C3. (Note that C3 isn't even used in current GCC, - // but add the edge anyway just in case.) - if (functionName.indexOf("C4E") != -1) { + // the CFG we get from GCC. So if we see a C4 constructor or D4 destructor, + // inject an edge to it from C1, C2, and C3 (or D1, D2, and D3). (Note that + // C3 isn't even used in current GCC, but add the edge anyway just in + // case.) + if (functionName.indexOf("C4E") != -1 || functionName.indexOf("D4Ev") != -1) { var [ mangled, unmangled ] = splitFunction(functionName); // E terminates the method name (and precedes the method parameters). - if (mangled.indexOf("C4E") != -1) { - // If "C4E" shows up in the mangled name for another reason, this - // will create bogus edges in the callgraph. But that shouldn't - // matter too much, and is somewhat difficult to avoid, so we will - // live with it. - var C1 = mangled.replace("C4E", "C1E"); - var C2 = mangled.replace("C4E", "C2E"); - var C3 = mangled.replace("C4E", "C3E"); - print("D " + memo(C1) + " " + memo(mangled)); - print("D " + memo(C2) + " " + memo(mangled)); - print("D " + memo(C3) + " " + memo(mangled)); + // If eg "C4E" shows up in the mangled name for another reason, this + // will create bogus edges in the callgraph. But will affect little and + // is somewhat difficult to avoid, so we will live with it. + for (let [synthetic, variant] of [['C4E', 'C1E'], + ['C4E', 'C2E'], + ['C4E', 'C3E'], + ['D4Ev', 'D1Ev'], + ['D4Ev', 'D2Ev'], + ['D4Ev', 'D3Ev']]) + { + if (mangled.indexOf(synthetic) == -1) + continue; + + let variant_mangled = mangled.replace(synthetic, variant); + let variant_full = variant_mangled + "$" + unmangled; + print("D " + memo(variant_full) + " " + memo(functionName)); } } } From a252c6a3a5aeca4069a0b73f5b5c478317574ed8 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 11:45:05 -0700 Subject: [PATCH 121/199] Bug 1259850 - Provide GC stacks for synthesized functions, r=terrence MozReview-Commit-ID: 6WYqoXN3k9J --HG-- extra : rebase_source : 9548ec562e0a068fec644cd181254977c29df6f6 extra : source : 4c7373c6c29efffa4e7f1f87acbf755d9fae8f0d --- .../rootAnalysis/computeGCFunctions.js | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/js/src/devtools/rootAnalysis/computeGCFunctions.js b/js/src/devtools/rootAnalysis/computeGCFunctions.js index 086cfef35886..97efcb38af22 100644 --- a/js/src/devtools/rootAnalysis/computeGCFunctions.js +++ b/js/src/devtools/rootAnalysis/computeGCFunctions.js @@ -21,16 +21,20 @@ loadCallgraph(callgraph_filename); printErr("Writing " + gcFunctions_filename); redirect(gcFunctions_filename); + for (var name in gcFunctions) { - print(""); - print("GC Function: " + name + "$" + readableNames[name][0]); - do { - name = gcFunctions[name]; - if (name in readableNames) - print(" " + readableNames[name][0]); - else - print(" " + name); - } while (name in gcFunctions); + for (let readable of readableNames[name]) { + print(""); + print("GC Function: " + name + "$" + readable); + let current = name; + do { + current = gcFunctions[current]; + if (current in readableNames) + print(" " + readableNames[current][0]); + else + print(" " + current); + } while (current in gcFunctions); + } } printErr("Writing " + gcFunctionsList_filename); From e409f193e1d0a080008ebc806b32d704edeb9e05 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 25 Mar 2016 14:40:23 -0700 Subject: [PATCH 122/199] Bug 1259850 - Rewrite the test suite, add several tests, r=terrence MozReview-Commit-ID: HtJ0uA3IfXZ --HG-- rename : js/src/devtools/rootAnalysis/run-test.py => js/src/devtools/rootAnalysis/t/sixgill.py extra : rebase_source : 3002048a93db2337ac6b7f2c5c0329c5bc603664 extra : source : aa434447a11bd8b62b954b24005729a1bd757679 --- js/src/devtools/rootAnalysis/run-test.py | 196 ++++++------------ .../rootAnalysis/t/hazards/source.cpp | 89 ++++++++ .../devtools/rootAnalysis/t/hazards/test.py | 36 ++++ .../rootAnalysis/t/sixgill-tree/source.cpp | 70 +++++++ .../rootAnalysis/t/sixgill-tree/test.py | 60 ++++++ js/src/devtools/rootAnalysis/t/sixgill.py | 63 ++++++ .../rootAnalysis/t/suppression/source.cpp | 64 ++++++ .../rootAnalysis/t/suppression/test.py | 23 ++ js/src/devtools/rootAnalysis/t/testlib.py | 119 +++++++++++ 9 files changed, 589 insertions(+), 131 deletions(-) create mode 100644 js/src/devtools/rootAnalysis/t/hazards/source.cpp create mode 100644 js/src/devtools/rootAnalysis/t/hazards/test.py create mode 100644 js/src/devtools/rootAnalysis/t/sixgill-tree/source.cpp create mode 100644 js/src/devtools/rootAnalysis/t/sixgill-tree/test.py create mode 100644 js/src/devtools/rootAnalysis/t/sixgill.py create mode 100644 js/src/devtools/rootAnalysis/t/suppression/source.cpp create mode 100644 js/src/devtools/rootAnalysis/t/suppression/test.py create mode 100644 js/src/devtools/rootAnalysis/t/testlib.py diff --git a/js/src/devtools/rootAnalysis/run-test.py b/js/src/devtools/rootAnalysis/run-test.py index a960b57d1399..0d205b0d421a 100644 --- a/js/src/devtools/rootAnalysis/run-test.py +++ b/js/src/devtools/rootAnalysis/run-test.py @@ -3,150 +3,84 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -import sys import os -import re -import json +import site import subprocess +import argparse -testdir = os.path.abspath(os.path.dirname(__file__)) +testdir = os.path.abspath(os.path.join(os.path.dirname(__file__), 't')) +site.addsitedir(testdir) +from testlib import Test, equal -cfg = {} -cfg['SIXGILL_ROOT'] = os.environ.get('SIXGILL', - os.path.join(testdir, "sixgill")) -cfg['SIXGILL_BIN'] = os.environ.get('SIXGILL_BIN', - os.path.join(cfg['SIXGILL_ROOT'], "usr", "bin")) -cfg['SIXGILL_PLUGIN'] = os.environ.get('SIXGILL_PLUGIN', - os.path.join(cfg['SIXGILL_ROOT'], "usr", "libexec", "sixgill", "gcc", "xgill.so")) -cfg['CC'] = os.environ.get("CC", - "gcc") -cfg['CXX'] = os.environ.get("CXX", - cfg.get('CC', 'g++')) -cfg['JS_BIN'] = os.environ["JS"] +scriptdir = os.path.abspath(os.path.dirname(__file__)) + +parser = argparse.ArgumentParser(description='run hazard analysis tests') +parser.add_argument( + '--js', default=os.environ.get('JS'), + help='JS binary to run the tests with') +parser.add_argument( + '--sixgill', default=os.environ.get('SIXGILL', os.path.join(testdir, "sixgill")), + help='Path to root of sixgill installation') +parser.add_argument( + '--sixgill-bin', default=os.environ.get('SIXGILL_BIN'), + help='Path to sixgill binary dir') +parser.add_argument( + '--sixgill-plugin', default=os.environ.get('SIXGILL_PLUGIN'), + help='Full path to sixgill gcc plugin') +parser.add_argument( + '--gccdir', default=os.environ.get('GCCDIR'), + help='Path to GCC installation dir') +parser.add_argument( + '--cc', default=os.environ.get('CC'), + help='Path to gcc') +parser.add_argument( + '--cxx', default=os.environ.get('CXX'), + help='Path to g++') +parser.add_argument( + '--verbose', '-v', action='store_true', + help='Display verbose output, including commands executed') + +cfg = parser.parse_args() + +if not cfg.js: + exit('Must specify JS binary through environment variable or --js option') +if not cfg.cc: + if cfg.gccdir: + cfg.cc = os.path.join(cfg.gccdir, "bin", "gcc") + else: + cfg.cc = "gcc" +if not cfg.cxx: + if cfg.gccdir: + cfg.cxx = os.path.join(cfg.gccdir, "bin", "g++") + else: + cfg.cxx = "g++" +if not cfg.sixgill_bin: + cfg.sixgill_bin = os.path.join(cfg.sixgill, "usr", "bin") +if not cfg.sixgill_plugin: + cfg.sixgill_plugin = os.path.join(cfg.sixgill, "usr", "libexec", "sixgill", "gcc", "xgill.so") + +subprocess.check_call([cfg.js, '-e', 'if (!getBuildConfiguration()["has-ctypes"]) quit(1)']) def binpath(prog): - return os.path.join(cfg['SIXGILL_BIN'], prog) + return os.path.join(cfg.sixgill_bin, prog) -if not os.path.exists("test-output"): - os.mkdir("test-output") +try: + os.mkdir(os.path.join('t', 'out')) +except OSError: + pass -# Simplified version of the body info. -class Body(dict): - def __init__(self, body): - self['BlockIdKind'] = body['BlockId']['Kind'] - if 'Variable' in body['BlockId']: - self['BlockName'] = body['BlockId']['Variable']['Name'][0] - self['LineRange'] = [ body['Location'][0]['Line'], body['Location'][1]['Line'] ] - self['Filename'] = body['Location'][0]['CacheString'] - self['Edges'] = body.get('PEdge', []) - self['Points'] = { i+1: body['PPoint'][i]['Location']['Line'] for i in range(len(body['PPoint'])) } - self['Index'] = body['Index'] - self['Variables'] = { x['Variable']['Name'][0]: x['Type'] for x in body['DefineVariable'] } - - # Indexes - self['Line2Points'] = {} - for point, line in self['Points'].items(): - self['Line2Points'].setdefault(line, []).append(point) - self['SrcPoint2Edges'] = {} - for edge in self['Edges']: - (src, dst) = edge['Index'] - self['SrcPoint2Edges'].setdefault(src, []).append(edge) - self['Line2Edges'] = {} - for (src, edges) in self['SrcPoint2Edges'].items(): - line = self['Points'][src] - self['Line2Edges'].setdefault(line, []).extend(edges) - - def edges_from_line(self, line): - return self['Line2Edges'][line] - - def edge_from_line(self, line): - edges = self.edges_from_line(line) - assert(len(edges) == 1) - return edges[0] - - def edges_from_point(self, point): - return self['SrcPoint2Edges'][point] - - def edge_from_point(self, point): - edges = self.edges_from_point(point) - assert(len(edges) == 1) - return edges[0] - - def assignment_point(self, varname): - for edge in self['Edges']: - if edge['Kind'] != 'Assign': - continue - dst = edge['Exp'][0] - if dst['Kind'] != 'Var': - continue - if dst['Variable']['Name'][0] == varname: - return edge['Index'][0] - raise Exception("assignment to variable %s not found" % varname) - - def assignment_line(self, varname): - return self['Points'][self.assignment_point(varname)] - -tests = ['test'] +tests = ['sixgill-tree', 'suppression', 'hazards'] for name in tests: indir = os.path.join(testdir, name) - outdir = os.path.join(testdir, "test-output", name) - if not os.path.exists(outdir): + outdir = os.path.join(testdir, 'out', name) + try: os.mkdir(outdir) + except OSError: + pass - def compile(source): - cmd = "{CXX} -c {source} -fplugin={sixgill}".format(source=os.path.join(indir, source), - CXX=cfg['CXX'], sixgill=cfg['SIXGILL_PLUGIN']) - print("Running %s" % cmd) - subprocess.check_call(["sh", "-c", cmd]) - - def load_db_entry(dbname, pattern): - if not isinstance(pattern, basestring): - output = subprocess.check_output([binpath("xdbkeys"), dbname + ".xdb"]) - entries = output.splitlines() - matches = [f for f in entries if re.search(pattern, f)] - if len(matches) == 0: - raise Exception("entry not found") - if len(matches) > 1: - raise Exception("multiple entries found") - pattern = matches[0] - - output = subprocess.check_output([binpath("xdbfind"), "-json", dbname + ".xdb", pattern]) - return json.loads(output) - - def computeGCTypes(): - file("defaults.py", "w").write('''\ -analysis_scriptdir = '{testdir}' -sixgill_bin = '{bindir}' -'''.format(testdir=testdir, bindir=cfg['SIXGILL_BIN'])) - cmd = [ - os.path.join(testdir, "analyze.py"), - "gcTypes", "--upto", "gcTypes", - "--source=%s" % indir, - "--objdir=%s" % outdir, - "--js=%s" % cfg['JS_BIN'], - ] - print("Running " + " ".join(cmd)) - output = subprocess.check_call(cmd) - - def loadGCTypes(): - gctypes = {'GCThings': [], 'GCPointers': []} - for line in file(os.path.join(outdir, "gcTypes.txt")): - m = re.match(r'^(GC\w+): (.*)', line) - if m: - gctypes[m.group(1) + 's'].append(m.group(2)) - return gctypes - - def process_body(body): - return Body(body) - - def process_bodies(bodies): - return [ process_body(b) for b in bodies ] - - def equal(got, expected): - if got != expected: - print("Got '%s', expected '%s'" % (got, expected)) + test = Test(indir, outdir, cfg) os.chdir(outdir) subprocess.call(["sh", "-c", "rm *.xdb"]) - execfile(os.path.join(indir, "test.py")) + execfile(os.path.join(indir, "test.py"), {'test': test, 'equal': equal}) print("TEST-PASSED: %s" % name) diff --git a/js/src/devtools/rootAnalysis/t/hazards/source.cpp b/js/src/devtools/rootAnalysis/t/hazards/source.cpp new file mode 100644 index 000000000000..1e5191bc9445 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/hazards/source.cpp @@ -0,0 +1,89 @@ +#define ANNOTATE(property) __attribute__((tag(property))) + +struct Cell { int f; } ANNOTATE("GC Thing"); + +class AutoSuppressGC_Base { + public: + AutoSuppressGC_Base() {} + ~AutoSuppressGC_Base() {} +} ANNOTATE("Suppress GC"); + +class AutoSuppressGC_Child : public AutoSuppressGC_Base { + public: + AutoSuppressGC_Child() : AutoSuppressGC_Base() {} +}; + +class AutoSuppressGC { + AutoSuppressGC_Child helpImBeingSuppressed; + + public: + AutoSuppressGC() {} +}; + +extern void GC() ANNOTATE("GC Call"); +extern void invisible(); + +void GC() +{ + // If the implementation is too trivial, the function body won't be emitted at all. + asm(""); + invisible(); +} + +extern void foo(Cell*); + +void suppressedFunction() { + GC(); // Calls GC, but is always called within AutoSuppressGC +} + +void halfSuppressedFunction() { + GC(); // Calls GC, but is sometimes called within AutoSuppressGC +} + +void unsuppressedFunction() { + GC(); // Calls GC, never within AutoSuppressGC +} + +volatile static int x = 3; +volatile static int* xp = &x; +struct GCInDestructor { + ~GCInDestructor() { + invisible(); + asm(""); + *xp = 4; + GC(); + } +}; + +Cell* +f() +{ + GCInDestructor kaboom; + + Cell cell; + Cell* cell1 = &cell; + Cell* cell2 = &cell; + Cell* cell3 = &cell; + Cell* cell4 = &cell; + { + AutoSuppressGC nogc; + suppressedFunction(); + halfSuppressedFunction(); + } + foo(cell1); + halfSuppressedFunction(); + foo(cell2); + unsuppressedFunction(); + { + // Old bug: it would look from the first AutoSuppressGC constructor it + // found to the last destructor. This statement *should* have no effect. + AutoSuppressGC nogc; + } + foo(cell3); + Cell* cell5 = &cell; + foo(cell5); + + // Hazard in return value due to ~GCInDestructor + Cell* cell6 = &cell; + return cell6; +} diff --git a/js/src/devtools/rootAnalysis/t/hazards/test.py b/js/src/devtools/rootAnalysis/t/hazards/test.py new file mode 100644 index 000000000000..5e234d18061d --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/hazards/test.py @@ -0,0 +1,36 @@ +test.compile("source.cpp") +test.run_analysis_script('gcTypes') + +# gcFunctions should be the inverse, but we get to rely on unmangled names here. +gcFunctions = test.load_gcFunctions() +print(gcFunctions) +assert('void GC()' in gcFunctions) +assert('void suppressedFunction()' not in gcFunctions) +assert('void halfSuppressedFunction()' in gcFunctions) +assert('void unsuppressedFunction()' in gcFunctions) +assert('Cell* f()' in gcFunctions) + +hazards = test.load_hazards() +hazmap = {haz.variable: haz for haz in hazards} +assert('cell1' not in hazmap) +assert('cell2' in hazmap) +assert('cell3' in hazmap) +assert('cell4' not in hazmap) +assert('cell5' not in hazmap) +assert('cell6' not in hazmap) +assert('' in hazmap) + +# All hazards should be in f() +assert(hazmap['cell2'].function == 'Cell* f()') +assert(len(set(haz.function for haz in hazards)) == 1) + +# Check that the correct GC call is reported for each hazard. (cell3 has a +# hazard from two different GC calls; it doesn't really matter which is +# reported.) +assert(hazmap['cell2'].GCFunction == 'void halfSuppressedFunction()') +assert(hazmap['cell3'].GCFunction in ('void halfSuppressedFunction()', 'void unsuppressedFunction()')) +assert(hazmap[''].GCFunction == 'void GCInDestructor::~GCInDestructor()') + +# Type names are handy to have in the report. +assert(hazmap['cell2'].type == 'Cell*') +assert(hazmap[''].type == 'Cell*') diff --git a/js/src/devtools/rootAnalysis/t/sixgill-tree/source.cpp b/js/src/devtools/rootAnalysis/t/sixgill-tree/source.cpp new file mode 100644 index 000000000000..2de9ef4bb90b --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/sixgill-tree/source.cpp @@ -0,0 +1,70 @@ +#define ANNOTATE(property) __attribute__((tag(property))) + +namespace js { +namespace gc { +struct Cell { int f; } ANNOTATE("GC Thing"); +} +} + +struct Bogon { +}; + +struct JustACell : public js::gc::Cell { + bool iHaveNoDataMembers() { return true; } +}; + +struct JSObject : public js::gc::Cell, public Bogon { + int g; +}; + +struct SpecialObject : public JSObject { + int z; +}; + +struct ErrorResult { + bool hasObj; + JSObject *obj; + void trace() {} +} ANNOTATE("Suppressed GC Pointer"); + +struct OkContainer { + ErrorResult res; + bool happy; +}; + +struct UnrootedPointer { + JSObject *obj; +}; + +template +class Rooted { + T data; +} ANNOTATE("Rooted Pointer"); + +extern void js_GC() ANNOTATE("GC Call") ANNOTATE("Slow"); + +void js_GC() {} + +void root_arg(JSObject *obj, JSObject *random) +{ + // Use all these types so they get included in the output. + SpecialObject so; + UnrootedPointer up; + Bogon b; + OkContainer okc; + Rooted ro; + Rooted rso; + + obj = random; + + JSObject *other1 = obj; + js_GC(); + + float MARKER1 = 0; + JSObject *other2 = obj; + other1->f = 1; + other2->f = -1; + + unsigned int u1 = 1; + unsigned int u2 = -1; +} diff --git a/js/src/devtools/rootAnalysis/t/sixgill-tree/test.py b/js/src/devtools/rootAnalysis/t/sixgill-tree/test.py new file mode 100644 index 000000000000..c0c0263cd024 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/sixgill-tree/test.py @@ -0,0 +1,60 @@ +import re + +test.compile("source.cpp") +test.computeGCTypes() +body = test.process_body(test.load_db_entry("src_body", re.compile(r'root_arg'))[0]) + +# Rendering positive and negative integers +marker1 = body.assignment_line('MARKER1') +equal(body.edge_from_line(marker1 + 2)['Exp'][1]['String'], '1') +equal(body.edge_from_line(marker1 + 3)['Exp'][1]['String'], '-1') + +equal(body.edge_from_point(body.assignment_point('u1'))['Exp'][1]['String'], '1') +equal(body.edge_from_point(body.assignment_point('u2'))['Exp'][1]['String'], '4294967295') + +assert('obj' in body['Variables']) +assert('random' in body['Variables']) +assert('other1' in body['Variables']) +assert('other2' in body['Variables']) + +# Test function annotations +js_GC = test.process_body(test.load_db_entry("src_body", re.compile(r'js_GC'))[0]) +annotations = js_GC['Variables']['void js_GC()']['Annotation'] +assert(annotations) +found_call_tag = False +for annotation in annotations: + (annType, value) = annotation['Name'] + if annType == 'Tag' and value == 'GC Call': + found_call_tag = True +assert(found_call_tag) + +# Test type annotations + +# js::gc::Cell first +cell = test.load_db_entry("src_comp", 'js::gc::Cell')[0] +assert(cell['Kind'] == 'Struct') +annotations = cell['Annotation'] +assert(len(annotations) == 1) +(tag, value) = annotations[0]['Name'] +assert(tag == 'Tag') +assert(value == 'GC Thing') + +# Check JSObject inheritance. +JSObject = test.load_db_entry("src_comp", 'JSObject')[0] +bases = [ b['Base'] for b in JSObject['CSUBaseClass'] ] +assert('js::gc::Cell' in bases) +assert('Bogon' in bases) +assert(len(bases) == 2) + +# Check type analysis +gctypes = test.load_gcTypes() +assert('js::gc::Cell' in gctypes['GCThings']) +assert('JustACell' in gctypes['GCThings']) +assert('JSObject' in gctypes['GCThings']) +assert('SpecialObject' in gctypes['GCThings']) +assert('UnrootedPointer' in gctypes['GCPointers']) +assert('Bogon' not in gctypes['GCThings']) +assert('Bogon' not in gctypes['GCPointers']) +assert('ErrorResult' not in gctypes['GCPointers']) +assert('OkContainer' not in gctypes['GCPointers']) +assert('class Rooted' not in gctypes['GCPointers']) diff --git a/js/src/devtools/rootAnalysis/t/sixgill.py b/js/src/devtools/rootAnalysis/t/sixgill.py new file mode 100644 index 000000000000..2bdf76a49b44 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/sixgill.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from collections import defaultdict + +# Simplified version of the body info. +class Body(dict): + def __init__(self, body): + self['BlockIdKind'] = body['BlockId']['Kind'] + if 'Variable' in body['BlockId']: + self['BlockName'] = body['BlockId']['Variable']['Name'][0].split("$")[-1] + loc = body['Location'] + self['LineRange'] = (loc[0]['Line'], loc[1]['Line']) + self['Filename'] = loc[0]['CacheString'] + self['Edges'] = body.get('PEdge', []) + self['Points'] = { i: p['Location']['Line'] for i, p in enumerate(body['PPoint'], 1) } + self['Index'] = body['Index'] + self['Variables'] = { x['Variable']['Name'][0].split("$")[-1]: x['Type'] for x in body['DefineVariable'] } + + # Indexes + self['Line2Points'] = defaultdict(list) + for point, line in self['Points'].items(): + self['Line2Points'][line].append(point) + self['SrcPoint2Edges'] = defaultdict(list) + for edge in self['Edges']: + src, dst = edge['Index'] + self['SrcPoint2Edges'][src].append(edge) + self['Line2Edges'] = defaultdict(list) + for (src, edges) in self['SrcPoint2Edges'].items(): + line = self['Points'][src] + self['Line2Edges'][line].extend(edges) + + def edges_from_line(self, line): + return self['Line2Edges'][line] + + def edge_from_line(self, line): + edges = self.edges_from_line(line) + assert(len(edges) == 1) + return edges[0] + + def edges_from_point(self, point): + return self['SrcPoint2Edges'][point] + + def edge_from_point(self, point): + edges = self.edges_from_point(point) + assert(len(edges) == 1) + return edges[0] + + def assignment_point(self, varname): + for edge in self['Edges']: + if edge['Kind'] != 'Assign': + continue + dst = edge['Exp'][0] + if dst['Kind'] != 'Var': + continue + if dst['Variable']['Name'][0] == varname: + return edge['Index'][0] + raise Exception("assignment to variable %s not found" % varname) + + def assignment_line(self, varname): + return self['Points'][self.assignment_point(varname)] diff --git a/js/src/devtools/rootAnalysis/t/suppression/source.cpp b/js/src/devtools/rootAnalysis/t/suppression/source.cpp new file mode 100644 index 000000000000..e7b41b4cb326 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/suppression/source.cpp @@ -0,0 +1,64 @@ +#define ANNOTATE(property) __attribute__((tag(property))) + +struct Cell { int f; } ANNOTATE("GC Thing"); + +class AutoSuppressGC_Base { + public: + AutoSuppressGC_Base() {} + ~AutoSuppressGC_Base() {} +} ANNOTATE("Suppress GC"); + +class AutoSuppressGC_Child : public AutoSuppressGC_Base { + public: + AutoSuppressGC_Child() : AutoSuppressGC_Base() {} +}; + +class AutoSuppressGC { + AutoSuppressGC_Child helpImBeingSuppressed; + + public: + AutoSuppressGC() {} +}; + +extern void GC() ANNOTATE("GC Call"); + +void GC() +{ + // If the implementation is too trivial, the function body won't be emitted at all. + asm(""); +} + +extern void foo(Cell*); + +void suppressedFunction() { + GC(); // Calls GC, but is always called within AutoSuppressGC +} + +void halfSuppressedFunction() { + GC(); // Calls GC, but is sometimes called within AutoSuppressGC +} + +void unsuppressedFunction() { + GC(); // Calls GC, never within AutoSuppressGC +} + +void f() { + Cell* cell1 = nullptr; + Cell* cell2 = nullptr; + Cell* cell3 = nullptr; + { + AutoSuppressGC nogc; + suppressedFunction(); + halfSuppressedFunction(); + } + foo(cell1); + halfSuppressedFunction(); + foo(cell2); + unsuppressedFunction(); + { + // Old bug: it would look from the first AutoSuppressGC constructor it + // found to the last destructor. This statement *should* have no effect. + AutoSuppressGC nogc; + } + foo(cell3); +} diff --git a/js/src/devtools/rootAnalysis/t/suppression/test.py b/js/src/devtools/rootAnalysis/t/suppression/test.py new file mode 100644 index 000000000000..65974cc3369a --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/suppression/test.py @@ -0,0 +1,23 @@ +test.compile("source.cpp") +test.run_analysis_script('gcTypes', upto='gcFunctions') + +# The suppressions file uses only mangled names since it's for internal use, +# though I may change that soon given (1) the unfortunate non-uniqueness of +# mangled constructor names, and (2) the usefulness of this file for +# mrgiggles's reporting. +suppressed = test.load_suppressed_functions() + +# Only one of these is fully suppressed (ie, *always* called within the scope +# of an AutoSuppressGC). +assert(len(filter(lambda f: 'suppressedFunction' in f, suppressed)) == 1) +assert(len(filter(lambda f: 'halfSuppressedFunction' in f, suppressed)) == 0) +assert(len(filter(lambda f: 'unsuppressedFunction' in f, suppressed)) == 0) + +# gcFunctions should be the inverse, but we get to rely on unmangled names here. +gcFunctions = test.load_gcFunctions() +print(gcFunctions) +assert('void GC()' in gcFunctions) +assert('void suppressedFunction()' not in gcFunctions) +assert('void halfSuppressedFunction()' in gcFunctions) +assert('void unsuppressedFunction()' in gcFunctions) +assert('void f()' in gcFunctions) diff --git a/js/src/devtools/rootAnalysis/t/testlib.py b/js/src/devtools/rootAnalysis/t/testlib.py new file mode 100644 index 000000000000..6f48ab19f278 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/testlib.py @@ -0,0 +1,119 @@ +import json +import os +import re +import subprocess + +from sixgill import Body +from collections import defaultdict, namedtuple + +scriptdir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + +HazardSummary = namedtuple('HazardSummary', ['function', 'variable', 'type', 'GCFunction', 'location']) + + +def equal(got, expected): + if got != expected: + print("Got '%s', expected '%s'" % (got, expected)) + +def extract_unmangled(func): + return func.split('$')[-1] + + +class Test(object): + def __init__(self, indir, outdir, cfg): + self.indir = indir + self.outdir = outdir + self.cfg = cfg + + def infile(self, path): + return os.path.join(self.indir, path) + + def binpath(self, prog): + return os.path.join(self.cfg.sixgill_bin, prog) + + def compile(self, source): + cmd = "{CXX} -c {source} -O3 -std=c++11 -fplugin={sixgill} -fplugin-arg-xgill-mangle=1".format( + source=self.infile(source), + CXX=self.cfg.cxx, sixgill=self.cfg.sixgill_plugin) + if self.cfg.verbose: + print("Running %s" % cmd) + subprocess.check_call(["sh", "-c", cmd]) + + def load_db_entry(self, dbname, pattern): + '''Look up an entry from an XDB database file, 'pattern' may be an exact + matching string, or an re pattern object matching a single entry.''' + + if not isinstance(pattern, basestring): + output = subprocess.check_output([self.binpath("xdbkeys"), dbname + ".xdb"]) + matches = filter(lambda _: re.search(pattern, _), output.splitlines()) + if len(matches) == 0: + raise Exception("entry not found") + if len(matches) > 1: + raise Exception("multiple entries found") + pattern = matches[0] + + output = subprocess.check_output([self.binpath("xdbfind"), "-json", dbname + ".xdb", pattern]) + return json.loads(output) + + def run_analysis_script(self, phase, upto=None): + file("defaults.py", "w").write('''\ +analysis_scriptdir = '{scriptdir}' +sixgill_bin = '{bindir}' +'''.format(scriptdir=scriptdir, bindir=self.cfg.sixgill_bin)) + cmd = [os.path.join(scriptdir, "analyze.py"), phase] + if upto: + cmd += ["--upto", upto] + cmd.append("--source=%s" % self.indir) + cmd.append("--objdir=%s" % self.outdir) + cmd.append("--js=%s" % self.cfg.js) + if self.cfg.verbose: + cmd.append("--verbose") + print("Running " + " ".join(cmd)) + subprocess.check_call(cmd) + + def computeGCTypes(self): + self.run_analysis_script("gcTypes", upto="gcTypes") + + def computeHazards(self): + self.run_analysis_script("callgraph") + + def load_text_file(self, filename, extract=lambda l: l): + fullpath = os.path.join(self.outdir, filename) + values = (extract(line.strip()) for line in file(fullpath)) + return filter(lambda _: _ is not None, values) + + def load_suppressed_functions(self): + return set(self.load_text_file("suppressedFunctions.lst")) + + def load_gcTypes(self): + def grab_type(line): + m = re.match(r'^(GC\w+): (.*)', line) + if m: + return (m.group(1) + 's', m.group(2)) + return None + + gctypes = defaultdict(list) + for collection, typename in self.load_text_file('gcTypes.txt', extract=grab_type): + gctypes[collection].append(typename) + return gctypes + + def load_gcFunctions(self): + return self.load_text_file('gcFunctions.lst', extract=extract_unmangled) + + def load_hazards(self): + def grab_hazard(line): + m = re.match(r"Function '(.*?)' has unrooted '(.*?)' of type '(.*?)' live across GC call '(.*?)' at (.*)", line) + if m: + info = list(m.groups()) + info[0] = info[0].split("$")[-1] + info[3] = info[3].split("$")[-1] + return HazardSummary(*info) + return None + + return self.load_text_file('rootingHazards.txt', extract=grab_hazard) + + def process_body(self, body): + return Body(body) + + def process_bodies(self, bodies): + return [self.process_body(b) for b in bodies] From 75e3654bc57889530cd45bf1a0917147647f2fd0 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Wed, 4 May 2016 18:36:23 -0700 Subject: [PATCH 123/199] Bug 1259850 - Update sixgill to ignore exception cleanup and properly qualify some type names MozReview-Commit-ID: 7WSYmLAVb7M --HG-- extra : rebase_source : 44c937df8e1fe080577d8a00b924698f033daa39 extra : source : b641d01138ab54ec1f6b29232614b338b02d42f1 --- .../linux64/hazard.manifest | 68 +++++++++---------- .../linux64/hazard.manifest | 56 +++++++-------- .../rootAnalysis/build/sixgill.manifest | 16 ++--- 3 files changed, 70 insertions(+), 70 deletions(-) diff --git a/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest b/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest index fcef5bfc5738..f4ab1fcabc74 100644 --- a/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest +++ b/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest @@ -1,47 +1,47 @@ [ { -"version": "gcc 4.9.3", -"size": 102421980, -"digest": "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", -"algorithm": "sha512", -"filename": "gcc.tar.xz", -"unpack": true +"size" : 102421980, +"digest" : "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", +"version" : "gcc 4.9.3", +"unpack" : true, +"filename" : "gcc.tar.xz", +"algorithm" : "sha512" }, { -"hg_id" : "cd93f15a30ce", +"unpack" : true, "algorithm" : "sha512", -"digest" : "541eb3842ab6b91bd87223cad7a5e4387ef3e496e5b580c8047b8b586bc7eb69fecf3c9eb8c45a1e0deebb53554f0e8acedfe1b4ca64d93b6d008f3f2eb11389", "filename" : "sixgill.tar.xz", -"size" : 2626640, +"hg_id" : "8cb9c3fb039a+ tip", +"digest" : "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029", +"size" : 2631908 +}, +{ +"algorithm" : "sha512", +"filename" : "gtk3.tar.xz", +"setup" : "setup.sh", +"unpack" : true, +"digest" : "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", +"size" : 12072532 +}, +{ +"size" : 89319524, +"digest" : "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", +"algorithm" : "sha512", +"filename" : "rustc.tar.xz", "unpack" : true }, { -"size": 12072532, -"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", -"algorithm": "sha512", -"filename": "gtk3.tar.xz", -"setup": "setup.sh", -"unpack": true +"algorithm" : "sha512", +"filename" : "sccache.tar.bz2", +"unpack" : true, +"digest" : "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", +"size" : 167175 }, { -"size": 89319524, -"digest": "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", -"algorithm": "sha512", -"filename": "rustc.tar.xz", -"unpack": true -}, -{ -"size": 167175, -"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", -"algorithm": "sha512", -"filename": "sccache.tar.bz2", -"unpack": true -}, -{ -"size": 31078810, -"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c", -"algorithm": "sha512", -"filename": "moz-tt.tar.bz2", -"unpack": true +"filename" : "moz-tt.tar.bz2", +"algorithm" : "sha512", +"unpack" : true, +"digest" : "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c", +"size" : 31078810 } ] diff --git a/browser/config/tooltool-manifests/linux64/hazard.manifest b/browser/config/tooltool-manifests/linux64/hazard.manifest index 9d0818273012..f05dcf166c5b 100644 --- a/browser/config/tooltool-manifests/linux64/hazard.manifest +++ b/browser/config/tooltool-manifests/linux64/hazard.manifest @@ -1,40 +1,40 @@ [ { -"version": "gcc 4.9.3", -"size": 102421980, -"digest": "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", -"algorithm": "sha512", -"filename": "gcc.tar.xz", -"unpack": true -}, -{ -"hg_id" : "cd93f15a30ce", +"size" : 102421980, +"version" : "gcc 4.9.3", +"filename" : "gcc.tar.xz", "algorithm" : "sha512", -"digest" : "541eb3842ab6b91bd87223cad7a5e4387ef3e496e5b580c8047b8b586bc7eb69fecf3c9eb8c45a1e0deebb53554f0e8acedfe1b4ca64d93b6d008f3f2eb11389", -"filename" : "sixgill.tar.xz", -"size" : 2626640, +"digest" : "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921", "unpack" : true }, { -"size": 12072532, -"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", -"algorithm": "sha512", -"filename": "gtk3.tar.xz", -"setup": "setup.sh", -"unpack": true +"digest" : "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029", +"unpack" : true, +"algorithm" : "sha512", +"filename" : "sixgill.tar.xz", +"size" : 2631908, +"hg_id" : "8cb9c3fb039a+ tip" }, { -"size": 89319524, -"digest": "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", -"algorithm": "sha512", -"filename": "rustc.tar.xz", -"unpack": true +"digest" : "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", +"unpack" : true, +"setup" : "setup.sh", +"algorithm" : "sha512", +"filename" : "gtk3.tar.xz", +"size" : 12072532 }, { -"size": 167175, -"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", -"algorithm": "sha512", -"filename": "sccache.tar.bz2", -"unpack": true +"unpack" : true, +"digest" : "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1", +"filename" : "rustc.tar.xz", +"algorithm" : "sha512", +"size" : 89319524 +}, +{ +"filename" : "sccache.tar.bz2", +"algorithm" : "sha512", +"digest" : "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", +"unpack" : true, +"size" : 167175 } ] diff --git a/js/src/devtools/rootAnalysis/build/sixgill.manifest b/js/src/devtools/rootAnalysis/build/sixgill.manifest index de0d12c709c6..d02bb1bf4226 100644 --- a/js/src/devtools/rootAnalysis/build/sixgill.manifest +++ b/js/src/devtools/rootAnalysis/build/sixgill.manifest @@ -1,10 +1,10 @@ [ - { - "hg_id" : "cd93f15a30ce", - "algorithm" : "sha512", - "digest" : "541eb3842ab6b91bd87223cad7a5e4387ef3e496e5b580c8047b8b586bc7eb69fecf3c9eb8c45a1e0deebb53554f0e8acedfe1b4ca64d93b6d008f3f2eb11389", - "filename" : "sixgill.tar.xz", - "size" : 2626640, - "unpack" : true - } +{ +"digest" : "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029", +"size" : 2631908, +"hg_id" : "8cb9c3fb039a+ tip", +"unpack" : true, +"filename" : "sixgill.tar.xz", +"algorithm" : "sha512" +} ] From 81c295d72d716c209d7960a373b2aaa51fa501d8 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 28 Apr 2016 14:19:01 -0700 Subject: [PATCH 124/199] Bug 1259850 - Fix constructor recognition, r=terrence MozReview-Commit-ID: 46064amRbCQ --HG-- extra : rebase_source : 362db8683b65758fa8f9976a166705b66a0f6d8b extra : source : 0b9dedcf7163ce35e4207a1d8fe33523a9a8f920 --- js/src/devtools/rootAnalysis/CFG.js | 2 +- js/src/devtools/rootAnalysis/annotations.js | 27 ++++++++++++++++--- .../devtools/rootAnalysis/computeCallgraph.js | 1 + js/src/devtools/rootAnalysis/run-test.py | 7 +++-- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/js/src/devtools/rootAnalysis/CFG.js b/js/src/devtools/rootAnalysis/CFG.js index 0b35e8914505..6e9facaa1201 100644 --- a/js/src/devtools/rootAnalysis/CFG.js +++ b/js/src/devtools/rootAnalysis/CFG.js @@ -70,7 +70,7 @@ function allRAIIGuardedCallPoints(bodies, body, isConstructor) continue; var variable = callee.Variable; assert(variable.Kind == "Func"); - if (!isConstructor(variable.Name)) + if (!isConstructor(edge.Type, variable.Name)) continue; if (!("PEdgeCallInstance" in edge)) continue; diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index cb50fc61b8dd..7008cbbacb95 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -324,10 +324,31 @@ function isUnsafeStorage(typeName) return typeName.startsWith('UniquePtr<'); } -function isSuppressConstructor(varName) +function isSuppressConstructor(edgeType, varName) { - // varName[1] contains the unqualified name - return GCSuppressionTypes.indexOf(varName[1]) != -1; + // Check whether this could be a constructor + if (edgeType.Kind != 'Function') + return false; + if (!('TypeFunctionCSU' in edgeType)) + return false; + if (edgeType.Type.Kind != 'Void') + return false; + + // Check whether the type is a known suppression type. + var type = edgeType.TypeFunctionCSU.Type.Name; + if (GCSuppressionTypes.indexOf(type) == -1) + return false; + + // And now make sure this is the constructor, not some other method on a + // suppression type. varName[0] contains the qualified name. + var [ mangled, unmangled ] = splitFunction(varName[0]); + if (mangled.search(/C\dE/) == -1) + return false; // Mangled names of constructors have CE + var m = unmangled.match(/([~\w]+)(?:<.*>)?\(/); + if (!m) + return false; + var type_stem = type.replace(/\w+::/g, '').replace(/\<.*\>/g, ''); + return m[1] == type_stem; } // nsISupports subclasses' methods may be scriptable (or overridden diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index ade5f26f0600..ae30d9bd5bca 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -346,6 +346,7 @@ function process(functionName, functionBodies) { for (var body of functionBodies) body.suppressed = []; + for (var body of functionBodies) { for (var [pbody, id] of allRAIIGuardedCallPoints(functionBodies, body, isSuppressConstructor)) pbody.suppressed[id] = true; diff --git a/js/src/devtools/rootAnalysis/run-test.py b/js/src/devtools/rootAnalysis/run-test.py index 0d205b0d421a..113e5fe351a1 100644 --- a/js/src/devtools/rootAnalysis/run-test.py +++ b/js/src/devtools/rootAnalysis/run-test.py @@ -39,6 +39,9 @@ parser.add_argument( parser.add_argument( '--verbose', '-v', action='store_true', help='Display verbose output, including commands executed') +parser.add_argument( + 'tests', nargs='*', default=['sixgill-tree', 'suppression', 'hazards'], + help='tests to run') cfg = parser.parse_args() @@ -69,8 +72,8 @@ try: except OSError: pass -tests = ['sixgill-tree', 'suppression', 'hazards'] -for name in tests: +for name in cfg.tests: + name = os.path.basename(name) indir = os.path.join(testdir, name) outdir = os.path.join(testdir, 'out', name) try: From 346b589e9021180aa35a92c520372b6cb16e3b5f Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 28 Apr 2016 11:27:50 -0700 Subject: [PATCH 125/199] Bug 1259850 - Remove unused test files, r=terrence MozReview-Commit-ID: sBZct85zDJ --HG-- extra : rebase_source : 5c41e5fee58035cb8e7862addc6daae7a782e884 extra : source : 19c13aa9b5ada4d1284a09d8284b3231e0790ebe --- js/src/devtools/rootAnalysis/test/source.cpp | 70 -------------------- js/src/devtools/rootAnalysis/test/test.py | 58 ---------------- 2 files changed, 128 deletions(-) delete mode 100644 js/src/devtools/rootAnalysis/test/source.cpp delete mode 100644 js/src/devtools/rootAnalysis/test/test.py diff --git a/js/src/devtools/rootAnalysis/test/source.cpp b/js/src/devtools/rootAnalysis/test/source.cpp deleted file mode 100644 index 2de9ef4bb90b..000000000000 --- a/js/src/devtools/rootAnalysis/test/source.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#define ANNOTATE(property) __attribute__((tag(property))) - -namespace js { -namespace gc { -struct Cell { int f; } ANNOTATE("GC Thing"); -} -} - -struct Bogon { -}; - -struct JustACell : public js::gc::Cell { - bool iHaveNoDataMembers() { return true; } -}; - -struct JSObject : public js::gc::Cell, public Bogon { - int g; -}; - -struct SpecialObject : public JSObject { - int z; -}; - -struct ErrorResult { - bool hasObj; - JSObject *obj; - void trace() {} -} ANNOTATE("Suppressed GC Pointer"); - -struct OkContainer { - ErrorResult res; - bool happy; -}; - -struct UnrootedPointer { - JSObject *obj; -}; - -template -class Rooted { - T data; -} ANNOTATE("Rooted Pointer"); - -extern void js_GC() ANNOTATE("GC Call") ANNOTATE("Slow"); - -void js_GC() {} - -void root_arg(JSObject *obj, JSObject *random) -{ - // Use all these types so they get included in the output. - SpecialObject so; - UnrootedPointer up; - Bogon b; - OkContainer okc; - Rooted ro; - Rooted rso; - - obj = random; - - JSObject *other1 = obj; - js_GC(); - - float MARKER1 = 0; - JSObject *other2 = obj; - other1->f = 1; - other2->f = -1; - - unsigned int u1 = 1; - unsigned int u2 = -1; -} diff --git a/js/src/devtools/rootAnalysis/test/test.py b/js/src/devtools/rootAnalysis/test/test.py deleted file mode 100644 index 59281570999d..000000000000 --- a/js/src/devtools/rootAnalysis/test/test.py +++ /dev/null @@ -1,58 +0,0 @@ -compile("source.cpp") -computeGCTypes() -body = process_body(load_db_entry("src_body", re.compile(r'root_arg'))[0]) - -# Rendering positive and negative integers -marker1 = body.assignment_line('MARKER1') -equal(body.edge_from_line(marker1 + 2)['Exp'][1]['String'], '1') -equal(body.edge_from_line(marker1 + 3)['Exp'][1]['String'], '-1') - -equal(body.edge_from_point(body.assignment_point('u1'))['Exp'][1]['String'], '1') -equal(body.edge_from_point(body.assignment_point('u2'))['Exp'][1]['String'], '4294967295') - -assert('obj' in body['Variables']) -assert('random' in body['Variables']) -assert('other1' in body['Variables']) -assert('other2' in body['Variables']) - -# Test function annotations -js_GC = process_body(load_db_entry("src_body", re.compile(r'js_GC'))[0]) -annotations = js_GC['Variables']['void js_GC()']['Annotation'] -assert(annotations) -found_call_tag = False -for annotation in annotations: - (annType, value) = annotation['Name'] - if annType == 'Tag' and value == 'GC Call': - found_call_tag = True -assert(found_call_tag) - -# Test type annotations - -# js::gc::Cell first -cell = load_db_entry("src_comp", 'js::gc::Cell')[0] -assert(cell['Kind'] == 'Struct') -annotations = cell['Annotation'] -assert(len(annotations) == 1) -(tag, value) = annotations[0]['Name'] -assert(tag == 'Tag') -assert(value == 'GC Thing') - -# Check JSObject inheritance. -JSObject = load_db_entry("src_comp", 'JSObject')[0] -bases = [ b['Base'] for b in JSObject['CSUBaseClass'] ] -assert('js::gc::Cell' in bases) -assert('Bogon' in bases) -assert(len(bases) == 2) - -# Check type analysis -gctypes = loadGCTypes() -assert('js::gc::Cell' in gctypes['GCThings']) -assert('JustACell' in gctypes['GCThings']) -assert('JSObject' in gctypes['GCThings']) -assert('SpecialObject' in gctypes['GCThings']) -assert('UnrootedPointer' in gctypes['GCPointers']) -assert('Bogon' not in gctypes['GCThings']) -assert('Bogon' not in gctypes['GCPointers']) -assert('ErrorResult' not in gctypes['GCPointers']) -assert('OkContainer' not in gctypes['GCPointers']) -assert('class Rooted' not in gctypes['GCPointers']) From 74a16a4cf1a6948858cac6346bbf8bf34a0c63f5 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 28 Apr 2016 15:05:09 -0700 Subject: [PATCH 126/199] Bug 1259850 - Test for sixgill exception handling + analysis constructor/destructor matching, r=terrence MozReview-Commit-ID: H5NZlyS2rx5 --HG-- rename : js/src/devtools/rootAnalysis/t/suppression/source.cpp => js/src/devtools/rootAnalysis/t/exceptions/source.cpp rename : js/src/devtools/rootAnalysis/t/suppression/test.py => js/src/devtools/rootAnalysis/t/exceptions/test.py extra : rebase_source : 04e6ff6569457cac14126781b2505874ec759d00 extra : source : 788ac18818c9b91f3057c4c382bf17be264eb03e --- js/src/devtools/rootAnalysis/analyzeRoots.js | 2 +- .../devtools/rootAnalysis/computeCallgraph.js | 2 +- js/src/devtools/rootAnalysis/run-test.py | 2 +- .../rootAnalysis/t/exceptions/source.cpp | 42 +++++++++++++++++++ .../rootAnalysis/t/exceptions/test.py | 19 +++++++++ js/src/devtools/rootAnalysis/t/testlib.py | 9 ++-- 6 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 js/src/devtools/rootAnalysis/t/exceptions/source.cpp create mode 100644 js/src/devtools/rootAnalysis/t/exceptions/test.py diff --git a/js/src/devtools/rootAnalysis/analyzeRoots.js b/js/src/devtools/rootAnalysis/analyzeRoots.js index a10f63d0cadd..a0fa94e061c2 100644 --- a/js/src/devtools/rootAnalysis/analyzeRoots.js +++ b/js/src/devtools/rootAnalysis/analyzeRoots.js @@ -29,7 +29,7 @@ var batch = (scriptArgs[5]|0) || 1; var numBatches = (scriptArgs[6]|0) || 1; var tmpfile = scriptArgs[7] || "tmp.txt"; -GCSuppressionTypes = loadTypeInfo(typeInfoFile)["Suppress GC"]; +GCSuppressionTypes = loadTypeInfo(typeInfoFile)["Suppress GC"] || []; var gcFunctions = {}; var text = snarf("gcFunctions.lst").split("\n"); diff --git a/js/src/devtools/rootAnalysis/computeCallgraph.js b/js/src/devtools/rootAnalysis/computeCallgraph.js index ae30d9bd5bca..dab3f76216f6 100644 --- a/js/src/devtools/rootAnalysis/computeCallgraph.js +++ b/js/src/devtools/rootAnalysis/computeCallgraph.js @@ -308,7 +308,7 @@ function processBody(functionName, body) } } -GCSuppressionTypes = loadTypeInfo(typeInfo_filename)["Suppress GC"]; +GCSuppressionTypes = loadTypeInfo(typeInfo_filename)["Suppress GC"] || []; var xdb = xdbLibrary(); xdb.open("src_comp.xdb"); diff --git a/js/src/devtools/rootAnalysis/run-test.py b/js/src/devtools/rootAnalysis/run-test.py index 113e5fe351a1..3bc9085a0aaf 100644 --- a/js/src/devtools/rootAnalysis/run-test.py +++ b/js/src/devtools/rootAnalysis/run-test.py @@ -40,7 +40,7 @@ parser.add_argument( '--verbose', '-v', action='store_true', help='Display verbose output, including commands executed') parser.add_argument( - 'tests', nargs='*', default=['sixgill-tree', 'suppression', 'hazards'], + 'tests', nargs='*', default=['sixgill-tree', 'suppression', 'hazards', 'exceptions'], help='tests to run') cfg = parser.parse_args() diff --git a/js/src/devtools/rootAnalysis/t/exceptions/source.cpp b/js/src/devtools/rootAnalysis/t/exceptions/source.cpp new file mode 100644 index 000000000000..14169740e8f8 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/exceptions/source.cpp @@ -0,0 +1,42 @@ +#define ANNOTATE(property) __attribute__((tag(property))) + +struct Cell { int f; } ANNOTATE("GC Thing"); + +extern void GC() ANNOTATE("GC Call"); + +void GC() +{ + // If the implementation is too trivial, the function body won't be emitted at all. + asm(""); +} + +class RAII_GC { + public: + RAII_GC() {} + ~RAII_GC() { GC(); } +}; + +// ~AutoSomething calls GC because of the RAII_GC field. The constructor, +// though, should *not* GC -- unless it throws an exception. Which is not +// possible when compiled with -fno-exceptions. +class AutoSomething { + RAII_GC gc; + public: + AutoSomething() : gc() { + asm(""); // Ooh, scary, this might throw an exception + } + ~AutoSomething() { + asm(""); + } +}; + +extern void usevar(Cell* cell); + +void f() { + Cell* thing = nullptr; // Live range starts here + + { + AutoSomething smth; // Constructor can GC only if exceptions are enabled + usevar(thing); // Live range ends here + } // In particular, 'thing' is dead at the destructor, so no hazard +} diff --git a/js/src/devtools/rootAnalysis/t/exceptions/test.py b/js/src/devtools/rootAnalysis/t/exceptions/test.py new file mode 100644 index 000000000000..f6d7f5e353f2 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/exceptions/test.py @@ -0,0 +1,19 @@ +test.compile("source.cpp", '-fno-exceptions') +test.run_analysis_script('gcTypes') + +hazards = test.load_hazards() +assert(len(hazards) == 0) + +# If we compile with exceptions, then there *should* be a hazard because +# AutoSomething::AutoSomething might throw an exception, which would cause the +# partially-constructed value to be torn down, which will call ~RAII_GC. + +test.compile("source.cpp", '-fexceptions') +test.run_analysis_script('gcTypes') + +hazards = test.load_hazards() +assert(len(hazards) == 1) +hazard = hazards[0] +assert(hazard.function == 'void f()') +assert(hazard.variable == 'thing') +assert("AutoSomething::AutoSomething" in hazard.GCFunction) diff --git a/js/src/devtools/rootAnalysis/t/testlib.py b/js/src/devtools/rootAnalysis/t/testlib.py index 6f48ab19f278..438398f1ed66 100644 --- a/js/src/devtools/rootAnalysis/t/testlib.py +++ b/js/src/devtools/rootAnalysis/t/testlib.py @@ -31,10 +31,11 @@ class Test(object): def binpath(self, prog): return os.path.join(self.cfg.sixgill_bin, prog) - def compile(self, source): - cmd = "{CXX} -c {source} -O3 -std=c++11 -fplugin={sixgill} -fplugin-arg-xgill-mangle=1".format( + def compile(self, source, options = ''): + cmd = "{CXX} -c {source} -O3 -std=c++11 -fplugin={sixgill} -fplugin-arg-xgill-mangle=1 {options}".format( source=self.infile(source), - CXX=self.cfg.cxx, sixgill=self.cfg.sixgill_plugin) + CXX=self.cfg.cxx, sixgill=self.cfg.sixgill_plugin, + options=options) if self.cfg.verbose: print("Running %s" % cmd) subprocess.check_call(["sh", "-c", cmd]) @@ -75,7 +76,7 @@ sixgill_bin = '{bindir}' self.run_analysis_script("gcTypes", upto="gcTypes") def computeHazards(self): - self.run_analysis_script("callgraph") + self.run_analysis_script("gcTypes") def load_text_file(self, filename, extract=lambda l: l): fullpath = os.path.join(self.outdir, filename) From af2225da898a8e64df61bcd4ae2d8512be54547b Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 24 May 2016 18:26:21 -0700 Subject: [PATCH 127/199] Bug 1259850 - Comments MozReview-Commit-ID: 9RjVxl4EX8N --HG-- extra : rebase_source : 032c4105bb9177197ec339381ae74275aae0770e extra : source : 95107c3ad9cf5a173a17aee4fcd8635defbf0eb4 --- js/public/GCAnnotations.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/public/GCAnnotations.h b/js/public/GCAnnotations.h index 28813a4db991..366d787bf4dd 100644 --- a/js/public/GCAnnotations.h +++ b/js/public/GCAnnotations.h @@ -23,7 +23,7 @@ # define JS_HAZ_ROOTED __attribute__((tag("Rooted Pointer"))) // Mark a type as something that should not be held live across a GC, but which -// is itself not a GC pointer. +// is not itself a GC pointer. # define JS_HAZ_GC_INVALIDATED __attribute__((tag("Invalidated by GC"))) // Mark a type that would otherwise be considered a GC Pointer (eg because it @@ -39,6 +39,7 @@ // invalidating GC pointers. # define JS_HAZ_GC_CALL __attribute__((tag("GC Call"))) +// Mark an RAII class as suppressing GC within its scope. # define JS_HAZ_GC_SUPPRESSED __attribute__((tag("Suppress GC"))) #else From 75d1976f53e1ad584f3e7794bf0c8597c37908fb Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 27 May 2016 16:48:31 -0700 Subject: [PATCH 128/199] Bug 1259850 - Annotate mFree function pointer as not GCing, r=terrence MozReview-Commit-ID: LrzzLYHsBnO --HG-- extra : rebase_source : 9290a9be8e419d9bc73cd4feabeac12916326b3c extra : source : a73f74f718e7464d40c17df3f4228c96d5819d1a --- js/src/devtools/rootAnalysis/annotations.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index 7008cbbacb95..ea26539dc64d 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -85,6 +85,7 @@ var ignoreCallees = { "std::strstreambuf._M_alloc_fun" : true, "std::strstreambuf._M_free_fun" : true, "struct js::gc::Callback.op" : true, + "mozilla::ThreadSharedFloatArrayBufferList::Storage.mFree" : true, }; function fieldCallCannotGC(csu, fullfield) @@ -160,6 +161,7 @@ function isSuppressedVirtualMethod(csu, method) var ignoreFunctions = { "ptio.c:pt_MapError" : true, "je_malloc_printf" : true, + "vprintf_stderr" : true, "PR_ExplodeTime" : true, "PR_ErrorInstallTable" : true, "PR_SetThreadPrivate" : true, @@ -223,6 +225,13 @@ var ignoreFunctions = { "uint64 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true, "uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true, "void js::Nursery::freeMallocedBuffers()" : true, + + // It would be cool to somehow annotate that nsTHashtable will use + // nsTHashtable::s_MatchEntry for its matchEntry function pointer, but + // there is no mechanism for that. So we will just annotate a particularly + // troublesome logging-related usage. + "EntryType* nsTHashtable::PutEntry(nsTHashtable::KeyType) [with EntryType = nsBaseHashtableET >; nsTHashtable::KeyType = const char*]" : true, + "EntryType* nsTHashtable::GetEntry(nsTHashtable::KeyType) const [with EntryType = nsBaseHashtableET >; nsTHashtable::KeyType = const char*]" : true, }; function isProtobuf(name) From c795982e76327918911eed36411961b9acee8af8 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 27 May 2016 22:00:10 -0700 Subject: [PATCH 129/199] Bug 1259850 - Make ZoneCellIter variants to communicate nogc to the hazard analysis, r=jonco Also create accessors on gc::Zone for a nicer API to ZoneCellIters. MozReview-Commit-ID: AxRTTUfWrND --HG-- extra : rebase_source : cefc7562d01e70b34288dbe8b1f53bc1555a2d86 extra : source : c95bdd426ced9a71bc64694ea236b46d035bb0df --- js/src/gc/Iteration.cpp | 22 +++--- js/src/gc/Statistics.cpp | 47 +++++++++--- js/src/gc/Statistics.h | 31 ++++++-- js/src/gc/Zone.cpp | 13 ++-- js/src/gc/Zone.h | 10 +++ js/src/jit/BaselineJIT.cpp | 10 +-- js/src/jit/Ion.cpp | 8 +- js/src/jscompartment.cpp | 7 +- js/src/jsgc.cpp | 62 +++++++++------- js/src/jsgc.h | 89 ++++++++++++++++++++++ js/src/jsgcinlines.h | 144 ++++++++++++++++++++++++++---------- js/src/jsopcode.cpp | 11 ++- js/src/vm/Debugger.cpp | 4 +- js/src/vm/HelperThreads.cpp | 5 +- js/src/vm/NativeObject.cpp | 2 +- js/src/vm/TypeInference.cpp | 18 ++--- 16 files changed, 343 insertions(+), 140 deletions(-) diff --git a/js/src/gc/Iteration.cpp b/js/src/gc/Iteration.cpp index daa8c741a8ef..6c76f326954a 100644 --- a/js/src/gc/Iteration.cpp +++ b/js/src/gc/Iteration.cpp @@ -95,21 +95,19 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment, void* data, IterateScriptCallback scriptCallback) { MOZ_ASSERT(!rt->mainThread.suppressGC); - rt->gc.evictNursery(); - MOZ_ASSERT(rt->gc.nursery.isEmpty()); - + AutoEmptyNursery empty(rt); AutoPrepareForTracing prep(rt, SkipAtoms); if (compartment) { - for (ZoneCellIterUnderGC i(compartment->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + Zone* zone = compartment->zone(); + for (auto script = zone->cellIter(empty); !script.done(); script.next()) { if (script->compartment() == compartment) scriptCallback(rt, data, script); } } else { for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { - for (ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) - scriptCallback(rt, data, i.get()); + for (auto script = zone->cellIter(empty); !script.done(); script.next()) + scriptCallback(rt, data, script); } } } @@ -117,14 +115,14 @@ js::IterateScripts(JSRuntime* rt, JSCompartment* compartment, void js::IterateGrayObjects(Zone* zone, GCThingCallback cellCallback, void* data) { - zone->runtimeFromMainThread()->gc.evictNursery(); - AutoPrepareForTracing prep(zone->runtimeFromMainThread(), SkipAtoms); + JSRuntime* rt = zone->runtimeFromMainThread(); + AutoEmptyNursery empty(rt); + AutoPrepareForTracing prep(rt, SkipAtoms); for (auto thingKind : ObjectAllocKinds()) { - for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) { - JSObject* obj = i.get(); + for (auto obj = zone->cellIter(thingKind, empty); !obj.done(); obj.next()) { if (obj->asTenured().isMarked(GRAY)) - cellCallback(data, JS::GCCellPtr(obj)); + cellCallback(data, JS::GCCellPtr(obj.get())); } } } diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 7dedbb8a8894..2952e1cbcc7b 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -7,6 +7,7 @@ #include "gc/Statistics.h" #include "mozilla/ArrayUtils.h" +#include "mozilla/DebugOnly.h" #include "mozilla/IntegerRange.h" #include "mozilla/PodOperations.h" @@ -27,6 +28,7 @@ using namespace js; using namespace js::gc; using namespace js::gcstats; +using mozilla::DebugOnly; using mozilla::MakeRange; using mozilla::PodArrayZero; using mozilla::PodZero; @@ -761,7 +763,7 @@ Statistics::Statistics(JSRuntime* rt) maxPauseInInterval(0), phaseNestingDepth(0), activeDagSlot(PHASE_DAG_NONE), - suspendedPhaseNestingDepth(0), + suspended(0), sliceCallback(nullptr), nurseryCollectionCallback(nullptr), aborted(false) @@ -1069,7 +1071,7 @@ Statistics::startTimingMutator() return false; } - MOZ_ASSERT(suspendedPhaseNestingDepth == 0); + MOZ_ASSERT(suspended == 0); timedGCTime = 0; phaseStartTimes[PHASE_MUTATOR] = 0; @@ -1094,6 +1096,35 @@ Statistics::stopTimingMutator(double& mutator_ms, double& gc_ms) return true; } +void +Statistics::suspendPhases(Phase suspension) +{ + MOZ_ASSERT(suspension == PHASE_EXPLICIT_SUSPENSION || suspension == PHASE_IMPLICIT_SUSPENSION); + while (phaseNestingDepth) { + MOZ_ASSERT(suspended < mozilla::ArrayLength(suspendedPhases)); + Phase parent = phaseNesting[phaseNestingDepth - 1]; + suspendedPhases[suspended++] = parent; + recordPhaseEnd(parent); + } + suspendedPhases[suspended++] = suspension; +} + +void +Statistics::resumePhases() +{ + DebugOnly popped = suspendedPhases[--suspended]; + MOZ_ASSERT(popped == PHASE_EXPLICIT_SUSPENSION || popped == PHASE_IMPLICIT_SUSPENSION); + while (suspended && + suspendedPhases[suspended - 1] != PHASE_EXPLICIT_SUSPENSION && + suspendedPhases[suspended - 1] != PHASE_IMPLICIT_SUSPENSION) + { + Phase resumePhase = suspendedPhases[--suspended]; + if (resumePhase == PHASE_MUTATOR) + timedGCTime += PRMJ_Now() - timedGCStart; + beginPhase(resumePhase); + } +} + void Statistics::beginPhase(Phase phase) { @@ -1105,9 +1136,7 @@ Statistics::beginPhase(Phase phase) // // Reuse this mechanism for managing PHASE_MUTATOR. if (parent == PHASE_GC_BEGIN || parent == PHASE_GC_END || parent == PHASE_MUTATOR) { - MOZ_ASSERT(suspendedPhaseNestingDepth < mozilla::ArrayLength(suspendedPhases)); - suspendedPhases[suspendedPhaseNestingDepth++] = parent; - recordPhaseEnd(parent); + suspendPhases(PHASE_IMPLICIT_SUSPENSION); parent = phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : PHASE_NO_PARENT; } @@ -1154,12 +1183,8 @@ Statistics::endPhase(Phase phase) // When emptying the stack, we may need to resume a callback phase // (PHASE_GC_BEGIN/END) or return to timing the mutator (PHASE_MUTATOR). - if (phaseNestingDepth == 0 && suspendedPhaseNestingDepth > 0) { - Phase resumePhase = suspendedPhases[--suspendedPhaseNestingDepth]; - if (resumePhase == PHASE_MUTATOR) - timedGCTime += PRMJ_Now() - timedGCStart; - beginPhase(resumePhase); - } + if (phaseNestingDepth == 0 && suspended > 0 && suspendedPhases[suspended - 1] == PHASE_IMPLICIT_SUSPENSION) + resumePhases(); } void diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 5fa4748a94c6..4de504e226e7 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -86,6 +86,8 @@ enum Phase : uint8_t { PHASE_LIMIT, PHASE_NONE = PHASE_LIMIT, + PHASE_EXPLICIT_SUSPENSION = PHASE_LIMIT, + PHASE_IMPLICIT_SUSPENSION, PHASE_MULTI_PARENTS }; @@ -169,6 +171,22 @@ struct Statistics void endPhase(Phase phase); void endParallelPhase(Phase phase, const GCParallelTask* task); + // Occasionally, we may be in the middle of something that is tracked by + // this class, and we need to do something unusual (eg evict the nursery) + // that doesn't normally nest within the current phase. Suspend the + // currently tracked phase stack, at which time the caller is free to do + // other tracked operations. + // + // This also happens internally with PHASE_GC_BEGIN and other "non-GC" + // phases. While in these phases, any beginPhase will automatically suspend + // the non-GC phase, until that inner stack is complete, at which time it + // will automatically resume the non-GC phase. Explicit suspensions do not + // get auto-resumed. + void suspendPhases(Phase suspension = PHASE_EXPLICIT_SUSPENSION); + + // Resume a suspended stack of phases. + void resumePhases(); + void beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind, SliceBudget budget, JS::gcreason::Reason reason); void endSlice(); @@ -318,13 +336,14 @@ struct Statistics size_t activeDagSlot; /* - * To avoid recursive nesting, we discontinue a callback phase when any - * other phases are started. Remember what phase to resume when the inner - * phases are complete. (And because GCs can nest within the callbacks any - * number of times, we need a whole stack of of phases to resume.) + * Certain phases can interrupt the phase stack, eg callback phases. When + * this happens, we move the suspended phases over to a sepearate list, + * terminated by a dummy PHASE_SUSPENSION phase (so that we can nest + * suspensions by suspending multiple stacks with a PHASE_SUSPENSION in + * between). */ - Phase suspendedPhases[MAX_NESTING]; - size_t suspendedPhaseNestingDepth; + Phase suspendedPhases[MAX_NESTING * 3]; + size_t suspended; /* Sweep times for SCCs of compartments. */ Vector sccTimes; diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index 1f4c6dee3b37..19d80af1551d 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -153,13 +153,13 @@ Zone::sweepBreakpoints(FreeOp* fop) */ MOZ_ASSERT(isGCSweepingOrCompacting()); - for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto iter = cellIter(); !iter.done(); iter.next()) { + JSScript* script = iter; if (!script->hasAnyBreakpointsOrStepMode()) continue; bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script); - MOZ_ASSERT(script == i.get()); + MOZ_ASSERT(script == iter); for (unsigned i = 0; i < script->length(); i++) { BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i)); if (!site) @@ -207,10 +207,8 @@ Zone::discardJitCode(FreeOp* fop) #ifdef DEBUG /* Assert no baseline scripts are marked as active. */ - for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = cellIter(); !script.done(); script.next()) MOZ_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active()); - } #endif /* Mark baseline scripts on the stack as active. */ @@ -219,8 +217,7 @@ Zone::discardJitCode(FreeOp* fop) /* Only mark OSI points if code is being discarded. */ jit::InvalidateAll(fop, this); - for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = cellIter(); !script.done(); script.next()) { jit::FinishInvalidation(fop, script); /* diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 1d019e4a426a..17e53425be57 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -84,6 +84,9 @@ using UniqueIdMap = GCHashMap +class ZoneCellIter; + } // namespace gc } // namespace js @@ -157,6 +160,13 @@ struct Zone : public JS::shadow::Zone, onTooMuchMalloc(); } + // Iterate over all cells in the zone. See the definition of ZoneCellIter + // in jsgcinlines.h for the possible arguments and documentation. + template + js::gc::ZoneCellIter cellIter(Args... args) { + return js::gc::ZoneCellIter(const_cast(this), mozilla::Forward(args)...); + } + bool isTooMuchMalloc() const { return gcMallocBytes <= 0; } void onTooMuchMalloc(); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index d649a5fb9ec7..06529002441e 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -488,6 +488,7 @@ BaselineScript::Trace(JSTracer* trc, BaselineScript* script) void BaselineScript::Destroy(FreeOp* fop, BaselineScript* script) { + MOZ_ASSERT(!script->hasPendingIonBuilder()); script->unlinkDependentWasmModules(fop); @@ -1171,8 +1172,7 @@ jit::ToggleBaselineProfiling(JSRuntime* runtime, bool enable) return; for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { - for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (!script->hasBaselineScript()) continue; AutoWritableJitCode awjc(script->baselineScript()->method()); @@ -1186,8 +1186,7 @@ void jit::ToggleBaselineTraceLoggerScripts(JSRuntime* runtime, bool enable) { for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { - for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (!script->hasBaselineScript()) continue; script->baselineScript()->toggleTraceLoggerScripts(runtime, script, enable); @@ -1199,8 +1198,7 @@ void jit::ToggleBaselineTraceLoggerEngine(JSRuntime* runtime, bool enable) { for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) { - for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (!script->hasBaselineScript()) continue; script->baselineScript()->toggleTraceLoggerEngine(enable); diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index ff012116a5f2..78d616f5e1db 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -11,6 +11,7 @@ #include "mozilla/ThreadLocal.h" #include "jscompartment.h" +#include "jsgc.h" #include "jsprf.h" #include "gc/Marking.h" @@ -589,8 +590,8 @@ JitRuntime::Mark(JSTracer* trc, AutoLockForExclusiveAccess& lock) { MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting()); Zone* zone = trc->runtime()->atomsCompartment(lock)->zone(); - for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::JITCODE); !i.done(); i.next()) { - JitCode* code = i.get(); + for (auto i = zone->cellIter(); !i.done(); i.next()) { + JitCode* code = i; TraceRoot(trc, &code, "wrapper"); } } @@ -1351,8 +1352,7 @@ jit::ToggleBarriers(JS::Zone* zone, bool needs) if (!rt->hasJitRuntime()) return; - for (gc::ZoneCellIterUnderGC i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (script->hasIonScript()) script->ionScript()->toggleBarriers(needs); if (script->hasBaselineScript()) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index be7d6213e26c..537cfc4eb06c 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -984,8 +984,8 @@ AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, A // want to delazify in that case: this pointer is weak so the JSScript // could be destroyed at the next GC. - for (gc::ZoneCellIter i(cx->zone(), kind); !i.done(); i.next()) { - JSFunction* fun = &i.get()->as(); + for (auto i = cx->zone()->cellIter(kind); !i.done(); i.next()) { + JSFunction* fun = &i->as(); // Sweeping is incremental; take care to not delazify functions that // are about to be finalized. GC things referenced by objects that are @@ -1157,8 +1157,7 @@ JSCompartment::clearScriptCounts() void JSCompartment::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler) { - for (gc::ZoneCellIter i(zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone()->cellIter(); !script.done(); script.next()) { if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode()) script->clearBreakpointsIn(fop, dbg, handler); } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index d884798c079d..c37269f1b4a4 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2381,15 +2381,10 @@ GCRuntime::sweepTypesAfterCompacting(Zone* zone) AutoClearTypeInferenceStateOnOOM oom(zone); - for (ZoneCellIterUnderGC i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) script->maybeSweepTypes(&oom); - } - - for (ZoneCellIterUnderGC i(zone, AllocKind::OBJECT_GROUP); !i.done(); i.next()) { - ObjectGroup* group = i.get(); + for (auto group = zone->cellIter(); !group.done(); group.next()) group->maybeSweep(&oom); - } zone->types.endSweep(rt); } @@ -3981,10 +3976,11 @@ GCRuntime::checkForCompartmentMismatches() return; CompartmentCheckTracer trc(rt); + AutoAssertEmptyNursery empty(rt); for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { trc.zone = zone; for (auto thingKind : AllAllocKinds()) { - for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) { + for (auto i = zone->cellIter(thingKind, empty); !i.done(); i.next()) { trc.src = i.getCell(); trc.srcKind = MapAllocToTraceKind(thingKind); trc.compartment = DispatchTraceKindTyped(MaybeCompartmentFunctor(), @@ -4003,9 +3999,10 @@ RelazifyFunctions(Zone* zone, AllocKind kind) kind == AllocKind::FUNCTION_EXTENDED); JSRuntime* rt = zone->runtimeFromMainThread(); + AutoAssertEmptyNursery empty(rt); - for (ZoneCellIterUnderGC i(zone, kind); !i.done(); i.next()) { - JSFunction* fun = &i.get()->as(); + for (auto i = zone->cellIter(kind, empty); !i.done(); i.next()) { + JSFunction* fun = &i->as(); if (fun->hasScript()) fun->maybeRelazify(rt); } @@ -6919,8 +6916,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) RootedObject targetStaticGlobalLexicalScope(rt); targetStaticGlobalLexicalScope = &target->maybeGlobal()->lexicalScope().staticBlock(); - for (ZoneCellIter iter(source->zone(), AllocKind::SCRIPT); !iter.done(); iter.next()) { - JSScript* script = iter.get(); + for (auto script = source->zone()->cellIter(); !script.done(); script.next()) { MOZ_ASSERT(script->compartment() == source); script->compartment_ = target; script->setTypesGeneration(target->zone()->types.generation); @@ -6952,14 +6948,12 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) } } - for (ZoneCellIter iter(source->zone(), AllocKind::BASE_SHAPE); !iter.done(); iter.next()) { - BaseShape* base = iter.get(); + for (auto base = source->zone()->cellIter(); !base.done(); base.next()) { MOZ_ASSERT(base->compartment() == source); base->compartment_ = target; } - for (ZoneCellIter iter(source->zone(), AllocKind::OBJECT_GROUP); !iter.done(); iter.next()) { - ObjectGroup* group = iter.get(); + for (auto group = source->zone()->cellIter(); !group.done(); group.next()) { group->setGeneration(target->zone()->types.generation); group->compartment_ = target; @@ -6981,8 +6975,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) // After fixing JSFunctions' compartments, we can fix LazyScripts' // enclosing scopes. - for (ZoneCellIter iter(source->zone(), AllocKind::LAZY_SCRIPT); !iter.done(); iter.next()) { - LazyScript* lazy = iter.get(); + for (auto lazy = source->zone()->cellIter(); !lazy.done(); lazy.next()) { MOZ_ASSERT(lazy->functionNonDelazifying()->compartment() == target); // See warning in handleParseWorkload. If we start optimizing global @@ -7116,12 +7109,9 @@ js::ReleaseAllJITCode(FreeOp* fop) void js::PurgeJITCaches(Zone* zone) { - for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); - - /* Discard Ion caches. */ + /* Discard Ion caches. */ + for (auto script = zone->cellIter(); !script.done(); script.next()) jit::PurgeCaches(script); - } } void @@ -7391,8 +7381,7 @@ js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt) for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { zone->checkUniqueIdTableAfterMovingGC(); - for (ZoneCellIterUnderGC i(zone, AllocKind::BASE_SHAPE); !i.done(); i.next()) { - BaseShape* baseShape = i.get(); + for (auto baseShape = zone->cellIter(); !baseShape.done(); baseShape.next()) { if (baseShape->hasTable()) baseShape->table().checkAfterMovingGC(); } @@ -7870,5 +7859,28 @@ StateName(State state) return names[state]; } +void +AutoAssertHeapBusy::checkCondition(JSRuntime *rt) +{ + this->rt = rt; + MOZ_ASSERT(rt->isHeapBusy()); +} + +void +AutoAssertEmptyNursery::checkCondition(JSRuntime *rt) { + this->rt = rt; + MOZ_ASSERT(rt->gc.nursery.isEmpty()); +} + +AutoEmptyNursery::AutoEmptyNursery(JSRuntime *rt) + : AutoAssertEmptyNursery() +{ + MOZ_ASSERT(!rt->mainThread.suppressGC); + rt->gc.stats.suspendPhases(); + rt->gc.evictNursery(); + rt->gc.stats.resumePhases(); + checkCondition(rt); +} + } /* namespace gc */ } /* namespace js */ diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 9e2a8b5a7aca..9ebc3c4ff249 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1328,6 +1328,95 @@ struct MOZ_RAII AutoAssertNoNurseryAlloc #endif }; +/* + * There are a couple of classes here that serve mostly as "tokens" indicating + * that a condition holds. Some functions force the caller to possess such a + * token because they would misbehave if the condition were false, and it is + * far more clear to make the condition visible at the point where it can be + * affected rather than just crashing in an assertion down in the place where + * it is relied upon. + */ + +/* + * Token meaning that the heap is busy and no allocations will be made. + * + * This class may be instantiated directly if it is known that the condition is + * already true, or it can be used as a base class for another RAII class that + * causes the condition to become true. Such base classes will use the no-arg + * constructor, establish the condition, then call checkCondition() to assert + * it and possibly record data needed to re-check the condition during + * destruction. + * + * Ordinarily, you would do something like this with a Maybe<> member that is + * emplaced during the constructor, but token-requiring functions want to + * require a reference to a base class instance. That said, you can always pass + * in the Maybe<> field as the token. + */ +class MOZ_RAII AutoAssertHeapBusy { + protected: + JSRuntime* rt; + + // Check that the heap really is busy, and record the rt for the check in + // the destructor. + void checkCondition(JSRuntime *rt); + + AutoAssertHeapBusy() : rt(nullptr) { + } + + public: + explicit AutoAssertHeapBusy(JSRuntime* rt) { + checkCondition(rt); + } + + ~AutoAssertHeapBusy() { + MOZ_ASSERT(rt); // checkCondition must always be called. + checkCondition(rt); + } +}; + +/* + * A class that serves as a token that the nursery is empty. It descends from + * AutoAssertHeapBusy, which means that it additionally requires the heap to be + * busy (which is not necessarily linked, but turns out to be true in practice + * for all users and simplifies the usage of these classes.) + */ +class MOZ_RAII AutoAssertEmptyNursery +{ + protected: + JSRuntime* rt; + + // Check that the nursery is empty. + void checkCondition(JSRuntime *rt); + + // For subclasses that need to empty the nursery in their constructors. + AutoAssertEmptyNursery() : rt(nullptr) { + } + + public: + explicit AutoAssertEmptyNursery(JSRuntime* rt) { + checkCondition(rt); + } + + ~AutoAssertEmptyNursery() { + checkCondition(rt); + } +}; + +/* + * Evict the nursery upon construction. Serves as a token indicating that the + * nursery is empty. (See AutoAssertEmptyNursery, above.) + * + * Note that this is very improper subclass of AutoAssertHeapBusy, in that the + * heap is *not* busy within the scope of an AutoEmptyNursery. I will most + * likely fix this by removing AutoAssertHeapBusy, but that is currently + * waiting on jonco's review. + */ +class MOZ_RAII AutoEmptyNursery : public AutoAssertEmptyNursery +{ + public: + explicit AutoEmptyNursery(JSRuntime *rt); +}; + const char* StateName(State state); diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index e9687ea71cc9..6b710a85f5bf 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -195,39 +195,71 @@ class ArenaCellIterUnderFinalize : public ArenaCellIterImpl explicit ArenaCellIterUnderFinalize(Arena* arena) : ArenaCellIterImpl(arena) {} }; -class ZoneCellIterImpl -{ +template +class ZoneCellIter; + +template <> +class ZoneCellIter { ArenaIter arenaIter; ArenaCellIterImpl cellIter; + JS::AutoAssertNoAlloc noAlloc; - public: - ZoneCellIterImpl(JS::Zone* zone, AllocKind kind) { + protected: + // For use when a subclass wants to insert some setup before init(). + ZoneCellIter() {} + + void init(JS::Zone* zone, AllocKind kind) { JSRuntime* rt = zone->runtimeFromAnyThread(); MOZ_ASSERT(zone); MOZ_ASSERT_IF(IsNurseryAllocable(kind), rt->gc.nursery.isEmpty()); + // If called from outside a GC, ensure that the heap is in a state + // that allows us to iterate. + if (!rt->isHeapBusy()) { + // Assert that no GCs can occur while a ZoneCellIter is live. + noAlloc.disallowAlloc(rt); + } + // We have a single-threaded runtime, so there's no need to protect // against other threads iterating or allocating. However, we do have // background finalization; we may have to wait for this to finish if // it's currently active. if (IsBackgroundFinalized(kind) && zone->arenas.needBackgroundFinalizeWait(kind)) rt->gc.waitBackgroundSweepEnd(); - arenaIter.init(zone, kind); if (!arenaIter.done()) cellIter.init(arenaIter.get()); } + public: + ZoneCellIter(JS::Zone* zone, AllocKind kind) { + // If we are iterating a nursery-allocated kind then we need to + // evict first so that we can see all things. + if (IsNurseryAllocable(kind)) { + JSRuntime* rt = zone->runtimeFromMainThread(); + rt->gc.evictNursery(); + } + + init(zone, kind); + } + + ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery&) { + // No need to evict the nursery. (This constructor is known statically + // to not GC.) + init(zone, kind); + } + bool done() const { return arenaIter.done(); } - template T* get() const { + template + T* get() const { MOZ_ASSERT(!done()); return cellIter.get(); } - Cell* getCell() const { + TenuredCell* getCell() const { MOZ_ASSERT(!done()); return cellIter.getCell(); } @@ -244,44 +276,74 @@ class ZoneCellIterImpl } }; -class ZoneCellIterUnderGC : public ZoneCellIterImpl -{ +// Iterator over the cells in a Zone, where the GC type (JSString, JSObject) is +// known, for a single AllocKind. Example usages: +// +// for (auto obj = zone->cellIter(AllocKind::OBJECT0); !obj.done(); obj.next()) +// ... +// +// for (auto script = zone->cellIter(); !script.done(); script.next()) +// f(script->code()); +// +// As this code demonstrates, you can use 'script' as if it were a JSScript*. +// Its actual type is ZoneCellIter, but for most purposes it will +// autoconvert to JSScript*. +// +// Note that in the JSScript case, ZoneCellIter is able to infer the AllocKind +// from the type 'JSScript', whereas in the JSObject case, the kind must be +// given (because there are multiple AllocKinds for objects). +// +// Also, the static rooting hazard analysis knows that the JSScript case will +// not GC during construction. The JSObject case needs to GC, or more precisely +// to empty the nursery and clear out the store buffer, so that it can see all +// objects to iterate over (the nursery is not iterable) and remove the +// possibility of having pointers from the store buffer to data hanging off +// stuff we're iterating over that we are going to delete. (The latter should +// not be a problem, since such instances should be using RelocatablePtr do +// remove themselves from the store buffer on deletion, but currently for +// subtle reasons that isn't good enough.) +// +// If the iterator is used within a GC, then there is no need to evict the +// nursery (again). You may select a variant that will skip the eviction either +// by specializing on a GCType that is never allocated in the nursery, or +// explicitly by passing in a trailing AutoAssertEmptyNursery argument. +// +template +class ZoneCellIter : public ZoneCellIter { public: - ZoneCellIterUnderGC(JS::Zone* zone, AllocKind kind) - : ZoneCellIterImpl(zone, kind) + // Non-nursery allocated (equivalent to having an entry in + // MapTypeToFinalizeKind). The template declaration here is to discard this + // constructor overload if MapTypeToFinalizeKind::kind does not + // exist. Note that there will be no remaining overloads that will work, + // which makes sense given that you haven't specified which of the + // AllocKinds to use for GCType. + // + // If we later add a nursery allocable GCType with a single AllocKind, we + // will want to add an overload of this constructor that does the right + // thing (ie, it empties the nursery before iterating.) + explicit ZoneCellIter(JS::Zone* zone) : ZoneCellIter() { + init(zone, MapTypeToFinalizeKind::kind); + } + + // Non-nursery allocated, nursery is known to be empty: same behavior as above. + ZoneCellIter(JS::Zone* zone, const js::gc::AutoAssertEmptyNursery&) : ZoneCellIter(zone) { + } + + // Arbitrary kind, which will be assumed to be nursery allocable (and + // therefore the nursery will be emptied before iterating.) + ZoneCellIter(JS::Zone* zone, AllocKind kind) : ZoneCellIter(zone, kind) { + } + + // Arbitrary kind, which will be assumed to be nursery allocable, but the + // nursery is known to be empty already: same behavior as non-nursery types. + ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery& empty) + : ZoneCellIter(zone, kind, empty) { - MOZ_ASSERT(zone->runtimeFromAnyThread()->isHeapBusy()); - } -}; - -class ZoneCellIter -{ - mozilla::Maybe impl; - JS::AutoAssertNoAlloc noAlloc; - - public: - ZoneCellIter(JS::Zone* zone, AllocKind kind) { - // If called from outside a GC, ensure that the heap is in a state - // that allows us to iterate. - JSRuntime* rt = zone->runtimeFromMainThread(); - if (!rt->isHeapBusy()) { - // If we are iterating a nursery-allocated kind then we need to - // evict first so that we can see all things. - if (IsNurseryAllocable(kind)) - rt->gc.evictNursery(); - - // Assert that no GCs can occur while a ZoneCellIter is live. - noAlloc.disallowAlloc(rt); - } - - impl.emplace(zone, kind); } - bool done() const { return impl->done(); } - template - T* get() const { return impl->get(); } - Cell* getCell() const { return impl->getCell(); } - void next() { impl->next(); } + GCType* get() const { return ZoneCellIter::get(); } + operator GCType*() const { return get(); } + GCType* operator ->() const { return get(); } }; class GCZonesIter diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 4514a3b73ca4..aee66c49f59a 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -201,8 +201,9 @@ js::DumpPCCounts(JSContext* cx, HandleScript script, Sprinter* sp) void js::DumpCompartmentPCCounts(JSContext* cx) { - for (ZoneCellIter i(cx->zone(), gc::AllocKind::SCRIPT); !i.done(); i.next()) { - RootedScript script(cx, i.get()); + RootedScript script(cx); + for (auto iter = cx->zone()->cellIter(); !iter.done(); iter.next()) { + script = iter; if (script->compartment() != cx->compartment()) continue; @@ -1657,8 +1658,7 @@ js::StopPCCountProfiling(JSContext* cx) return; for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { - for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (script->hasScriptCounts() && script->types()) { if (!vec->append(script)) return; @@ -2025,8 +2025,7 @@ GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out) } Rooted topScripts(cx, ScriptVector(cx)); for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { - for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) { - JSScript* script = i.get(); + for (auto script = zone->cellIter(); !script.done(); script.next()) { if (script->compartment() != comp || !script->isTopLevel() || !script->filename()) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index a97e9ef89a89..642e4e363bfd 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -2352,8 +2352,8 @@ UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone, return false; } } else { - for (gc::ZoneCellIter iter(zone, gc::AllocKind::SCRIPT); !iter.done(); iter.next()) { - JSScript* script = iter.get(); + for (auto iter = zone->cellIter(); !iter.done(); iter.next()) { + JSScript* script = iter; if (obs.shouldRecompileOrInvalidate(script) && !gc::IsAboutToBeFinalizedUnbarriered(&script)) { diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index 5a02869a35dd..c82c2ba82246 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -1271,8 +1271,6 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par LeaveParseTaskZone(rt, parseTask); { - gc::ZoneCellIter iter(parseTask->cx->zone(), gc::AllocKind::OBJECT_GROUP); - // Generator functions don't have Function.prototype as prototype but a // different function object, so the IdentifyStandardPrototype trick // below won't work. Just special-case it. @@ -1288,8 +1286,7 @@ GlobalHelperThreadState::mergeParseTaskCompartment(JSRuntime* rt, ParseTask* par // to the corresponding prototype in the new compartment. This will briefly // create cross compartment pointers, which will be fixed by the // MergeCompartments call below. - for (; !iter.done(); iter.next()) { - ObjectGroup* group = iter.get(); + for (auto group = parseTask->cx->zone()->cellIter(); !group.done(); group.next()) { TaggedProto proto(group->proto()); if (!proto.isObject()) continue; diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index ffa472c6f538..5d33a0224a8d 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1049,7 +1049,7 @@ CallAddPropertyHookDense(ExclusiveContext* cx, HandleNativeObject obj, uint32_t } static bool -UpdateShapeTypeAndValue(ExclusiveContext* cx, NativeObject* obj, Shape* shape, const Value& value) +UpdateShapeTypeAndValue(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape, const Value& value) { jsid id = shape->propid(); if (shape->hasSlot()) { diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index fe6780b03f9f..c78e363fc74c 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -2545,16 +2545,15 @@ js::PrintTypes(JSContext* cx, JSCompartment* comp, bool force) if (!force && !InferSpewActive(ISpewResult)) return; - for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) { - RootedScript script(cx, i.get()); + RootedScript script(cx); + for (auto iter = zone->cellIter(); !iter.done(); iter.next()) { + script = iter; if (script->types()) script->types()->printTypes(cx, script); } - for (gc::ZoneCellIter i(zone, gc::AllocKind::OBJECT_GROUP); !i.done(); i.next()) { - ObjectGroup* group = i.get(); + for (auto group = zone->cellIter(); !group.done(); group.next()) group->print(); - } #endif } @@ -4425,10 +4424,8 @@ TypeZone::endSweep(JSRuntime* rt) void TypeZone::clearAllNewScriptsOnOOM() { - for (gc::ZoneCellIter iter(zone(), gc::AllocKind::OBJECT_GROUP); - !iter.done(); iter.next()) - { - ObjectGroup* group = iter.get(); + for (auto iter = zone()->cellIter(); !iter.done(); iter.next()) { + ObjectGroup* group = iter; if (!IsAboutToBeFinalizedUnbarriered(&group)) group->maybeClearNewScriptOnOOM(); } @@ -4437,8 +4434,9 @@ TypeZone::clearAllNewScriptsOnOOM() AutoClearTypeInferenceStateOnOOM::~AutoClearTypeInferenceStateOnOOM() { if (oom) { + JSRuntime* rt = zone->runtimeFromMainThread(); zone->setPreservingCode(false); - zone->discardJitCode(zone->runtimeFromMainThread()->defaultFreeOp()); + zone->discardJitCode(rt->defaultFreeOp()); zone->types.clearAllNewScriptsOnOOM(); } } From 2e127016b55c3b48f7232791e5b2779282b09ade Mon Sep 17 00:00:00 2001 From: Trevor Saunders Date: Thu, 12 May 2016 18:09:12 -0400 Subject: [PATCH 130/199] bug 1272503 - make alerting for changes in num_constructors more agressive r=froydnj f=wlach There shouldn't be any noise in this number, either a patch adds or removes constructors or it doesn't. Choosing 0.25% is kind of arbitrary, but it should be good enough since it will make us alert for an increase of one constructor so long as we have less than 400 constructors, and currently we have 98. --- build/util/count_ctors.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/util/count_ctors.py b/build/util/count_ctors.py index d65a9534baad..6a3a87068564 100644 --- a/build/util/count_ctors.py +++ b/build/util/count_ctors.py @@ -55,7 +55,8 @@ if __name__ == '__main__': "name": "compiler_metrics", "subtests": [{ "name": "num_constructors", - "value": count_ctors(f) + "value": count_ctors(f), + "alertThreshold": 0.25 }]} ] } From db5849856e48b2d5b5d94f8e9e79dceb5219d570 Mon Sep 17 00:00:00 2001 From: Jordan Lund Date: Tue, 31 May 2016 12:43:15 -0700 Subject: [PATCH 131/199] Bug 1277041 - update migration scripts to support robustcheckout, DONTBUILD r=gps MozReview-Commit-ID: GD3vspSFmTa * fix clean_repos. it expects vcs_config key revision but now we use branch --HG-- extra : rebase_source : d09622ca30eb1c7face42892f149812e5ae5a26a extra : amend_source : cf7491d93ff2ea9ca13929606368fd443c8f026b --- testing/mozharness/configs/merge_day/aurora_to_beta.py | 9 ++++++--- testing/mozharness/configs/merge_day/beta_to_release.py | 9 ++++++--- testing/mozharness/configs/merge_day/bump_esr.py | 6 +++++- .../mozharness/configs/merge_day/central_to_aurora.py | 8 +++++--- testing/mozharness/configs/merge_day/release_to_esr.py | 8 +++++--- .../configs/merge_day/staging_beta_migration.py | 6 +++++- .../mozharness/mozharness/mozilla/repo_manupulation.py | 4 ++-- testing/mozharness/scripts/merge_day/gecko_migration.py | 4 ++-- 8 files changed, 36 insertions(+), 18 deletions(-) diff --git a/testing/mozharness/configs/merge_day/aurora_to_beta.py b/testing/mozharness/configs/merge_day/aurora_to_beta.py index 7038d97c96fd..01b29f475daf 100644 --- a/testing/mozharness/configs/merge_day/aurora_to_beta.py +++ b/testing/mozharness/configs/merge_day/aurora_to_beta.py @@ -1,3 +1,7 @@ +import os + +ABS_WORK_DIR = os.path.join(os.getcwd(), "build") + config = { "log_name": "aurora_to_beta", "version_files": [ @@ -57,11 +61,10 @@ config = { "# Enable enforcing that add-ons are signed by the trusted root") ], - # Disallow sharing, since we want pristine .hg directories. - # "vcs_share_base": None, + "vcs_share_base": os.path.join(ABS_WORK_DIR, 'hg-shared'), # "hg_share_base": None, "tools_repo_url": "https://hg.mozilla.org/build/tools", - "tools_repo_revision": "default", + "tools_repo_branch": "default", "from_repo_url": "ssh://hg.mozilla.org/releases/mozilla-aurora", "to_repo_url": "ssh://hg.mozilla.org/releases/mozilla-beta", diff --git a/testing/mozharness/configs/merge_day/beta_to_release.py b/testing/mozharness/configs/merge_day/beta_to_release.py index f2da46bf0f82..497db5b7dcfd 100644 --- a/testing/mozharness/configs/merge_day/beta_to_release.py +++ b/testing/mozharness/configs/merge_day/beta_to_release.py @@ -1,3 +1,7 @@ +import os + +ABS_WORK_DIR = os.path.join(os.getcwd(), "build") + config = { "log_name": "beta_to_release", "copy_files": [ @@ -25,11 +29,10 @@ config = { "MAR_CHANNEL_ID=firefox-mozilla-release"), ], - # Disallow sharing, since we want pristine .hg directories. - # "vcs_share_base": None, + "vcs_share_base": os.path.join(ABS_WORK_DIR, 'hg-shared'), # "hg_share_base": None, "tools_repo_url": "https://hg.mozilla.org/build/tools", - "tools_repo_revision": "default", + "tools_repo_branch": "default", "from_repo_url": "ssh://hg.mozilla.org/releases/mozilla-beta", "to_repo_url": "ssh://hg.mozilla.org/releases/mozilla-release", diff --git a/testing/mozharness/configs/merge_day/bump_esr.py b/testing/mozharness/configs/merge_day/bump_esr.py index 2a6bbf15e5e9..3b25abb9d45a 100644 --- a/testing/mozharness/configs/merge_day/bump_esr.py +++ b/testing/mozharness/configs/merge_day/bump_esr.py @@ -1,4 +1,8 @@ +import os + +ABS_WORK_DIR = os.path.join(os.getcwd(), "build") config = { + "vcs_share_base": os.path.join(ABS_WORK_DIR, 'hg-shared'), "log_name": "bump_esr", "version_files": [ {"file": "browser/config/version.txt", "suffix": ""}, @@ -6,7 +10,7 @@ config = { {"file": "config/milestone.txt", "suffix": ""}, ], "tools_repo_url": "https://hg.mozilla.org/build/tools", - "tools_repo_revision": "default", + "tools_repo_branch": "default", "to_repo_url": "ssh://hg.mozilla.org/releases/mozilla-esr45", "migration_behavior": "bump_second_digit", diff --git a/testing/mozharness/configs/merge_day/central_to_aurora.py b/testing/mozharness/configs/merge_day/central_to_aurora.py index b791d3beac27..b13753a77ce1 100644 --- a/testing/mozharness/configs/merge_day/central_to_aurora.py +++ b/testing/mozharness/configs/merge_day/central_to_aurora.py @@ -1,3 +1,6 @@ +import os + +ABS_WORK_DIR = os.path.join(os.getcwd(), "build") config = { "log_name": "central_to_aurora", "version_files": [ @@ -66,11 +69,10 @@ config = { "mobile/android/locales/all-locales" ], - # Disallow sharing, since we want pristine .hg directories. - # "vcs_share_base": None, + "vcs_share_base": os.path.join(ABS_WORK_DIR, 'hg-shared'), # "hg_share_base": None, "tools_repo_url": "https://hg.mozilla.org/build/tools", - "tools_repo_revision": "default", + "tools_repo_branch": "default", "from_repo_url": "ssh://hg.mozilla.org/mozilla-central", "to_repo_url": "ssh://hg.mozilla.org/releases/mozilla-aurora", diff --git a/testing/mozharness/configs/merge_day/release_to_esr.py b/testing/mozharness/configs/merge_day/release_to_esr.py index 0b6a6df819ff..d9f260ad5770 100644 --- a/testing/mozharness/configs/merge_day/release_to_esr.py +++ b/testing/mozharness/configs/merge_day/release_to_esr.py @@ -1,3 +1,6 @@ +import os + +ABS_WORK_DIR = os.path.join(os.getcwd(), "build") NEW_ESR_REPO = "ssh://hg.mozilla.org/releases/mozilla-esr45" OLD_ESR_REPO = "https://hg.mozilla.org/releases/mozilla-esr38" OLD_ESR_CHANGESET = "16351963d75c" @@ -18,11 +21,10 @@ config = { "MAR_CHANNEL_ID=firefox-mozilla-release", "MAR_CHANNEL_ID=firefox-mozilla-esr"), ], - # Disallow sharing, since we want pristine .hg directories. - # "vcs_share_base": None, + "vcs_share_base": os.path.join(ABS_WORK_DIR, 'hg-shared'), # "hg_share_base": None, "tools_repo_url": "https://hg.mozilla.org/build/tools", - "tools_repo_revision": "default", + "tools_repo_branch": "default", "from_repo_url": "ssh://hg.mozilla.org/releases/mozilla-release", "to_repo_url": NEW_ESR_REPO, diff --git a/testing/mozharness/configs/merge_day/staging_beta_migration.py b/testing/mozharness/configs/merge_day/staging_beta_migration.py index de9bfc3cbf01..9b6ac198e3cc 100644 --- a/testing/mozharness/configs/merge_day/staging_beta_migration.py +++ b/testing/mozharness/configs/merge_day/staging_beta_migration.py @@ -2,12 +2,16 @@ # mozharness/scripts/merge_day/gecko_migration.py -c \ # mozharness/configs/merge_day/aurora_to_beta.py -c # mozharness/configs/merge_day/staging_beta_migration.py ... +import os + +ABS_WORK_DIR = os.path.join(os.getcwd(), "build") config = { "log_name": "staging_beta", + "vcs_share_base": os.path.join(ABS_WORK_DIR, 'hg-shared'), "tools_repo_url": "https://hg.mozilla.org/build/tools", - "tools_repo_revision": "default", + "tools_repo_branch": "default", "from_repo_url": "ssh://hg.mozilla.org/releases/mozilla-aurora", "to_repo_url": "ssh://hg.mozilla.org/users/stage-ffxbld/mozilla-beta", diff --git a/testing/mozharness/mozharness/mozilla/repo_manupulation.py b/testing/mozharness/mozharness/mozilla/repo_manupulation.py index 0a3f8c2142de..a2dfc46a2f43 100644 --- a/testing/mozharness/mozharness/mozilla/repo_manupulation.py +++ b/testing/mozharness/mozharness/mozilla/repo_manupulation.py @@ -71,7 +71,7 @@ class MercurialRepoManipulationMixin(object): if os.path.exists(repo_path): # hg up -C to discard uncommitted changes self.run_command( - hg + ["up", "-C", "-r", repo_config['revision']], + hg + ["up", "-C", "-r", repo_config['branch']], cwd=repo_path, error_list=HgErrorList, halt_on_failure=True, @@ -93,7 +93,7 @@ class MercurialRepoManipulationMixin(object): # 2nd hg up -C to make sure we're not on a stranded head # which can happen when reverting debugsetparents self.run_command( - hg + ["up", "-C", "-r", repo_config['revision']], + hg + ["up", "-C", "-r", repo_config['branch']], cwd=repo_path, error_list=HgErrorList, halt_on_failure=True, diff --git a/testing/mozharness/scripts/merge_day/gecko_migration.py b/testing/mozharness/scripts/merge_day/gecko_migration.py index 37dc70ca1b0c..0be17505fe59 100755 --- a/testing/mozharness/scripts/merge_day/gecko_migration.py +++ b/testing/mozharness/scripts/merge_day/gecko_migration.py @@ -153,7 +153,7 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, if url: self.gecko_repos.append({ "repo": url, - "revision": self.config.get("%s_repo_revision", "default"), + "branch": self.config.get("%s_repo_branch" % (k,), "default"), "dest": dirs['abs_%s_dir' % k], "vcs": "hg", }) @@ -462,7 +462,7 @@ class GeckoMigration(MercurialScript, BalrogMixin, VirtualenvMixin, """ repos = [{ "repo": self.config["tools_repo_url"], - "revision": self.config["tools_repo_revision"], + "branch": self.config["tools_repo_branch"], "dest": "tools", "vcs": "hg", }] + self.query_repos() From 4bd9943d0477ddc89bd5b4d620a1da39a0cb9160 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Mon, 3 Aug 2015 19:24:35 -0400 Subject: [PATCH 132/199] bug 1186948 - remove plugins that are click-to-play from navigator.plugins (restricted to Flash) r=jst --- browser/modules/PluginContent.jsm | 52 ++++++++++++++++++++++++++++++ dom/base/nsPluginArray.cpp | 41 +++++++++++++++++++++-- dom/base/nsPluginArray.h | 4 +++ dom/plugins/base/nsIPluginHost.idl | 12 +++++++ dom/plugins/base/nsPluginHost.cpp | 14 ++++++-- dom/plugins/base/nsPluginTags.cpp | 2 +- dom/plugins/base/nsPluginTags.h | 6 ++++ 7 files changed, 124 insertions(+), 7 deletions(-) diff --git a/browser/modules/PluginContent.jsm b/browser/modules/PluginContent.jsm index fea664163b4c..0d93f970cf10 100644 --- a/browser/modules/PluginContent.jsm +++ b/browser/modules/PluginContent.jsm @@ -53,6 +53,8 @@ PluginContent.prototype = { global.addMessageListener("BrowserPlugins:NPAPIPluginProcessCrashed", this); global.addMessageListener("BrowserPlugins:CrashReportSubmitted", this); global.addMessageListener("BrowserPlugins:Test:ClearCrashData", this); + + Services.obs.addObserver(this, "Plugin::HiddenPluginTouched", false); }, uninit: function() { @@ -75,6 +77,8 @@ PluginContent.prototype = { global.removeMessageListener("BrowserPlugins:Test:ClearCrashData", this); delete this.global; delete this.content; + + Services.obs.removeObserver(this, "Plugin::HiddenPluginTouched"); }, receiveMessage: function (msg) { @@ -116,6 +120,15 @@ PluginContent.prototype = { } }, + observe: function observe(aSubject, aTopic, aData) { + let pluginTag = aSubject; + if (aTopic == "Plugin::HiddenPluginTouched") { + this._showClickToPlayNotification(pluginTag, false); + } else { + Cu.reportError("unknown topic observed: " + aTopic); + } + }, + onPageShow: function (event) { // Ignore events that aren't from the main document. if (!this.content || event.target != this.content.document) { @@ -194,6 +207,45 @@ PluginContent.prototype = { }; }, + _getPluginInfoForTag: function (pluginTag, tagMimetype, fallbackType) { + let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); + + let pluginName = gNavigatorBundle.GetStringFromName("pluginInfo.unknownPlugin"); + let permissionString = null; + let blocklistState = null; + + if (pluginTag) { + pluginName = BrowserUtils.makeNicePluginName(pluginTag.name); + + permissionString = pluginHost.getPermissionStringForTag(pluginTag); + blocklistState = pluginTag.blocklistState; + + // Convert this from nsIPluginTag so it can be serialized. + let properties = ["name", "description", "filename", "version", "enabledState", "niceName"]; + let pluginTagCopy = {}; + for (let prop of properties) { + pluginTagCopy[prop] = pluginTag[prop]; + } + pluginTag = pluginTagCopy; + + // Make state-softblocked == state-notblocked for our purposes, + // they have the same UI. STATE_OUTDATED should not exist for plugin + // items, but let's alias it anyway, just in case. + if (blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED || + blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) { + blocklistState = Ci.nsIBlocklistService.STATE_NOT_BLOCKED; + } + } + + return { mimetype: tagMimetype, + pluginName: pluginName, + pluginTag: pluginTag, + permissionString: permissionString, + fallbackType: fallbackType, + blocklistState: blocklistState, + }; + }, + /** * Update the visibility of the plugin overlay. */ diff --git a/dom/base/nsPluginArray.cpp b/dom/base/nsPluginArray.cpp index b6630c78b333..610b32bfe973 100644 --- a/dom/base/nsPluginArray.cpp +++ b/dom/base/nsPluginArray.cpp @@ -19,6 +19,8 @@ #include "nsIWeakReference.h" #include "mozilla/Services.h" #include "nsIInterfaceRequestorUtils.h" +#include "nsIPermissionManager.h" +#include "nsIDocument.h" using namespace mozilla; using namespace mozilla::dom; @@ -66,7 +68,8 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsPluginArray, mWindow, - mPlugins) + mPlugins, + mCTPPlugins) static void GetPluginMimeTypes(const nsTArray >& aPlugins, @@ -146,6 +149,7 @@ nsPluginArray::Refresh(bool aReloadDocuments) } mPlugins.Clear(); + mCTPPlugins.Clear(); nsCOMPtr navigator = mWindow->GetNavigator(); @@ -221,6 +225,13 @@ nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound) nsPluginElement* plugin = FindPlugin(mPlugins, aName); aFound = (plugin != nullptr); + if (!aFound) { + nsPluginElement* hiddenPlugin = FindPlugin(mCTPPlugins, aName); + if (hiddenPlugin) { + nsCOMPtr obs = mozilla::services::GetObserverService(); + obs->NotifyObservers(hiddenPlugin->PluginTag(), "Plugin::HiddenPluginTouched", nsString(aName).get()); + } + } return plugin; } @@ -282,7 +293,7 @@ operator<(const RefPtr& lhs, void nsPluginArray::EnsurePlugins() { - if (!mPlugins.IsEmpty()) { + if (!mPlugins.IsEmpty() || !mCTPPlugins.IsEmpty()) { // We already have an array of plugin elements. return; } @@ -299,7 +310,31 @@ nsPluginArray::EnsurePlugins() // need to wrap each of these with a nsPluginElement, which is // scriptable. for (uint32_t i = 0; i < pluginTags.Length(); ++i) { - mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i])); + nsCOMPtr pluginTag = do_QueryInterface(pluginTags[i]); + if (!pluginTag) { + mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i])); + } else if (pluginTag->IsActive()) { + uint32_t permission = nsIPermissionManager::ALLOW_ACTION; + if (pluginTag->IsClicktoplay()) { + nsCString name; + pluginTag->GetName(name); + if (NS_LITERAL_CSTRING("Shockwave Flash").Equals(name)) { + RefPtr pluginHost = nsPluginHost::GetInst(); + nsCString permString; + nsresult rv = pluginHost->GetPermissionStringForTag(pluginTag, 0, permString); + if (rv == NS_OK) { + nsIPrincipal* principal = mWindow->GetExtantDoc()->NodePrincipal(); + nsCOMPtr permMgr = services::GetPermissionManager(); + permMgr->TestPermissionFromPrincipal(principal, permString.get(), &permission); + } + } + } + if (permission == nsIPermissionManager::ALLOW_ACTION) { + mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i])); + } else { + mCTPPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i])); + } + } } // Alphabetize the enumeration order of non-hidden plugins to reduce diff --git a/dom/base/nsPluginArray.h b/dom/base/nsPluginArray.h index 526bb73b6769..8e02c9a711ac 100644 --- a/dom/base/nsPluginArray.h +++ b/dom/base/nsPluginArray.h @@ -60,6 +60,10 @@ private: nsCOMPtr mWindow; nsTArray > mPlugins; + /* A separate list of click-to-play plugins that we don't tell content + * about but keep track of so we can still prompt the user to click to play. + */ + nsTArray > mCTPPlugins; }; class nsPluginElement final : public nsISupports, diff --git a/dom/plugins/base/nsIPluginHost.idl b/dom/plugins/base/nsIPluginHost.idl index ae349bea2a26..d1124aa25959 100644 --- a/dom/plugins/base/nsIPluginHost.idl +++ b/dom/plugins/base/nsIPluginHost.idl @@ -99,6 +99,18 @@ interface nsIPluginHost : nsISupports ACString getPermissionStringForType(in AUTF8String mimeType, [optional] in uint32_t excludeFlags); + /** + * Get the "permission string" for the plugin. This is a string that can be + * passed to the permission manager to see whether the plugin is allowed to + * run, for example. This will typically be based on the plugin's "nice name" + * and its blocklist state. + * + * @tag The tage we're interested in + * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE. + */ + ACString getPermissionStringForTag(in nsIPluginTag tag, + [optional] in uint32_t excludeFlags); + /** * Get the nsIPluginTag for this MIME type. This method works with both * enabled and disabled/blocklisted plugins, but an enabled plugin will diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index ad0815d4edd8..fbb565730f89 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -1110,11 +1110,19 @@ nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsresult rv = GetPluginTagForType(aMimeType, aExcludeFlags, getter_AddRefs(tag)); NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(tag, NS_ERROR_FAILURE); + return GetPermissionStringForTag(tag, aExcludeFlags, aPermissionString); +} + +NS_IMETHODIMP +nsPluginHost::GetPermissionStringForTag(nsIPluginTag* aTag, + uint32_t aExcludeFlags, + nsACString &aPermissionString) +{ + NS_ENSURE_TRUE(aTag, NS_ERROR_FAILURE); aPermissionString.Truncate(); uint32_t blocklistState; - rv = tag->GetBlocklistState(&blocklistState); + nsresult rv = aTag->GetBlocklistState(&blocklistState); NS_ENSURE_SUCCESS(rv, rv); if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE || @@ -1126,7 +1134,7 @@ nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, } nsCString niceName; - rv = tag->GetNiceName(niceName); + rv = aTag->GetNiceName(niceName); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(!niceName.IsEmpty(), NS_ERROR_FAILURE); diff --git a/dom/plugins/base/nsPluginTags.cpp b/dom/plugins/base/nsPluginTags.cpp index 326b6ddd91e6..1620935ecb3b 100644 --- a/dom/plugins/base/nsPluginTags.cpp +++ b/dom/plugins/base/nsPluginTags.cpp @@ -323,7 +323,7 @@ nsPluginTag::~nsPluginTag() NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349"); } -NS_IMPL_ISUPPORTS(nsPluginTag, nsIPluginTag, nsIInternalPluginTag) +NS_IMPL_ISUPPORTS(nsPluginTag, nsPluginTag, nsIInternalPluginTag, nsIPluginTag) void nsPluginTag::InitMime(const char* const* aMimeTypes, const char* const* aMimeDescriptions, diff --git a/dom/plugins/base/nsPluginTags.h b/dom/plugins/base/nsPluginTags.h index 9d7a5259b6e8..08bd88f880de 100644 --- a/dom/plugins/base/nsPluginTags.h +++ b/dom/plugins/base/nsPluginTags.h @@ -31,6 +31,9 @@ struct FakePluginTagInit; { 0xe8fdd227, 0x27da, 0x46ee, \ { 0xbe, 0xf3, 0x1a, 0xef, 0x5a, 0x8f, 0xc5, 0xb4 } } +#define NS_PLUGINTAG_IID \ + { 0xcce2e8b9, 0x9702, 0x4d4b, \ + { 0xbe, 0xa4, 0x7c, 0x1e, 0x13, 0x1f, 0xaf, 0x78 } } class nsIInternalPluginTag : public nsIPluginTag { public: @@ -91,6 +94,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIInternalPluginTag, NS_IINTERNALPLUGINTAG_IID) class nsPluginTag final : public nsIInternalPluginTag { public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_PLUGINTAG_IID) + NS_DECL_ISUPPORTS NS_DECL_NSIPLUGINTAG @@ -193,6 +198,7 @@ private: static uint32_t sNextId; }; +NS_DEFINE_STATIC_IID_ACCESSOR(nsPluginTag, NS_PLUGINTAG_IID) // A class representing "fake" plugin tags; that is plugin tags not // corresponding to actual NPAPI plugins. In practice these are all From 79fb27c7a11f354243c112aa6231428f73f7ef33 Mon Sep 17 00:00:00 2001 From: Chris Manchester Date: Tue, 31 May 2016 10:34:05 -0700 Subject: [PATCH 133/199] Bug 1274090 - Attempt to convert str objects containing non-ascii characters in Python configure rather than failing outright. r=glandium,gps MozReview-Commit-ID: CxFqFXS7Dh9 --HG-- extra : rebase_source : cd5136461756248335245d433b579dd06d266c97 --- .../mozbuild/mozbuild/configure/__init__.py | 19 +++++++++++- python/mozbuild/mozbuild/configure/util.py | 31 +++++++++++-------- .../test/configure/data/subprocess.configure | 23 ++++++++++++++ .../mozbuild/test/configure/test_util.py | 30 ++++++++++++++++++ 4 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 python/mozbuild/mozbuild/test/configure/data/subprocess.configure diff --git a/python/mozbuild/mozbuild/configure/__init__.py b/python/mozbuild/mozbuild/configure/__init__.py index 920b073699e4..e13850e065bd 100644 --- a/python/mozbuild/mozbuild/configure/__init__.py +++ b/python/mozbuild/mozbuild/configure/__init__.py @@ -25,6 +25,7 @@ from mozbuild.configure.options import ( from mozbuild.configure.help import HelpFormatter from mozbuild.configure.util import ( ConfigureOutputHandler, + getpreferredencoding, LineIO, ) from mozbuild.util import ( @@ -157,8 +158,24 @@ class ConfigureSandbox(dict): def queue_debug(): yield + # Some callers will manage to log a bytestring with characters in it + # that can't be converted to ascii. Make our log methods robust to this + # by detecting the encoding that a producer is likely to have used. + encoding = getpreferredencoding() + def wrapped_log_method(logger, key): + method = getattr(logger, key) + if not encoding: + return method + def wrapped(*args, **kwargs): + out_args = [ + arg.decode(encoding) if isinstance(arg, str) else arg + for arg in args + ] + return method(*out_args, **kwargs) + return wrapped + log_namespace = { - k: getattr(logger, k) + k: wrapped_log_method(logger, k) for k in ('debug', 'info', 'warning', 'error') } log_namespace['queue_debug'] = queue_debug diff --git a/python/mozbuild/mozbuild/configure/util.py b/python/mozbuild/mozbuild/configure/util.py index a49e9b8a2dd4..c7a305282b7f 100644 --- a/python/mozbuild/mozbuild/configure/util.py +++ b/python/mozbuild/mozbuild/configure/util.py @@ -14,6 +14,20 @@ from collections import deque from contextlib import contextmanager from distutils.version import LooseVersion +def getpreferredencoding(): + # locale._parse_localename makes locale.getpreferredencoding + # return None when LC_ALL is C, instead of e.g. 'US-ASCII' or + # 'ANSI_X3.4-1968' when it uses nl_langinfo. + encoding = None + try: + encoding = locale.getpreferredencoding() + except ValueError: + # On english OSX, LC_ALL is UTF-8 (not en-US.UTF-8), and + # that throws off locale._parse_localename, which ends up + # being used on e.g. homebrew python. + if os.environ.get('LC_ALL', '').upper() == 'UTF-8': + encoding = 'utf-8' + return encoding class Version(LooseVersion): '''A simple subclass of distutils.version.LooseVersion. @@ -70,19 +84,7 @@ class ConfigureOutputHandler(logging.Handler): isatty = True if not isatty: - encoding = None - try: - encoding = locale.getpreferredencoding() - except ValueError: - # On english OSX, LC_ALL is UTF-8 (not en-US.UTF-8), and - # that throws off locale._parse_localename, which ends up - # being used on e.g. homebrew python. - if os.environ.get('LC_ALL', '').upper() == 'UTF-8': - encoding = 'utf-8' - - # locale._parse_localename makes locale.getpreferredencoding - # return None when LC_ALL is C, instead of e.g. 'US-ASCII' or - # 'ANSI_X3.4-1968' when it uses nl_langinfo. + encoding = getpreferredencoding() if encoding: return codecs.getwriter(encoding)(fh) return fh @@ -195,8 +197,11 @@ class LineIO(object): def __init__(self, callback): self._callback = callback self._buf = '' + self._encoding = getpreferredencoding() def write(self, buf): + if self._encoding and isinstance(buf, str): + buf = buf.decode(self._encoding) lines = buf.splitlines() if not lines: return diff --git a/python/mozbuild/mozbuild/test/configure/data/subprocess.configure b/python/mozbuild/mozbuild/test/configure/data/subprocess.configure new file mode 100644 index 000000000000..0595d90404f8 --- /dev/null +++ b/python/mozbuild/mozbuild/test/configure/data/subprocess.configure @@ -0,0 +1,23 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +@depends('--help') +@imports('codecs') +@imports(_from='mozbuild.configure.util', _import='getpreferredencoding') +@imports('os') +@imports(_from='__builtin__', _import='open') +def dies_when_logging(_): + test_file = 'test.txt' + quote_char = "'" + if getpreferredencoding().lower() == 'utf-8': + quote_char = '\u00B4'.encode('utf-8') + try: + with open(test_file, 'w+') as fh: + fh.write(quote_char) + out = check_cmd_output('cat', 'test.txt') + log.info(out) + finally: + os.remove(test_file) diff --git a/python/mozbuild/mozbuild/test/configure/test_util.py b/python/mozbuild/mozbuild/test/configure/test_util.py index 8aa50108a817..38b3c636e5c1 100644 --- a/python/mozbuild/mozbuild/test/configure/test_util.py +++ b/python/mozbuild/mozbuild/test/configure/test_util.py @@ -18,9 +18,15 @@ from mozpack import path as mozpath from mozbuild.configure.util import ( ConfigureOutputHandler, + getpreferredencoding, LineIO, Version, ) + +from mozbuild.configure import ( + ConfigureSandbox, +) + from mozbuild.util import exec_ from buildconfig import topsrcdir @@ -411,6 +417,30 @@ class TestLineIO(unittest.TestCase): self.assertEqual(lines, ['a', 'b', 'c']) +class TestLogSubprocessOutput(unittest.TestCase): + + def test_non_ascii_subprocess_output(self): + out = StringIO() + sandbox = ConfigureSandbox({}, {}, [], out, out) + + sandbox.include_file(mozpath.join(topsrcdir, 'build', + 'moz.configure', 'util.configure')) + sandbox.include_file(mozpath.join(topsrcdir, 'python', 'mozbuild', + 'mozbuild', 'test', 'configure', + 'data', 'subprocess.configure')) + status = 0 + try: + sandbox.run() + except SystemExit as e: + status = e.code + + self.assertEquals(status, 0) + quote_char = "'" + if getpreferredencoding().lower() == 'utf-8': + quote_char = '\u00B4'.encode('utf-8') + self.assertEquals(out.getvalue().strip(), quote_char) + + class TestVersion(unittest.TestCase): def test_version_simple(self): v = Version('1') From 140a910bc307956f0b729abb31abb2aff69ce082 Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Wed, 1 Jun 2016 09:20:37 +1200 Subject: [PATCH 134/199] Bug 1261900 - Remove obsolete patch file. r=me --- media/libnestegg/bug1271866.patch | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 media/libnestegg/bug1271866.patch diff --git a/media/libnestegg/bug1271866.patch b/media/libnestegg/bug1271866.patch deleted file mode 100644 index 93863ad9518b..000000000000 --- a/media/libnestegg/bug1271866.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/media/libnestegg/src/nestegg.c b/media/libnestegg/src/nestegg.c -index b9d3391..b13b2ae 100644 ---- a/media/libnestegg/src/nestegg.c -+++ b/media/libnestegg/src/nestegg.c -@@ -2333,9 +2333,6 @@ nestegg_read_packet(nestegg * ctx, nestegg_packet ** pkt) - - *pkt = NULL; - -- if (!ctx->ancestor) -- return -1; -- - for (;;) { - r = ne_peek_element(ctx, &id, &size); - if (r != 1) From b2883162ea4093f5358f1e4accb9adf205492c3f Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Tue, 31 May 2016 14:44:46 -0700 Subject: [PATCH 135/199] Bug 1258036 - Separate global state checks from fb-specific checks. - r=jrmuizel --- dom/canvas/WebGLContext.cpp | 31 ++++++++++++++++--------------- dom/canvas/WebGLContext.h | 2 +- dom/canvas/WebGLContextUtils.cpp | 21 +++++++++++---------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index a173b4c0c84b..723d551ab56f 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -984,6 +984,9 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight) mOptions.antialias = gl->Caps().antialias; + ////// + // Initial setup. + MakeContextCurrent(); gl->fViewport(0, 0, mWidth, mHeight); @@ -991,20 +994,13 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight) mViewportHeight = mHeight; gl->fScissor(0, 0, mWidth, mHeight); - - // Make sure that we clear this out, otherwise - // we'll end up displaying random memory gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0); + ////// + // Check everything + AssertCachedBindings(); - AssertCachedState(); - - // Clear immediately, because we need to present the cleared initial - // buffer. - mBackbufferNeedsClear = true; - ClearBackbufferIfNeeded(); - - mShouldPresent = true; + AssertCachedGlobalState(); MOZ_ASSERT(gl->Caps().color); @@ -1020,8 +1016,14 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight) MOZ_ASSERT(gl->Caps().antialias == mOptions.antialias); MOZ_ASSERT(gl->Caps().preserve == mOptions.preserveDrawingBuffer); - AssertCachedBindings(); - AssertCachedState(); + ////// + // Clear immediately, because we need to present the cleared initial buffer + mBackbufferNeedsClear = true; + ClearBackbufferIfNeeded(); + + mShouldPresent = true; + + ////// reporter.SetSuccessful(); @@ -1416,8 +1418,7 @@ WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield clearBits, // Fun GL fact: No need to worry about the viewport here, glViewport is just // setting up a coordinates transformation, it doesn't affect glClear at all. - AssertCachedState(); // Can't check cached bindings, as we could - // have a different FB bound temporarily. + AssertCachedGlobalState(); // Prepare GL state for clearing. gl->fDisable(LOCAL_GL_SCISSOR_TEST); diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index 1c989ae1b5eb..329736503963 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -376,7 +376,7 @@ public: bool TryToRestoreContext(); void AssertCachedBindings(); - void AssertCachedState(); + void AssertCachedGlobalState(); dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; } diff --git a/dom/canvas/WebGLContextUtils.cpp b/dom/canvas/WebGLContextUtils.cpp index b9d0c7349563..707651129124 100644 --- a/dom/canvas/WebGLContextUtils.cpp +++ b/dom/canvas/WebGLContextUtils.cpp @@ -684,7 +684,7 @@ WebGLContext::AssertCachedBindings() AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound); } - // Bound object state + // Framebuffers if (IsWebGL2()) { GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->mGLName : 0; @@ -699,6 +699,15 @@ WebGLContext::AssertCachedBindings() AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound); } + GLint stencilBits = 0; + if (GetStencilBits(&stencilBits)) { // Depends on current draw framebuffer. + const GLuint stencilRefMask = (1 << stencilBits) - 1; + + AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_REF, stencilRefMask, mStencilRefFront); + AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_REF, stencilRefMask, mStencilRefBack); + } + + // Program GLuint bound = mCurrentProgram ? mCurrentProgram->mGLName : 0; AssertUintParamCorrect(gl, LOCAL_GL_CURRENT_PROGRAM, bound); @@ -730,7 +739,7 @@ WebGLContext::AssertCachedBindings() } void -WebGLContext::AssertCachedState() +WebGLContext::AssertCachedGlobalState() { #ifdef DEBUG MakeContextCurrent(); @@ -771,14 +780,6 @@ WebGLContext::AssertCachedState() AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_CLEAR_VALUE, mStencilClearValue); - GLint stencilBits = 0; - if (GetStencilBits(&stencilBits)) { - const GLuint stencilRefMask = (1 << stencilBits) - 1; - - AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_REF, stencilRefMask, mStencilRefFront); - AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_REF, stencilRefMask, mStencilRefBack); - } - // GLES 3.0.4, $4.1.4, p177: // [...] the front and back stencil mask are both set to the value `2^s - 1`, where // `s` is greater than or equal to the number of bits in the deepest stencil buffer From 7fad0075ea2fd072f5bb5d94af7918aa849b6180 Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Thu, 26 May 2016 17:22:08 -0700 Subject: [PATCH 136/199] Bug 1276096 - ANGLE should check for NONE readbuffer. - r=jrmuizel --- gfx/angle/src/libANGLE/Framebuffer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gfx/angle/src/libANGLE/Framebuffer.cpp b/gfx/angle/src/libANGLE/Framebuffer.cpp index 3def57b87e4e..d86a01decc08 100644 --- a/gfx/angle/src/libANGLE/Framebuffer.cpp +++ b/gfx/angle/src/libANGLE/Framebuffer.cpp @@ -69,6 +69,9 @@ const std::string &Framebuffer::Data::getLabel() const FramebufferAttachment *Framebuffer::Data::getReadAttachment() const { + if (mReadBufferState == GL_NONE) + return nullptr; + ASSERT(mReadBufferState == GL_BACK || (mReadBufferState >= GL_COLOR_ATTACHMENT0 && mReadBufferState <= GL_COLOR_ATTACHMENT15)); size_t readIndex = (mReadBufferState == GL_BACK ? 0 : static_cast(mReadBufferState - GL_COLOR_ATTACHMENT0)); ASSERT(readIndex < mColorAttachments.size()); From 3773a303620148e0276c06ab0c4399b05d84d067 Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Thu, 26 May 2016 18:07:03 -0700 Subject: [PATCH 137/199] Bug 1276096 - Introspect for readBuffer mode. - r=jrmuizel --- dom/canvas/WebGL2ContextState.cpp | 7 ++----- dom/canvas/WebGLFramebuffer.h | 2 ++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dom/canvas/WebGL2ContextState.cpp b/dom/canvas/WebGL2ContextState.cpp index cd2677d72fb9..d9acea28c902 100644 --- a/dom/canvas/WebGL2ContextState.cpp +++ b/dom/canvas/WebGL2ContextState.cpp @@ -43,11 +43,8 @@ WebGL2Context::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) /* GLenum */ case LOCAL_GL_READ_BUFFER: { - if (mBoundReadFramebuffer) { - GLint val = LOCAL_GL_NONE; - gl->fGetIntegerv(pname, &val); - return JS::Int32Value(val); - } + if (mBoundReadFramebuffer) + return JS::Int32Value(mBoundReadFramebuffer->ReadBufferMode()); return JS::Int32Value(LOCAL_GL_BACK); } diff --git a/dom/canvas/WebGLFramebuffer.h b/dom/canvas/WebGLFramebuffer.h index 370d1d469827..14b25e4bf1ac 100644 --- a/dom/canvas/WebGLFramebuffer.h +++ b/dom/canvas/WebGLFramebuffer.h @@ -257,6 +257,8 @@ public: mReadBufferMode = readBufferMode; } + GLenum ReadBufferMode() const { return mReadBufferMode; } + protected: WebGLFBAttachPoint* GetAttachPoint(GLenum attachment); // Fallible From 42afe5649774338a63ce7afd610e28ab377109a5 Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Thu, 26 May 2016 18:07:52 -0700 Subject: [PATCH 138/199] Bug 1276096 - Forbid reads from backbuffer with readBuffer(NONE). - r=jrmuizel --- dom/canvas/WebGLContext.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index 723d551ab56f..cd7bceb896ee 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -1882,6 +1882,14 @@ WebGLContext::ValidateCurFBForRead(const char* funcName, GLenum* const out_mode) { if (!mBoundReadFramebuffer) { + const GLenum readBufferMode = gl->Screen()->GetReadBufferMode(); + if (readBufferMode == LOCAL_GL_NONE) { + ErrorInvalidOperation("%s: Can't read from backbuffer when readBuffer mode is" + " NONE.", + funcName); + return false; + } + ClearBackbufferIfNeeded(); // FIXME - here we're assuming that the default framebuffer is backed by From f9b51c3c018b1843a34f3c1975a7f8b46dfcf0b7 Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Thu, 26 May 2016 18:08:31 -0700 Subject: [PATCH 139/199] Bug 1276096 - Use existing can-I-read-from-this helper. - r=jrmuizel --- dom/canvas/WebGLContextState.cpp | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/dom/canvas/WebGLContextState.cpp b/dom/canvas/WebGLContextState.cpp index 8684748bd51c..693714b8beb2 100644 --- a/dom/canvas/WebGLContextState.cpp +++ b/dom/canvas/WebGLContextState.cpp @@ -380,16 +380,11 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return JS::NumberValue(uint32_t(i)); } case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: { - if (mBoundReadFramebuffer) { - nsCString fbStatusInfoIgnored; - const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(&fbStatusInfoIgnored); - if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { - ErrorInvalidOperation("getParameter: Read framebuffer must be" - " complete before querying" - " IMPLEMENTATION_COLOR_READ_TYPE."); - return JS::NullValue(); - } - } + const webgl::FormatUsageInfo* usage; + uint32_t width, height; + GLenum mode; + if (!ValidateCurFBForRead(funcName, &usage, &width, &height, &mode)) + return JS::NullValue(); GLint i = 0; if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { @@ -401,16 +396,11 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return JS::NumberValue(uint32_t(i)); } case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT: { - if (mBoundReadFramebuffer) { - nsCString fbStatusInfoIgnored; - const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(&fbStatusInfoIgnored); - if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { - ErrorInvalidOperation("getParameter: Read framebuffer must be" - " complete before querying" - " IMPLEMENTATION_COLOR_READ_FORMAT."); - return JS::NullValue(); - } - } + const webgl::FormatUsageInfo* usage; + uint32_t width, height; + GLenum mode; + if (!ValidateCurFBForRead(funcName, &usage, &width, &height, &mode)) + return JS::NullValue(); GLint i = 0; if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { From 92bf50526245279a53888f744f5815199a8224be Mon Sep 17 00:00:00 2001 From: Randall Barker Date: Thu, 26 May 2016 14:39:04 -0700 Subject: [PATCH 140/199] Bug 1240065 - Throttle repaints when pinch zooming r=botond --- gfx/layers/apz/src/AsyncPanZoomController.cpp | 36 +++++++++++++++++-- gfx/layers/apz/src/AsyncPanZoomController.h | 5 +++ gfx/thebes/gfxPrefs.h | 1 + modules/libpref/init/all.js | 1 + 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index d6c5f7da7f25..f7e48d0e5a01 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -365,6 +365,12 @@ typedef GenericFlingAnimation FlingAnimation; * \li\b apz.zoom_animation_duration_ms * This controls how long the zoom-to-rect animation takes.\n * Units: ms + * + * \li\b apz.scale_repaint_delay_ms + * How long to delay between repaint requests during a scale. + * A negative number prevents repaint requests during a scale.\n + * Units: ms + * */ /** @@ -703,6 +709,7 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId, mState(NOTHING), mNotificationBlockers(0), mInputQueue(aInputQueue), + mPinchPaintTimerSet(false), mAPZCId(sAsyncPanZoomControllerCount++), mSharedLock(nullptr), mAsyncTransformAppliedToContent(false), @@ -1281,6 +1288,7 @@ nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEven nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) { APZC_LOG("%p got a scale-begin in state %d\n", this, mState); + mPinchPaintTimerSet = false; // Note that there may not be a touch block at this point, if we received the // PinchGestureEvent directly from widget code without any touch events. if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) { @@ -1375,8 +1383,21 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) { } ScheduleComposite(); - // We don't want to redraw on every scale, so don't use - // RequestContentRepaint() + + // We don't want to redraw on every scale, so throttle it. + if (!mPinchPaintTimerSet) { + const int delay = gfxPrefs::APZScaleRepaintDelay(); + if (delay >= 0) { + if (RefPtr controller = GetGeckoContentController()) { + mPinchPaintTimerSet = true; + controller->PostDelayedTask( + NewRunnableMethod(this, + &AsyncPanZoomController::DoDelayedRequestContentRepaint), + delay); + } + } + } + UpdateSharedCompositorFrameMetrics(); } @@ -1389,6 +1410,8 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) { nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent) { APZC_LOG("%p got a scale-end in state %d\n", this, mState); + mPinchPaintTimerSet = false; + if (HasReadyTouchBlock() && !CurrentTouchBlock()->TouchActionAllowsPinchZoom()) { return nsEventStatus_eIgnore; } @@ -1613,6 +1636,15 @@ AsyncPanZoomController::AllowScrollHandoffInCurrentBlock() const return result; } +void AsyncPanZoomController::DoDelayedRequestContentRepaint() +{ + if (!IsDestroyed() && mPinchPaintTimerSet) { + ReentrantMonitorAutoEnter lock(mMonitor); + RequestContentRepaint(); + } + mPinchPaintTimerSet = false; +} + static ScrollInputMethod ScrollInputMethodForWheelDeltaType(ScrollWheelInput::ScrollDeltaType aDeltaType) { diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 34b69889504e..654892f1dc02 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -898,6 +898,8 @@ private: ParentLayerPoint mLastFlingVelocity; // The time at which the most recent fling started. TimeStamp mLastFlingTime; + // Indicates if the repaint-during-pinch timer is currently set + bool mPinchPaintTimerSet; // Deal with overscroll resulting from a fling animation. This is only ever // called on APZC instances that were actually performing a fling. @@ -921,6 +923,9 @@ private: // Returns whether overscroll is allowed during an event. bool AllowScrollHandoffInCurrentBlock() const; + // Invoked by the pinch repaint timer. + void DoDelayedRequestContentRepaint(); + /* =================================================================== * The functions and members in this section are used to make ancestor chains * out of APZC instances. These chains can only be walked or manipulated diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index 40e57b6ea401..52c30b76a95b 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -187,6 +187,7 @@ private: DECL_GFX_PREF(Live, "apz.y_skate_size_multiplier", APZYSkateSizeMultiplier, float, 2.5f); DECL_GFX_PREF(Live, "apz.y_stationary_size_multiplier", APZYStationarySizeMultiplier, float, 3.5f); DECL_GFX_PREF(Live, "apz.zoom_animation_duration_ms", APZZoomAnimationDuration, int32_t, 250); + DECL_GFX_PREF(Live, "apz.scale_repaint_delay_ms", APZScaleRepaintDelay, int32_t, 500); DECL_GFX_PREF(Live, "browser.ui.zoom.force-user-scalable", ForceUserScalable, bool, false); DECL_GFX_PREF(Live, "browser.viewport.desktopWidth", DesktopViewportWidth, int32_t, 980); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 0cd571124f91..d8aeab116837 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -644,6 +644,7 @@ pref("apz.y_skate_size_multiplier", "3.5"); pref("apz.x_stationary_size_multiplier", "1.5"); pref("apz.y_stationary_size_multiplier", "3.5"); pref("apz.zoom_animation_duration_ms", 250); +pref("apz.scale_repaint_delay_ms", 500); #if defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID) // Mobile prefs From a8d3b4b1d90a2f52eeac469844c1ba6192d89daf Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Fri, 27 May 2016 18:55:13 -0700 Subject: [PATCH 141/199] Bug 1276405 - EXT_color_buffer_float should disable clearColor clamping. - r=jrmuizel --- dom/canvas/WebGLContextFramebufferOperations.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dom/canvas/WebGLContextFramebufferOperations.cpp b/dom/canvas/WebGLContextFramebufferOperations.cpp index fb1550317ca3..91c431cd0fc4 100644 --- a/dom/canvas/WebGLContextFramebufferOperations.cpp +++ b/dom/canvas/WebGLContextFramebufferOperations.cpp @@ -72,7 +72,8 @@ WebGLContext::ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) MakeContextCurrent(); - const bool supportsFloatColorBuffers = (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) || + const bool supportsFloatColorBuffers = (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_float) || + IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) || IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)); if (!supportsFloatColorBuffers) { r = GLClampFloat(r); From 1b4ffa70c186c321bc84cb8a43fac2c1cfe10f20 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Wed, 1 Jun 2016 00:37:33 +0200 Subject: [PATCH 142/199] Backed out changeset 7b5c8958c61c (bug 1270962) for often failing browser_readerMode.js. r=backout --- browser/base/content/test/general/browser.ini | 3 --- dom/base/test/mochitest.ini | 1 - dom/browser-element/mochitest/mochitest-oop.ini | 3 +-- dom/media/tests/mochitest/mochitest.ini | 1 + dom/tests/mochitest/general/mochitest.ini | 4 ---- 5 files changed, 2 insertions(+), 10 deletions(-) diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 5c3a9d63f1ea..5d80e2c4c49d 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -167,7 +167,6 @@ skip-if = true # browser_bug321000.js is disabled because newline handling is sh [browser_bug406216.js] [browser_bug408415.js] [browser_bug409481.js] -subsuite = clipboard [browser_bug409624.js] [browser_bug413915.js] [browser_bug416661.js] @@ -285,7 +284,6 @@ skip-if = os == 'win' [browser_clipboard.js] subsuite = clipboard [browser_clipboard_pastefile.js] -subsuite = clipboard [browser_contentAreaClick.js] [browser_contextmenu.js] tags = fullscreen @@ -508,7 +506,6 @@ tags = mcb [browser_contextmenu_childprocess.js] [browser_bug963945.js] [browser_readerMode.js] -subsuite = clipboard support-files = readerModeArticle.html readerModeArticleHiddenNodes.html diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index ecadff8ca8df..7425462e8aed 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -337,7 +337,6 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec [test_bug330925.xhtml] [test_bug331959.html] [test_bug333064.html] -subsuite = clipboard skip-if = toolkit == 'android' [test_bug333198.html] [test_bug333673.html] diff --git a/dom/browser-element/mochitest/mochitest-oop.ini b/dom/browser-element/mochitest/mochitest-oop.ini index b191a127df56..f0d5dcc1ff78 100644 --- a/dom/browser-element/mochitest/mochitest-oop.ini +++ b/dom/browser-element/mochitest/mochitest-oop.ini @@ -40,7 +40,6 @@ skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_oop_Close.html] [test_browserElement_oop_CookiesNotThirdParty.html] [test_browserElement_oop_CopyPaste.html] -subsuite = clipboard [test_browserElement_oop_DOMRequestError.html] [test_browserElement_oop_DataURI.html] [test_browserElement_oop_DisallowEmbedAppsInOOP.html] @@ -132,4 +131,4 @@ tags = audiochannel [test_browserElement_oop_OpenWindowEmpty.html] skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator [test_browserElement_oop_ActiveStateChangeOnChangingMutedOrVolume.html] -tags = audiochannel +tags = audiochannel \ No newline at end of file diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index 266f6c631766..cebe42781efd 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -129,6 +129,7 @@ skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio skip-if = buildapp == 'b2g' || buildapp == 'mulet' || os == 'android' # bug 1043403 # Bug 1141029 Mulet parity with B2G Desktop for TC [test_peerConnection_bug1064223.html] [test_peerConnection_capturedVideo.html] +subsuite = clipboard tags=capturestream skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) [test_peerConnection_captureStream_canvas_2d.html] diff --git a/dom/tests/mochitest/general/mochitest.ini b/dom/tests/mochitest/general/mochitest.ini index 4c5a227242a6..06f382902133 100644 --- a/dom/tests/mochitest/general/mochitest.ini +++ b/dom/tests/mochitest/general/mochitest.ini @@ -65,7 +65,6 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec [test_bug861217.html] [test_clientRects.html] [test_clipboard_disallowed.html] -subsuite = clipboard [test_clipboard_events.html] subsuite = clipboard skip-if = buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined) @@ -88,7 +87,6 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIME skip-if = buildapp == 'b2g' || buildapp == 'mulet' [test_img_mutations.html] [test_interfaces.html] -subsuite = clipboard skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage # [test_network_events.html] # Disable this test until bug 795711 is fixed. @@ -98,7 +96,6 @@ support-files = test_offsets.js [test_outerHTML.xhtml] skip-if = buildapp == 'mulet' [test_paste_selection.html] -subsuite = clipboard skip-if = buildapp == 'mulet' [test_performance_timeline.html] [test_picture_mutations.html] @@ -130,7 +127,6 @@ subsuite = clipboard skip-if = (toolkit == 'android') # Disabled on Android, see bug 1230231 [test_bug1161721.html] [test_bug1170911.html] -subsuite = clipboard [test_storagePermissionsAccept.html] skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g [test_storagePermissionsRejectForeign.html] From 52e7064618f76646527c87e4063ef202ec6efb73 Mon Sep 17 00:00:00 2001 From: Gerald Squelart Date: Tue, 10 May 2016 18:13:14 +1000 Subject: [PATCH 143/199] Bug 1271593 - NS_NewRunnableFunction should forward its arg - r=froydnj By perfect-forwarding its argument, we can automatically gain move semantics optimization when storing the given function object into nsRunnableFunction. Also it allows movable-only function objects. MozReview-Commit-ID: 9EZK84ZhMvR --HG-- extra : rebase_source : 9ec11f728d69d4b032d9ba4263241832030b383a --- xpcom/glue/nsThreadUtils.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/xpcom/glue/nsThreadUtils.h b/xpcom/glue/nsThreadUtils.h index 11b7839565ca..e02530e4c58c 100644 --- a/xpcom/glue/nsThreadUtils.h +++ b/xpcom/glue/nsThreadUtils.h @@ -20,6 +20,7 @@ #include "mozilla/Atomics.h" #include "mozilla/IndexSequence.h" #include "mozilla/Likely.h" +#include "mozilla/Move.h" #include "mozilla/Tuple.h" #include "mozilla/TypeTraits.h" @@ -266,8 +267,9 @@ template class nsRunnableFunction : public mozilla::Runnable { public: - explicit nsRunnableFunction(const Function& aFunction) - : mFunction(aFunction) + template + explicit nsRunnableFunction(F&& aFunction) + : mFunction(mozilla::Forward(aFunction)) { } NS_IMETHOD Run() { @@ -281,9 +283,9 @@ private: }; template -nsRunnableFunction* NS_NewRunnableFunction(const Function& aFunction) +nsRunnableFunction* NS_NewRunnableFunction(Function&& aFunction) { - return new nsRunnableFunction(aFunction); + return new nsRunnableFunction(mozilla::Forward(aFunction)); } // An event that can be used to call a method on a class. The class type must From 7912b3c534eff0901920da76ae36d1930bb615c3 Mon Sep 17 00:00:00 2001 From: Gerald Squelart Date: Fri, 27 May 2016 19:20:03 +0200 Subject: [PATCH 144/199] Bug 1271593 - Unit test for NS_NewRunnableFunction - r=froydnj MozReview-Commit-ID: ArwIG9BEhDX --HG-- extra : rebase_source : fa6fda2379fa563cd2ecaaaef1f66b164d7b5712 --- xpcom/tests/TestThreadUtils.cpp | 201 +++++++++++++++++++++++++++++++- 1 file changed, 199 insertions(+), 2 deletions(-) diff --git a/xpcom/tests/TestThreadUtils.cpp b/xpcom/tests/TestThreadUtils.cpp index 521933827555..94a580020cbe 100644 --- a/xpcom/tests/TestThreadUtils.cpp +++ b/xpcom/tests/TestThreadUtils.cpp @@ -116,8 +116,207 @@ public: NS_IMPL_ISUPPORTS0(nsBar) +struct TestCopyWithNoMove +{ + explicit TestCopyWithNoMove(int& aCopyCounter) : mCopyCounter(aCopyCounter) {} + TestCopyWithNoMove(const TestCopyWithNoMove& a) : mCopyCounter(a.mCopyCounter) { ++mCopyCounter; }; + // No 'move' declaration, allows passing object by rvalue copy. + void operator()() {} + int& mCopyCounter; +}; +struct TestCopyWithDeletedMove +{ + explicit TestCopyWithDeletedMove(int& aCopyCounter) : mCopyCounter(aCopyCounter) {} + TestCopyWithDeletedMove(const TestCopyWithDeletedMove& a) : mCopyCounter(a.mCopyCounter) { ++mCopyCounter; }; + // Deleted move prevents passing by rvalue (even if copy would work) + TestCopyWithDeletedMove(TestCopyWithDeletedMove&&) = delete; + void operator()() {} + int& mCopyCounter; +}; +struct TestMove +{ + explicit TestMove(int& aMoveCounter) : mMoveCounter(aMoveCounter) {} + TestMove(const TestMove&) = delete; + TestMove(TestMove&& a) : mMoveCounter(a.mMoveCounter) { ++mMoveCounter; } + void operator()() {} + int& mMoveCounter; +}; +struct TestCopyMove +{ + TestCopyMove(int& aCopyCounter, int& aMoveCounter) : mCopyCounter(aCopyCounter), mMoveCounter(aMoveCounter) {} + TestCopyMove(const TestCopyMove& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { ++mCopyCounter; }; + TestCopyMove(TestCopyMove&& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { ++mMoveCounter; } + void operator()() {} + int& mCopyCounter; + int& mMoveCounter; +}; + +static int Expect(const char* aContext, int aCounter, int aMaxExpected) +{ + if (aCounter > aMaxExpected) { + fail("%s: expected %d max, got %d", aContext, aMaxExpected, aCounter); + return 1; + } + passed("%s: got %d <= %d as expected", aContext, aCounter, aMaxExpected); + return 0; +} + +int TestNS_NewRunnableFunction() +{ + int result = 0; + + // Test NS_NewRunnableFunction with copyable-only function object. + { + int copyCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyWithNoMove tracker(copyCounter); + trackedRunnable = NS_NewRunnableFunction(tracker); + } + } + result |= Expect("NS_NewRunnableFunction with copyable-only (and no move) function, copies", + copyCounter, 1); + } + { + int copyCounter = 0; + { + nsCOMPtr trackedRunnable; + { + // Passing as rvalue, but using copy. + // (TestCopyWithDeletedMove wouldn't allow this.) + trackedRunnable = NS_NewRunnableFunction(TestCopyWithNoMove(copyCounter)); + } + } + result |= Expect("NS_NewRunnableFunction with copyable-only (and no move) function rvalue, copies", + copyCounter, 1); + } + { + int copyCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyWithDeletedMove tracker(copyCounter); + trackedRunnable = NS_NewRunnableFunction(tracker); + } + } + result |= Expect("NS_NewRunnableFunction with copyable-only (and deleted move) function, copies", + copyCounter, 1); + } + + // Test NS_NewRunnableFunction with movable-only function object. + { + int moveCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestMove tracker(moveCounter); + trackedRunnable = NS_NewRunnableFunction(Move(tracker)); + } + } + result |= Expect("NS_NewRunnableFunction with movable-only function, moves", + moveCounter, 1); + } + { + int moveCounter = 0; + { + nsCOMPtr trackedRunnable; + { + trackedRunnable = NS_NewRunnableFunction(TestMove(moveCounter)); + } + } + result |= Expect("NS_NewRunnableFunction with movable-only function rvalue, moves", + moveCounter, 1); + } + + // Test NS_NewRunnableFunction with copyable&movable function object. + { + int copyCounter = 0; + int moveCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyMove tracker(copyCounter, moveCounter); + trackedRunnable = NS_NewRunnableFunction(Move(tracker)); + } + } + result |= Expect("NS_NewRunnableFunction with copyable&movable function, copies", + copyCounter, 0); + result |= Expect("NS_NewRunnableFunction with copyable&movable function, moves", + moveCounter, 1); + } + { + int copyCounter = 0; + int moveCounter = 0; + { + nsCOMPtr trackedRunnable; + { + trackedRunnable = + NS_NewRunnableFunction(TestCopyMove(copyCounter, moveCounter)); + } + } + result |= Expect("NS_NewRunnableFunction with copyable&movable function rvalue, copies", + copyCounter, 0); + result |= Expect("NS_NewRunnableFunction with copyable&movable function rvalue, moves", + moveCounter, 1); + } + + // Test NS_NewRunnableFunction with copyable-only lambda capture. + { + int copyCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyWithNoMove tracker(copyCounter); + // Expect 2 copies (here -> local lambda -> runnable lambda). + trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); }); + } + } + result |= Expect("NS_NewRunnableFunction with copyable-only (and no move) capture, copies", + copyCounter, 2); + } + { + int copyCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyWithDeletedMove tracker(copyCounter); + // Expect 2 copies (here -> local lambda -> runnable lambda). + trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); }); + } + } + result |= Expect("NS_NewRunnableFunction with copyable-only (and deleted move) capture, copies", + copyCounter, 2); + } + + // Note: Not possible to use move-only captures. + // (Until we can use C++14 generalized lambda captures) + + // Test NS_NewRunnableFunction with copyable&movable lambda capture. + { + int copyCounter = 0; + int moveCounter = 0; + { + nsCOMPtr trackedRunnable; + { + TestCopyMove tracker(copyCounter, moveCounter); + trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); }); + // Expect 1 copy (here -> local lambda) and 1 move (local -> runnable lambda). + } + } + result |= Expect("NS_NewRunnableFunction with copyable&movable capture, copies", + copyCounter, 1); + result |= Expect("NS_NewRunnableFunction with copyable&movable capture, moves", + moveCounter, 1); + } + + return result; +} + int main(int argc, char** argv) { + int result = TestNS_NewRunnableFunction(); + ScopedXPCOM xpcom("ThreadUtils"); NS_ENSURE_FALSE(xpcom.failed(), 1); @@ -168,8 +367,6 @@ int main(int argc, char** argv) NS_ProcessPendingEvents(nullptr); } - int result = 0; - for (uint32_t i = 0; i < MAX_TESTS; i++) { if (gRunnableExecuted[i]) { passed("Test %d passed",i); From 738f9c933bda4ff144f29ea8f982ce9e0efc6ca7 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 31 May 2016 16:05:58 -0700 Subject: [PATCH 145/199] Backed out changeset d51737cf083d for android reftest failures --- mobile/android/app/mobile.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index f9c5d7b22f79..eeebb9ebf2a8 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -582,7 +582,11 @@ pref("apz.overscroll.enabled", true); #endif pref("layers.progressive-paint", true); +#ifdef NIGHTLY_BUILD +pref("layers.low-precision-buffer", false); +#else pref("layers.low-precision-buffer", true); +#endif pref("layers.low-precision-resolution", "0.25"); pref("layers.low-precision-opacity", "1.0"); // We want to limit layers for two reasons: @@ -740,6 +744,10 @@ pref("layout.framevisibility.numscrollportwidths", 1); pref("layout.framevisibility.numscrollportheights", 1); pref("layers.enable-tiles", true); +#ifdef NIGHTLY_BUILD +pref("layers.tiles.fade-in.enabled", true); +pref("layers.tiles.fade-in.duration-ms", 250); +#endif // Enable the dynamic toolbar pref("browser.chrome.dynamictoolbar", true); From 5843da7b347bdbb53e22601f98d30cbc61f3fa05 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 31 May 2016 16:06:06 -0700 Subject: [PATCH 146/199] Backed out changeset 43ecf82afb92 (bug 1270241) for android reftest failures --- gfx/thebes/gfxAndroidPlatform.cpp | 10 +++++----- gfx/thebes/moz.build | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index 41eadd1fbec9..43120d595b04 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -313,15 +313,15 @@ gfxAndroidPlatform::FontHintingEnabled() // In "mobile" builds, we sometimes use non-reflow-zoom, so we // might not want hinting. Let's see. -#ifdef MOZ_WIDGET_ANDROID - // On Android, we currently only use gecko to render web +#ifdef MOZ_USING_ANDROID_JAVA_WIDGETS + // On android-java, we currently only use gecko to render web // content that can always be be non-reflow-zoomed. So turn off // hinting. // // XXX when gecko-android-java is used as an "app runtime", we may // want to re-enable hinting for non-browser processes there. return false; -#endif // MOZ_WIDGET_ANDROID +#endif // MOZ_USING_ANDROID_JAVA_WIDGETS #ifdef MOZ_WIDGET_GONK // On B2G, the UX preference is currently to keep hinting disabled @@ -339,8 +339,8 @@ gfxAndroidPlatform::FontHintingEnabled() bool gfxAndroidPlatform::RequiresLinearZoom() { -#ifdef MOZ_WIDGET_ANDROID - // On Android, we currently only use gecko to render web +#ifdef MOZ_USING_ANDROID_JAVA_WIDGETS + // On android-java, we currently only use gecko to render web // content that can always be be non-reflow-zoomed. // // XXX when gecko-android-java is used as an "app runtime", we may diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build index fb24a362bec8..cc42e820bf33 100644 --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -289,6 +289,11 @@ CFLAGS += CONFIG['TK_CFLAGS'] if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk', 'qt'): CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS'] +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': + # This is set for "normal Android", that is, when Gecko is running on + # top of the android java runtime. + DEFINES['MOZ_USING_ANDROID_JAVA_WIDGETS'] = True + if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'qt'): CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS'] From 5866e2ac1500b1720d3833f5134b9b5ec85de827 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 31 May 2016 16:15:45 -0700 Subject: [PATCH 147/199] Bug 1259850 trail of tears - more bogus reasons for calloc() to GC found. Suppress them. MozReview-Commit-ID: 1DrGZ9RW556 --HG-- extra : rebase_source : 975daa7185871abc1b3342a6618c7c6588e5b2ee --- js/src/devtools/rootAnalysis/annotations.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/js/src/devtools/rootAnalysis/annotations.js b/js/src/devtools/rootAnalysis/annotations.js index ea26539dc64d..194b84d00123 100644 --- a/js/src/devtools/rootAnalysis/annotations.js +++ b/js/src/devtools/rootAnalysis/annotations.js @@ -232,6 +232,12 @@ var ignoreFunctions = { // troublesome logging-related usage. "EntryType* nsTHashtable::PutEntry(nsTHashtable::KeyType) [with EntryType = nsBaseHashtableET >; nsTHashtable::KeyType = const char*]" : true, "EntryType* nsTHashtable::GetEntry(nsTHashtable::KeyType) const [with EntryType = nsBaseHashtableET >; nsTHashtable::KeyType = const char*]" : true, + "EntryType* nsTHashtable::PutEntry(nsTHashtable::KeyType) [with EntryType = nsBaseHashtableET, nsAutoPtr::OrderingEntry> >; nsTHashtable::KeyType = const mozilla::BlockingResourceBase*]" : true, + "EntryType* nsTHashtable::GetEntry(nsTHashtable::KeyType) const [with EntryType = nsBaseHashtableET, nsAutoPtr::OrderingEntry> >; nsTHashtable::KeyType = const mozilla::BlockingResourceBase*]" : true, + + // The big hammers. + "PR_GetCurrentThread" : true, + "calloc" : true, }; function isProtobuf(name) From a181d1f7a03a8bbd893b908260caa0259e9b9e3f Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Tue, 31 May 2016 16:47:44 -0700 Subject: [PATCH 148/199] Backed out 2 changesets (bug 1271593) for GTest failures Backed out changeset 0839aaf88283 (bug 1271593) Backed out changeset bf90268fe339 (bug 1271593) --- xpcom/glue/nsThreadUtils.h | 10 +- xpcom/tests/TestThreadUtils.cpp | 201 +------------------------------- 2 files changed, 6 insertions(+), 205 deletions(-) diff --git a/xpcom/glue/nsThreadUtils.h b/xpcom/glue/nsThreadUtils.h index e02530e4c58c..11b7839565ca 100644 --- a/xpcom/glue/nsThreadUtils.h +++ b/xpcom/glue/nsThreadUtils.h @@ -20,7 +20,6 @@ #include "mozilla/Atomics.h" #include "mozilla/IndexSequence.h" #include "mozilla/Likely.h" -#include "mozilla/Move.h" #include "mozilla/Tuple.h" #include "mozilla/TypeTraits.h" @@ -267,9 +266,8 @@ template class nsRunnableFunction : public mozilla::Runnable { public: - template - explicit nsRunnableFunction(F&& aFunction) - : mFunction(mozilla::Forward(aFunction)) + explicit nsRunnableFunction(const Function& aFunction) + : mFunction(aFunction) { } NS_IMETHOD Run() { @@ -283,9 +281,9 @@ private: }; template -nsRunnableFunction* NS_NewRunnableFunction(Function&& aFunction) +nsRunnableFunction* NS_NewRunnableFunction(const Function& aFunction) { - return new nsRunnableFunction(mozilla::Forward(aFunction)); + return new nsRunnableFunction(aFunction); } // An event that can be used to call a method on a class. The class type must diff --git a/xpcom/tests/TestThreadUtils.cpp b/xpcom/tests/TestThreadUtils.cpp index 94a580020cbe..521933827555 100644 --- a/xpcom/tests/TestThreadUtils.cpp +++ b/xpcom/tests/TestThreadUtils.cpp @@ -116,207 +116,8 @@ public: NS_IMPL_ISUPPORTS0(nsBar) -struct TestCopyWithNoMove -{ - explicit TestCopyWithNoMove(int& aCopyCounter) : mCopyCounter(aCopyCounter) {} - TestCopyWithNoMove(const TestCopyWithNoMove& a) : mCopyCounter(a.mCopyCounter) { ++mCopyCounter; }; - // No 'move' declaration, allows passing object by rvalue copy. - void operator()() {} - int& mCopyCounter; -}; -struct TestCopyWithDeletedMove -{ - explicit TestCopyWithDeletedMove(int& aCopyCounter) : mCopyCounter(aCopyCounter) {} - TestCopyWithDeletedMove(const TestCopyWithDeletedMove& a) : mCopyCounter(a.mCopyCounter) { ++mCopyCounter; }; - // Deleted move prevents passing by rvalue (even if copy would work) - TestCopyWithDeletedMove(TestCopyWithDeletedMove&&) = delete; - void operator()() {} - int& mCopyCounter; -}; -struct TestMove -{ - explicit TestMove(int& aMoveCounter) : mMoveCounter(aMoveCounter) {} - TestMove(const TestMove&) = delete; - TestMove(TestMove&& a) : mMoveCounter(a.mMoveCounter) { ++mMoveCounter; } - void operator()() {} - int& mMoveCounter; -}; -struct TestCopyMove -{ - TestCopyMove(int& aCopyCounter, int& aMoveCounter) : mCopyCounter(aCopyCounter), mMoveCounter(aMoveCounter) {} - TestCopyMove(const TestCopyMove& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { ++mCopyCounter; }; - TestCopyMove(TestCopyMove&& a) : mCopyCounter(a.mCopyCounter), mMoveCounter(a.mMoveCounter) { ++mMoveCounter; } - void operator()() {} - int& mCopyCounter; - int& mMoveCounter; -}; - -static int Expect(const char* aContext, int aCounter, int aMaxExpected) -{ - if (aCounter > aMaxExpected) { - fail("%s: expected %d max, got %d", aContext, aMaxExpected, aCounter); - return 1; - } - passed("%s: got %d <= %d as expected", aContext, aCounter, aMaxExpected); - return 0; -} - -int TestNS_NewRunnableFunction() -{ - int result = 0; - - // Test NS_NewRunnableFunction with copyable-only function object. - { - int copyCounter = 0; - { - nsCOMPtr trackedRunnable; - { - TestCopyWithNoMove tracker(copyCounter); - trackedRunnable = NS_NewRunnableFunction(tracker); - } - } - result |= Expect("NS_NewRunnableFunction with copyable-only (and no move) function, copies", - copyCounter, 1); - } - { - int copyCounter = 0; - { - nsCOMPtr trackedRunnable; - { - // Passing as rvalue, but using copy. - // (TestCopyWithDeletedMove wouldn't allow this.) - trackedRunnable = NS_NewRunnableFunction(TestCopyWithNoMove(copyCounter)); - } - } - result |= Expect("NS_NewRunnableFunction with copyable-only (and no move) function rvalue, copies", - copyCounter, 1); - } - { - int copyCounter = 0; - { - nsCOMPtr trackedRunnable; - { - TestCopyWithDeletedMove tracker(copyCounter); - trackedRunnable = NS_NewRunnableFunction(tracker); - } - } - result |= Expect("NS_NewRunnableFunction with copyable-only (and deleted move) function, copies", - copyCounter, 1); - } - - // Test NS_NewRunnableFunction with movable-only function object. - { - int moveCounter = 0; - { - nsCOMPtr trackedRunnable; - { - TestMove tracker(moveCounter); - trackedRunnable = NS_NewRunnableFunction(Move(tracker)); - } - } - result |= Expect("NS_NewRunnableFunction with movable-only function, moves", - moveCounter, 1); - } - { - int moveCounter = 0; - { - nsCOMPtr trackedRunnable; - { - trackedRunnable = NS_NewRunnableFunction(TestMove(moveCounter)); - } - } - result |= Expect("NS_NewRunnableFunction with movable-only function rvalue, moves", - moveCounter, 1); - } - - // Test NS_NewRunnableFunction with copyable&movable function object. - { - int copyCounter = 0; - int moveCounter = 0; - { - nsCOMPtr trackedRunnable; - { - TestCopyMove tracker(copyCounter, moveCounter); - trackedRunnable = NS_NewRunnableFunction(Move(tracker)); - } - } - result |= Expect("NS_NewRunnableFunction with copyable&movable function, copies", - copyCounter, 0); - result |= Expect("NS_NewRunnableFunction with copyable&movable function, moves", - moveCounter, 1); - } - { - int copyCounter = 0; - int moveCounter = 0; - { - nsCOMPtr trackedRunnable; - { - trackedRunnable = - NS_NewRunnableFunction(TestCopyMove(copyCounter, moveCounter)); - } - } - result |= Expect("NS_NewRunnableFunction with copyable&movable function rvalue, copies", - copyCounter, 0); - result |= Expect("NS_NewRunnableFunction with copyable&movable function rvalue, moves", - moveCounter, 1); - } - - // Test NS_NewRunnableFunction with copyable-only lambda capture. - { - int copyCounter = 0; - { - nsCOMPtr trackedRunnable; - { - TestCopyWithNoMove tracker(copyCounter); - // Expect 2 copies (here -> local lambda -> runnable lambda). - trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); }); - } - } - result |= Expect("NS_NewRunnableFunction with copyable-only (and no move) capture, copies", - copyCounter, 2); - } - { - int copyCounter = 0; - { - nsCOMPtr trackedRunnable; - { - TestCopyWithDeletedMove tracker(copyCounter); - // Expect 2 copies (here -> local lambda -> runnable lambda). - trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); }); - } - } - result |= Expect("NS_NewRunnableFunction with copyable-only (and deleted move) capture, copies", - copyCounter, 2); - } - - // Note: Not possible to use move-only captures. - // (Until we can use C++14 generalized lambda captures) - - // Test NS_NewRunnableFunction with copyable&movable lambda capture. - { - int copyCounter = 0; - int moveCounter = 0; - { - nsCOMPtr trackedRunnable; - { - TestCopyMove tracker(copyCounter, moveCounter); - trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); }); - // Expect 1 copy (here -> local lambda) and 1 move (local -> runnable lambda). - } - } - result |= Expect("NS_NewRunnableFunction with copyable&movable capture, copies", - copyCounter, 1); - result |= Expect("NS_NewRunnableFunction with copyable&movable capture, moves", - moveCounter, 1); - } - - return result; -} - int main(int argc, char** argv) { - int result = TestNS_NewRunnableFunction(); - ScopedXPCOM xpcom("ThreadUtils"); NS_ENSURE_FALSE(xpcom.failed(), 1); @@ -367,6 +168,8 @@ int main(int argc, char** argv) NS_ProcessPendingEvents(nullptr); } + int result = 0; + for (uint32_t i = 0; i < MAX_TESTS; i++) { if (gRunnableExecuted[i]) { passed("Test %d passed",i); From 83f9dc03f4f060074e75ef6cab61762d647177f3 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 31 May 2016 21:06:56 +1200 Subject: [PATCH 149/199] Bug 1271525 - Factor out data used by FindD3D11BlacklistedDLL() into arguments passed in. r=kentuckyfriedtakahe This enables us to use the existing D3D11 blacklist code to create a D3D9 blacklist by just passing in a new blacklist cache. MozReview-Commit-ID: GyQ8o8U1jwg --HG-- extra : rebase_source : e0f1b99d9129c6a8bf6ca70001593194291a83b1 --- .../platforms/wmf/WMFVideoMFTManager.cpp | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp index 4485bb85bf81..51d94f79ab88 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp @@ -153,46 +153,46 @@ WMFVideoMFTManager::GetMediaSubtypeGUID() }; } -struct D3D11BlacklistingCache +struct D3DDLLBlacklistingCache { - // D3D11-blacklist pref last seen. + // Blacklist pref value last seen. nsCString mBlacklistPref; - // Non-empty if a D3D11-blacklisted DLL was found. + // Non-empty if a blacklisted DLL was found. nsCString mBlacklistedDLL; }; -StaticAutoPtr sD3D11BlacklistingCache; +StaticAutoPtr sD3D11BlacklistingCache; // If a blacklisted DLL is found, return its information, otherwise "". static const nsACString& -FindD3D11BlacklistedDLL() +FindDXVABlacklistedDLL(StaticAutoPtr& aDLLBlacklistingCache, + const char* aDLLBlacklistPrefName) { NS_ASSERTION(NS_IsMainThread(), "Must be on main thread."); - if (!sD3D11BlacklistingCache) { + if (!aDLLBlacklistingCache) { // First time here, create persistent data that will be reused in all // D3D11-blacklisting checks. - sD3D11BlacklistingCache = new D3D11BlacklistingCache(); - ClearOnShutdown(&sD3D11BlacklistingCache); + aDLLBlacklistingCache = new D3DDLLBlacklistingCache(); + ClearOnShutdown(&aDLLBlacklistingCache); } - nsAdoptingCString blacklist = - Preferences::GetCString("media.wmf.disable-d3d11-for-dlls"); + nsAdoptingCString blacklist = Preferences::GetCString(aDLLBlacklistPrefName); if (blacklist.IsEmpty()) { // Empty blacklist -> No blacklisting. - sD3D11BlacklistingCache->mBlacklistPref.SetLength(0); - sD3D11BlacklistingCache->mBlacklistedDLL.SetLength(0); - return sD3D11BlacklistingCache->mBlacklistedDLL; + aDLLBlacklistingCache->mBlacklistPref.SetLength(0); + aDLLBlacklistingCache->mBlacklistedDLL.SetLength(0); + return aDLLBlacklistingCache->mBlacklistedDLL; } // Detect changes in pref. - if (sD3D11BlacklistingCache->mBlacklistPref.Equals(blacklist)) { + if (aDLLBlacklistingCache->mBlacklistPref.Equals(blacklist)) { // Same blacklist -> Return same result (i.e., don't check DLLs again). - return sD3D11BlacklistingCache->mBlacklistedDLL; + return aDLLBlacklistingCache->mBlacklistedDLL; } // Adopt new pref now, so we don't work on it again. - sD3D11BlacklistingCache->mBlacklistPref = blacklist; + aDLLBlacklistingCache->mBlacklistPref = blacklist; - // media.wmf.disable-d3d11-for-dlls format: (whitespace is trimmed) + // media.wmf.disable-d3d*-for-dlls format: (whitespace is trimmed) // "dll1.dll: 1.2.3.4[, more versions...][; more dlls...]" nsTArray dlls; SplitAt(";", blacklist, dlls); @@ -200,7 +200,8 @@ FindD3D11BlacklistedDLL() nsTArray nameAndVersions; SplitAt(":", dll, nameAndVersions); if (nameAndVersions.Length() != 2) { - NS_WARNING("Skipping incorrect 'media.wmf.disable-d3d11-for-dlls' dll:versions format"); + NS_WARNING(nsPrintfCString("Skipping incorrect '%s' dll:versions format", + aDLLBlacklistPrefName).get()); continue; } @@ -235,7 +236,8 @@ FindD3D11BlacklistedDLL() nsTArray numberStrings; SplitAt(".", version, numberStrings); if (numberStrings.Length() != 4) { - NS_WARNING("Skipping incorrect 'media.wmf.disable-d3d11-for-dlls' a.b.c.d version format"); + NS_WARNING(nsPrintfCString("Skipping incorrect '%s' a.b.c.d version format", + aDLLBlacklistPrefName).get()); continue; } DWORD numbers[4]; @@ -253,25 +255,32 @@ FindD3D11BlacklistedDLL() } if (NS_FAILED(errorCode)) { - NS_WARNING("Skipping incorrect 'media.wmf.disable-d3d11-for-dlls' a.b.c.d version format"); + NS_WARNING(nsPrintfCString("Skipping incorrect '%s' a.b.c.d version format", + aDLLBlacklistPrefName).get()); continue; } if (vInfo->dwFileVersionMS == ((numbers[0] << 16) | numbers[1]) && vInfo->dwFileVersionLS == ((numbers[2] << 16) | numbers[3])) { // Blacklisted! Record bad DLL. - sD3D11BlacklistingCache->mBlacklistedDLL.SetLength(0); - sD3D11BlacklistingCache->mBlacklistedDLL.AppendPrintf( + aDLLBlacklistingCache->mBlacklistedDLL.SetLength(0); + aDLLBlacklistingCache->mBlacklistedDLL.AppendPrintf( "%s (%lu.%lu.%lu.%lu)", nameAndVersions[0].get(), numbers[0], numbers[1], numbers[2], numbers[3]); - return sD3D11BlacklistingCache->mBlacklistedDLL; + return aDLLBlacklistingCache->mBlacklistedDLL; } } } // No blacklisted DLL. - sD3D11BlacklistingCache->mBlacklistedDLL.SetLength(0); - return sD3D11BlacklistingCache->mBlacklistedDLL; + aDLLBlacklistingCache->mBlacklistedDLL.SetLength(0); + return aDLLBlacklistingCache->mBlacklistedDLL; +} + +static const nsACString& +FindD3D11BlacklistedDLL() { + return FindDXVABlacklistedDLL(sD3D11BlacklistingCache, + "media.wmf.disable-d3d11-for-dlls"); } class CreateDXVAManagerEvent : public Runnable { From f41e85bf8a662763d1772f4ca9ea6976a516f871 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 31 May 2016 21:08:03 +1200 Subject: [PATCH 150/199] Bug 1271525 - Add ability to blacklist D3D9 DXVA via pref. r=kentuckyfriedtakahe MozReview-Commit-ID: HSm6TTc6kct --HG-- extra : rebase_source : cacbcef2352f6e4897845a5fdb3373c60571e938 --- .../platforms/wmf/WMFVideoMFTManager.cpp | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp index 51d94f79ab88..03766dd3a693 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp @@ -161,6 +161,7 @@ struct D3DDLLBlacklistingCache nsCString mBlacklistedDLL; }; StaticAutoPtr sD3D11BlacklistingCache; +StaticAutoPtr sD3D9BlacklistingCache; // If a blacklisted DLL is found, return its information, otherwise "". static const nsACString& @@ -283,6 +284,12 @@ FindD3D11BlacklistedDLL() { "media.wmf.disable-d3d11-for-dlls"); } +static const nsACString& +FindD3D9BlacklistedDLL() { + return FindDXVABlacklistedDLL(sD3D9BlacklistingCache, + "media.wmf.disable-d3d9-for-dlls"); +} + class CreateDXVAManagerEvent : public Runnable { public: CreateDXVAManagerEvent(LayersBackend aBackend, nsCString& aFailureReason) @@ -311,9 +318,16 @@ public: failureReason = &secondFailureReason; mFailureReason.Append(NS_LITERAL_CSTRING("; ")); } - mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA(*failureReason); - // Make sure we include the messages from both attempts (if applicable). - mFailureReason.Append(secondFailureReason); + + const nsACString& blacklistedDLL = FindD3D9BlacklistedDLL(); + if (!blacklistedDLL.IsEmpty()) { + mFailureReason.AppendPrintf("D3D9 blacklisted with DLL %s", + blacklistedDLL); + } else { + mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA(*failureReason); + // Make sure we include the messages from both attempts (if applicable). + mFailureReason.Append(secondFailureReason); + } return NS_OK; } nsAutoPtr mDXVA2Manager; From 9da01872ae27391c8cd188685072191ffbf7e1c9 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 31 May 2016 21:08:08 +1200 Subject: [PATCH 151/199] Bug 1271525 - Blacklist igdumd64.dll versions for D3D9 DXVA which are suspected to crash. r=kentuckyfriedtakahe MozReview-Commit-ID: B4RET7bldTT --HG-- extra : rebase_source : 41c0ae161f6043bea9e7a201cdaeb31ee8ccd063 --- modules/libpref/init/all.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index d8aeab116837..e7c8d0d83d58 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -326,6 +326,7 @@ pref("media.wmf.low-latency.enabled", false); pref("media.wmf.skip-blacklist", false); pref("media.windows-media-foundation.allow-d3d11-dxva", true); pref("media.wmf.disable-d3d11-for-dlls", "igd10iumd32.dll: 20.19.15.4444, 20.19.15.4424, 20.19.15.4409, 20.19.15.4390, 20.19.15.4380, 20.19.15.4360, 10.18.10.4358, 20.19.15.4331, 20.19.15.4312, 20.19.15.4300, 10.18.15.4281, 10.18.15.4279, 10.18.10.4276, 10.18.15.4268, 10.18.15.4256, 10.18.10.4252, 10.18.15.4248, 10.18.14.4112, 10.18.10.3958, 10.18.10.3496, 10.18.10.3431, 10.18.10.3412, 10.18.10.3355, 9.18.10.3234, 9.18.10.3071, 9.18.10.3055, 9.18.10.3006; igd10umd32.dll: 9.17.10.4229, 9.17.10.3040, 9.17.10.2857, 8.15.10.2274, 8.15.10.2272, 8.15.10.2246, 8.15.10.1840, 8.15.10.1808; igd10umd64.dll: 9.17.10.4229, 10.18.10.3496; isonyvideoprocessor.dll: 4.1.2247.8090, 4.1.2153.6200; tosqep.dll: 1.2.15.526, 1.1.12.201, 1.0.11.318, 1.0.11.215, 1.0.10.1224; tosqep64.dll: 1.1.12.201, 1.0.11.215; nvwgf2um.dll: 10.18.13.6510, 10.18.13.5891, 10.18.13.5887, 10.18.13.5582, 10.18.13.5382, 9.18.13.4195, 9.18.13.3165; atidxx32.dll: 8.17.10.671, 8.17.10.661, 8.17.10.648, 8.17.10.644, 8.17.10.625, 8.17.10.605, 8.17.10.581, 8.17.10.569, 8.17.10.560, 8.17.10.545, 8.17.10.539, 8.17.10.531, 8.17.10.525, 8.17.10.520, 8.17.10.519, 8.17.10.514, 8.17.10.511, 8.17.10.494, 8.17.10.489, 8.17.10.483, 8.17.10.453, 8.17.10.451, 8.17.10.441, 8.17.10.436, 8.17.10.432, 8.17.10.425, 8.17.10.418, 8.17.10.414, 8.17.10.401, 8.17.10.395, 8.17.10.385, 8.17.10.378, 8.17.10.362, 8.17.10.355, 8.17.10.342, 8.17.10.331, 8.17.10.318, 8.17.10.310, 8.17.10.286, 8.17.10.269, 8.17.10.261, 8.17.10.247, 8.17.10.240, 8.15.10.212; atidxx64.dll: 8.17.10.661, 8.17.10.644"); +pref("media.wmf.disable-d3d9-for-dlls", "igdumd64.dll: 8.15.10.2189, 8.771.1.0"); #endif #if defined(MOZ_FFMPEG) #if defined(XP_MACOSX) From 9fe123f6012526a76ba619f71edb6317aaa818a3 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 1 Jun 2016 09:13:21 +1000 Subject: [PATCH 152/199] Bug 1276837 (part 1) - Shrink NodePool::Block. r=mccr8. 15% of our "small" OOM crashes are allocations of this struct. Halving its size will hopefully help reduce that. --HG-- extra : rebase_source : 5480a540a9584899def045c1401a5aa9a271d72c --- xpcom/base/nsCycleCollector.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 9730dbd8e899..0c3f0f445556 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -627,14 +627,14 @@ public: /** * A structure designed to be used like a linked list of PtrInfo, except - * that allocates the PtrInfo 32K-at-a-time. + * it allocates many PtrInfos at a time. */ class NodePool { private: // The -2 allows us to use |BlockSize + 1| for |mEntries|, and fit |mNext|, // all without causing slop. - enum { BlockSize = 8 * 1024 - 2 }; + enum { BlockSize = 4 * 1024 - 2 }; struct Block { @@ -647,8 +647,8 @@ private: // Ensure Block is the right size (see the comment on BlockSize above). static_assert( - sizeof(Block) == 163824 || // 32-bit; equals 39.997 pages - sizeof(Block) == 262120, // 64-bit; equals 63.994 pages + sizeof(Block) == 81904 || // 32-bit; equals 19.996 x 4 KiB pages + sizeof(Block) == 131048, // 64-bit; equals 31.994 x 4 KiB pages "ill-sized NodePool::Block" ); } From a6bf1a62c25032f9a131e0da14a32ec8d8146ec9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 1 Jun 2016 09:18:33 +1000 Subject: [PATCH 153/199] Bug 1276837 (part 2) - Rename the CC Block structs. r=mccr8. nsCycleCollector.cpp has three different structs named "Block", which makes it hard to read. This patch renames them as EdgeBlock, NodeBlock, and PurpleBlock. --HG-- extra : rebase_source : cae17ba925559b625f65a4741ae0bb4bee99d078 --- xpcom/base/nsCycleCollector.cpp | 122 ++++++++++++++++---------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 0c3f0f445556..1728efb81a75 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -368,9 +368,9 @@ public: void Clear() { - Block* b = Blocks(); + EdgeBlock* b = EdgeBlocks(); while (b) { - Block* next = b->Next(); + EdgeBlock* next = b->Next(); delete b; b = next; } @@ -388,27 +388,27 @@ public: #endif private: - struct Block; + struct EdgeBlock; union PtrInfoOrBlock { // Use a union to avoid reinterpret_cast and the ensuing // potential aliasing bugs. PtrInfo* ptrInfo; - Block* block; + EdgeBlock* block; }; - struct Block + struct EdgeBlock { - enum { BlockSize = 16 * 1024 }; + enum { EdgeBlockSize = 16 * 1024 }; - PtrInfoOrBlock mPointers[BlockSize]; - Block() + PtrInfoOrBlock mPointers[EdgeBlockSize]; + EdgeBlock() { - mPointers[BlockSize - 2].block = nullptr; // sentinel - mPointers[BlockSize - 1].block = nullptr; // next block pointer + mPointers[EdgeBlockSize - 2].block = nullptr; // sentinel + mPointers[EdgeBlockSize - 1].block = nullptr; // next block pointer } - Block*& Next() + EdgeBlock*& Next() { - return mPointers[BlockSize - 1].block; + return mPointers[EdgeBlockSize - 1].block; } PtrInfoOrBlock* Start() { @@ -416,7 +416,7 @@ private: } PtrInfoOrBlock* End() { - return &mPointers[BlockSize - 2]; + return &mPointers[EdgeBlockSize - 2]; } }; @@ -424,11 +424,11 @@ private: // before adding any edges and without adding any blocks. PtrInfoOrBlock mSentinelAndBlocks[2]; - Block*& Blocks() + EdgeBlock*& EdgeBlocks() { return mSentinelAndBlocks[1].block; } - Block* Blocks() const + EdgeBlock* EdgeBlocks() const { return mSentinelAndBlocks[1].block; } @@ -487,7 +487,7 @@ public: explicit Builder(EdgePool& aPool) : mCurrent(&aPool.mSentinelAndBlocks[0]) , mBlockEnd(&aPool.mSentinelAndBlocks[0]) - , mNextBlockPtr(&aPool.Blocks()) + , mNextBlockPtr(&aPool.EdgeBlocks()) { } @@ -499,7 +499,7 @@ public: void Add(PtrInfo* aEdge) { if (mCurrent == mBlockEnd) { - Block* b = new Block(); + EdgeBlock* b = new EdgeBlock(); *mNextBlockPtr = b; mCurrent = b->Start(); mBlockEnd = b->End(); @@ -511,13 +511,13 @@ public: // mBlockEnd points to space for null sentinel PtrInfoOrBlock* mCurrent; PtrInfoOrBlock* mBlockEnd; - Block** mNextBlockPtr; + EdgeBlock** mNextBlockPtr; }; size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = 0; - Block* b = Blocks(); + EdgeBlock* b = EdgeBlocks(); while (b) { n += aMallocSizeOf(b); b = b->Next(); @@ -577,7 +577,7 @@ public: MOZ_ASSERT(!IsGrayJS() && !IsBlackJS()); } - // Allow NodePool::Block's constructor to compile. + // Allow NodePool::NodeBlock's constructor to compile. PtrInfo() { NS_NOTREACHED("should never be called"); @@ -632,33 +632,33 @@ public: class NodePool { private: - // The -2 allows us to use |BlockSize + 1| for |mEntries|, and fit |mNext|, - // all without causing slop. - enum { BlockSize = 4 * 1024 - 2 }; + // The -2 allows us to use |NodeBlockSize + 1| for |mEntries|, and fit + // |mNext|, all without causing slop. + enum { NodeBlockSize = 4 * 1024 - 2 }; - struct Block + struct NodeBlock { - // We create and destroy Block using moz_xmalloc/free rather - // than new and delete to avoid calling its constructor and - // destructor. - Block() + // We create and destroy NodeBlock using moz_xmalloc/free rather than new + // and delete to avoid calling its constructor and destructor. + NodeBlock() { NS_NOTREACHED("should never be called"); - // Ensure Block is the right size (see the comment on BlockSize above). + // Ensure NodeBlock is the right size (see the comment on NodeBlockSize + // above). static_assert( - sizeof(Block) == 81904 || // 32-bit; equals 19.996 x 4 KiB pages - sizeof(Block) == 131048, // 64-bit; equals 31.994 x 4 KiB pages - "ill-sized NodePool::Block" + sizeof(NodeBlock) == 81904 || // 32-bit; equals 19.996 x 4 KiB pages + sizeof(NodeBlock) == 131048, // 64-bit; equals 31.994 x 4 KiB pages + "ill-sized NodeBlock" ); } - ~Block() + ~NodeBlock() { NS_NOTREACHED("should never be called"); } - Block* mNext; - PtrInfo mEntries[BlockSize + 1]; // +1 to store last child of last node + NodeBlock* mNext; + PtrInfo mEntries[NodeBlockSize + 1]; // +1 to store last child of last node }; public: @@ -675,9 +675,9 @@ public: void Clear() { - Block* b = mBlocks; + NodeBlock* b = mBlocks; while (b) { - Block* n = b->mNext; + NodeBlock* n = b->mNext; free(b); b = n; } @@ -708,21 +708,21 @@ public: PtrInfo* Add(void* aPointer, nsCycleCollectionParticipant* aParticipant) { if (mNext == mBlockEnd) { - Block* block = static_cast(malloc(sizeof(Block))); + NodeBlock* block = static_cast(malloc(sizeof(NodeBlock))); if (!block) { return nullptr; } *mNextBlock = block; mNext = block->mEntries; - mBlockEnd = block->mEntries + BlockSize; + mBlockEnd = block->mEntries + NodeBlockSize; block->mNext = nullptr; mNextBlock = &block->mNext; } return new (mNext++) PtrInfo(aPointer, aParticipant); } private: - Block** mNextBlock; + NodeBlock** mNextBlock; PtrInfo*& mNext; PtrInfo* mBlockEnd; }; @@ -755,9 +755,9 @@ public: { MOZ_ASSERT(!IsDone(), "calling GetNext when done"); if (mNext == mBlockEnd) { - Block* nextBlock = mCurBlock ? mCurBlock->mNext : mFirstBlock; + NodeBlock* nextBlock = mCurBlock ? mCurBlock->mNext : mFirstBlock; mNext = nextBlock->mEntries; - mBlockEnd = mNext + BlockSize; + mBlockEnd = mNext + NodeBlockSize; mCurBlock = nextBlock; } return mNext++; @@ -765,8 +765,8 @@ public: private: // mFirstBlock is a reference to allow an Enumerator to be constructed // for an empty graph. - Block*& mFirstBlock; - Block* mCurBlock; + NodeBlock*& mFirstBlock; + NodeBlock* mCurBlock; // mNext is the next value we want to return, unless mNext == mBlockEnd // NB: mLast is a reference to allow enumerating while building! PtrInfo* mNext; @@ -779,7 +779,7 @@ public: // We don't measure the things pointed to by mEntries[] because those // pointers are non-owning. size_t n = 0; - Block* b = mBlocks; + NodeBlock* b = mBlocks; while (b) { n += aMallocSizeOf(b); b = b->mNext; @@ -788,7 +788,7 @@ public: } private: - Block* mBlocks; + NodeBlock* mBlocks; PtrInfo* mLast; }; @@ -992,9 +992,9 @@ class nsCycleCollector; struct nsPurpleBuffer { private: - struct Block + struct PurpleBlock { - Block* mNext; + PurpleBlock* mNext; // Try to match the size of a jemalloc bucket, to minimize slop bytes. // - On 32-bit platforms sizeof(nsPurpleBufferEntry) is 12, so mEntries // is 16,380 bytes, which leaves 4 bytes for mNext. @@ -1002,13 +1002,13 @@ private: // is 32,544 bytes, which leaves 8 bytes for mNext. nsPurpleBufferEntry mEntries[1365]; - Block() : mNext(nullptr) + PurpleBlock() : mNext(nullptr) { - // Ensure Block is the right size (see above). + // Ensure PurpleBlock is the right size (see above). static_assert( - sizeof(Block) == 16384 || // 32-bit - sizeof(Block) == 32768, // 64-bit - "ill-sized nsPurpleBuffer::Block" + sizeof(PurpleBlock) == 16384 || // 32-bit + sizeof(PurpleBlock) == 32768, // 64-bit + "ill-sized nsPurpleBuffer::PurpleBlock" ); } @@ -1028,7 +1028,7 @@ private: // buffer. uint32_t mCount; - Block mFirstBlock; + PurpleBlock mFirstBlock; nsPurpleBufferEntry* mFreeList; public: @@ -1045,7 +1045,7 @@ public: template void VisitEntries(PurpleVisitor& aVisitor) { - for (Block* b = &mFirstBlock; b; b = b->mNext) { + for (PurpleBlock* b = &mFirstBlock; b; b = b->mNext) { b->VisitEntries(*this, aVisitor); } } @@ -1057,7 +1057,7 @@ public: StartBlock(&mFirstBlock); } - void StartBlock(Block* aBlock) + void StartBlock(PurpleBlock* aBlock) { MOZ_ASSERT(!mFreeList, "should not have free list"); @@ -1077,12 +1077,12 @@ public: if (mCount > 0) { UnmarkRemainingPurple(&mFirstBlock); } - Block* b = mFirstBlock.mNext; + PurpleBlock* b = mFirstBlock.mNext; while (b) { if (mCount > 0) { UnmarkRemainingPurple(b); } - Block* next = b->mNext; + PurpleBlock* next = b->mNext; delete b; b = next; } @@ -1103,7 +1103,7 @@ public: } }; - void UnmarkRemainingPurple(Block* aBlock) + void UnmarkRemainingPurple(PurpleBlock* aBlock) { UnmarkRemainingPurpleVisitor visitor; aBlock->VisitEntries(*this, visitor); @@ -1126,7 +1126,7 @@ public: MOZ_ALWAYS_INLINE nsPurpleBufferEntry* NewEntry() { if (MOZ_UNLIKELY(!mFreeList)) { - Block* b = new Block; + PurpleBlock* b = new PurpleBlock; StartBlock(b); // Add the new block as the second block in the list. @@ -1177,7 +1177,7 @@ public: size_t n = 0; // Don't measure mFirstBlock because it's within |this|. - const Block* block = mFirstBlock.mNext; + const PurpleBlock* block = mFirstBlock.mNext; while (block) { n += aMallocSizeOf(block); block = block->mNext; From 5c01d7c3eed6ae43f2c43828ab1129ef5d999f29 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Wed, 1 Jun 2016 10:04:54 +1000 Subject: [PATCH 154/199] Bug 1276549 - Remove rvalue reference mark for already_AddRefed params of nsIEventTarget::Dispatch and related methods. r=froydnj MozReview-Commit-ID: J5RAfGW3X7T --HG-- extra : source : e1ff4b0d5d9d2599ac00bac376597357ffd58ce0 --- dom/base/WebSocket.cpp | 8 ++++---- dom/canvas/WebGLContextLossHandler.cpp | 4 ++-- dom/workers/WorkerPrivate.cpp | 16 ++++++++-------- dom/workers/WorkerPrivate.h | 14 +++++++------- dom/workers/WorkerThread.cpp | 8 ++++---- dom/workers/WorkerThread.h | 8 ++++---- netwerk/base/nsSocketTransportService2.cpp | 4 ++-- netwerk/base/nsStreamTransportService.cpp | 4 ++-- xpcom/threads/LazyIdleThread.cpp | 4 ++-- xpcom/threads/SharedThreadPool.h | 4 ++-- xpcom/threads/nsIEventTarget.idl | 2 +- xpcom/threads/nsThread.cpp | 12 ++++++------ xpcom/threads/nsThread.h | 6 +++--- xpcom/threads/nsThreadPool.cpp | 6 +++--- xpcom/threads/nsThreadPool.h | 2 +- 15 files changed, 51 insertions(+), 51 deletions(-) diff --git a/dom/base/WebSocket.cpp b/dom/base/WebSocket.cpp index 09e109c63553..b089654f9a61 100644 --- a/dom/base/WebSocket.cpp +++ b/dom/base/WebSocket.cpp @@ -2719,10 +2719,10 @@ class WorkerRunnableDispatcher final : public WorkerRunnable public: WorkerRunnableDispatcher(WebSocketImpl* aImpl, WorkerPrivate* aWorkerPrivate, - already_AddRefed&& aEvent) + already_AddRefed aEvent) : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) , mWebSocketImpl(aImpl) - , mEvent(aEvent) + , mEvent(Move(aEvent)) { } @@ -2779,7 +2779,7 @@ WebSocketImpl::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) } NS_IMETHODIMP -WebSocketImpl::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) +WebSocketImpl::Dispatch(already_AddRefed aEvent, uint32_t aFlags) { nsCOMPtr event_ref(aEvent); // If the target is the main-thread we can just dispatch the runnable. @@ -2811,7 +2811,7 @@ WebSocketImpl::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) } NS_IMETHODIMP -WebSocketImpl::DelayedDispatch(already_AddRefed&&, uint32_t) +WebSocketImpl::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/dom/canvas/WebGLContextLossHandler.cpp b/dom/canvas/WebGLContextLossHandler.cpp index 771ce4c31c6c..caea5026519c 100644 --- a/dom/canvas/WebGLContextLossHandler.cpp +++ b/dom/canvas/WebGLContextLossHandler.cpp @@ -68,7 +68,7 @@ ContextLossWorkerEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t a } NS_IMETHODIMP -ContextLossWorkerEventTarget::Dispatch(already_AddRefed&& aEvent, +ContextLossWorkerEventTarget::Dispatch(already_AddRefed aEvent, uint32_t aFlags) { nsCOMPtr eventRef(aEvent); @@ -78,7 +78,7 @@ ContextLossWorkerEventTarget::Dispatch(already_AddRefed&& aEvent, } NS_IMETHODIMP -ContextLossWorkerEventTarget::DelayedDispatch(already_AddRefed&&, +ContextLossWorkerEventTarget::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 4c581b0b67a7..70c79e4a31b1 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -1828,7 +1828,7 @@ TimerThreadEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFla } NS_IMETHODIMP -TimerThreadEventTarget::Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags) +TimerThreadEventTarget::Dispatch(already_AddRefed aRunnable, uint32_t aFlags) { // This should only happen on the timer thread. MOZ_ASSERT(!NS_IsMainThread()); @@ -1850,7 +1850,7 @@ TimerThreadEventTarget::Dispatch(already_AddRefed&& aRunnable, uint } NS_IMETHODIMP -TimerThreadEventTarget::DelayedDispatch(already_AddRefed&&, uint32_t) +TimerThreadEventTarget::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; } @@ -2402,7 +2402,7 @@ WorkerPrivateParent::WrapObject(JSContext* aCx, JS::Handle a template nsresult -WorkerPrivateParent::DispatchPrivate(already_AddRefed&& aRunnable, +WorkerPrivateParent::DispatchPrivate(already_AddRefed aRunnable, nsIEventTarget* aSyncLoopTarget) { // May be called on any thread! @@ -2480,7 +2480,7 @@ WorkerPrivateParent::DisableDebugger() template nsresult WorkerPrivateParent::DispatchControlRunnable( - already_AddRefed&& aWorkerControlRunnable) + already_AddRefed aWorkerControlRunnable) { // May be called on any thread! RefPtr runnable(aWorkerControlRunnable); @@ -2518,7 +2518,7 @@ WorkerPrivateParent::DispatchControlRunnable( template nsresult WorkerPrivateParent::DispatchDebuggerRunnable( - already_AddRefed&& aDebuggerRunnable) + already_AddRefed aDebuggerRunnable) { // May be called on any thread! @@ -2548,7 +2548,7 @@ WorkerPrivateParent::DispatchDebuggerRunnable( template already_AddRefed -WorkerPrivateParent::MaybeWrapAsWorkerRunnable(already_AddRefed&& aRunnable) +WorkerPrivateParent::MaybeWrapAsWorkerRunnable(already_AddRefed aRunnable) { // May be called on any thread! @@ -6738,7 +6738,7 @@ EventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) template NS_IMETHODIMP WorkerPrivateParent:: -EventTarget::Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags) +EventTarget::Dispatch(already_AddRefed aRunnable, uint32_t aFlags) { // May be called on any thread! nsCOMPtr event(aRunnable); @@ -6774,7 +6774,7 @@ EventTarget::Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags template NS_IMETHODIMP WorkerPrivateParent:: -EventTarget::DelayedDispatch(already_AddRefed&&, uint32_t) +EventTarget::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 2745d24a1d20..7a538d05c3d7 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -238,7 +238,7 @@ private: ErrorResult& aRv); nsresult - DispatchPrivate(already_AddRefed&& aRunnable, nsIEventTarget* aSyncLoopTarget); + DispatchPrivate(already_AddRefed aRunnable, nsIEventTarget* aSyncLoopTarget); public: virtual JSObject* @@ -263,19 +263,19 @@ public: } nsresult - Dispatch(already_AddRefed&& aRunnable) + Dispatch(already_AddRefed aRunnable) { return DispatchPrivate(Move(aRunnable), nullptr); } nsresult - DispatchControlRunnable(already_AddRefed&& aWorkerControlRunnable); + DispatchControlRunnable(already_AddRefed aWorkerControlRunnable); nsresult - DispatchDebuggerRunnable(already_AddRefed&& aDebuggerRunnable); + DispatchDebuggerRunnable(already_AddRefed aDebuggerRunnable); already_AddRefed - MaybeWrapAsWorkerRunnable(already_AddRefed&& aRunnable); + MaybeWrapAsWorkerRunnable(already_AddRefed aRunnable); already_AddRefed GetEventTarget(); @@ -1538,10 +1538,10 @@ protected: NS_IMETHOD - Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags) override; + Dispatch(already_AddRefed aRunnable, uint32_t aFlags) override; NS_IMETHOD - DelayedDispatch(already_AddRefed&&, uint32_t) override; + DelayedDispatch(already_AddRefed, uint32_t) override; NS_IMETHOD IsOnCurrentThread(bool* aIsOnCurrentThread) override; diff --git a/dom/workers/WorkerThread.cpp b/dom/workers/WorkerThread.cpp index 893e6b958346..4e79a72c2a16 100644 --- a/dom/workers/WorkerThread.cpp +++ b/dom/workers/WorkerThread.cpp @@ -145,7 +145,7 @@ WorkerThread::SetWorker(const WorkerThreadFriendKey& /* aKey */, nsresult WorkerThread::DispatchPrimaryRunnable(const WorkerThreadFriendKey& /* aKey */, - already_AddRefed&& aRunnable) + already_AddRefed aRunnable) { nsCOMPtr runnable(aRunnable); @@ -170,7 +170,7 @@ WorkerThread::DispatchPrimaryRunnable(const WorkerThreadFriendKey& /* aKey */, nsresult WorkerThread::DispatchAnyThread(const WorkerThreadFriendKey& /* aKey */, - already_AddRefed&& aWorkerRunnable) + already_AddRefed aWorkerRunnable) { // May be called on any thread! @@ -213,7 +213,7 @@ WorkerThread::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) } NS_IMETHODIMP -WorkerThread::Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags) +WorkerThread::Dispatch(already_AddRefed aRunnable, uint32_t aFlags) { // May be called on any thread! nsCOMPtr runnable(aRunnable); // in case we exit early @@ -299,7 +299,7 @@ WorkerThread::Dispatch(already_AddRefed&& aRunnable, uint32_t aFlag } NS_IMETHODIMP -WorkerThread::DelayedDispatch(already_AddRefed&&, uint32_t) +WorkerThread::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/dom/workers/WorkerThread.h b/dom/workers/WorkerThread.h index f80a589a014d..d1df13332d97 100644 --- a/dom/workers/WorkerThread.h +++ b/dom/workers/WorkerThread.h @@ -68,11 +68,11 @@ public: nsresult DispatchPrimaryRunnable(const WorkerThreadFriendKey& aKey, - already_AddRefed&& aRunnable); + already_AddRefed aRunnable); nsresult DispatchAnyThread(const WorkerThreadFriendKey& aKey, - already_AddRefed&& aWorkerRunnable); + already_AddRefed aWorkerRunnable); uint32_t RecursionDepth(const WorkerThreadFriendKey& aKey) const; @@ -86,13 +86,13 @@ private: // This should only be called by consumers that have an // nsIEventTarget/nsIThread pointer. NS_IMETHOD - Dispatch(already_AddRefed&& aRunnable, uint32_t aFlags) override; + Dispatch(already_AddRefed aRunnable, uint32_t aFlags) override; NS_IMETHOD DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override; NS_IMETHOD - DelayedDispatch(already_AddRefed&&, uint32_t) override; + DelayedDispatch(already_AddRefed, uint32_t) override; }; } // namespace workers diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index 4107ca2d2336..ed20ea24542c 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -161,7 +161,7 @@ nsSocketTransportService::DispatchFromScript(nsIRunnable *event, uint32_t flags) } NS_IMETHODIMP -nsSocketTransportService::Dispatch(already_AddRefed&& event, uint32_t flags) +nsSocketTransportService::Dispatch(already_AddRefed event, uint32_t flags) { nsCOMPtr event_ref(event); SOCKET_LOG(("STS dispatch [%p]\n", event_ref.get())); @@ -178,7 +178,7 @@ nsSocketTransportService::Dispatch(already_AddRefed&& event, uint32 } NS_IMETHODIMP -nsSocketTransportService::DelayedDispatch(already_AddRefed&&, uint32_t) +nsSocketTransportService::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/netwerk/base/nsStreamTransportService.cpp b/netwerk/base/nsStreamTransportService.cpp index 52d2a35df4d6..23ca3b860008 100644 --- a/netwerk/base/nsStreamTransportService.cpp +++ b/netwerk/base/nsStreamTransportService.cpp @@ -514,7 +514,7 @@ nsStreamTransportService::DispatchFromScript(nsIRunnable *task, uint32_t flags) } NS_IMETHODIMP -nsStreamTransportService::Dispatch(already_AddRefed&& task, uint32_t flags) +nsStreamTransportService::Dispatch(already_AddRefed task, uint32_t flags) { nsCOMPtr event(task); // so it gets released on failure paths nsCOMPtr pool; @@ -530,7 +530,7 @@ nsStreamTransportService::Dispatch(already_AddRefed&& task, uint32_ } NS_IMETHODIMP -nsStreamTransportService::DelayedDispatch(already_AddRefed&&, uint32_t) +nsStreamTransportService::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/xpcom/threads/LazyIdleThread.cpp b/xpcom/threads/LazyIdleThread.cpp index 2c744977f263..3b42b5a06777 100644 --- a/xpcom/threads/LazyIdleThread.cpp +++ b/xpcom/threads/LazyIdleThread.cpp @@ -398,7 +398,7 @@ LazyIdleThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) } NS_IMETHODIMP -LazyIdleThread::Dispatch(already_AddRefed&& aEvent, +LazyIdleThread::Dispatch(already_AddRefed aEvent, uint32_t aFlags) { ASSERT_OWNING_THREAD(); @@ -431,7 +431,7 @@ LazyIdleThread::Dispatch(already_AddRefed&& aEvent, } NS_IMETHODIMP -LazyIdleThread::DelayedDispatch(already_AddRefed&&, uint32_t) +LazyIdleThread::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/xpcom/threads/SharedThreadPool.h b/xpcom/threads/SharedThreadPool.h index 83835d9165de..2d4aeca19f25 100644 --- a/xpcom/threads/SharedThreadPool.h +++ b/xpcom/threads/SharedThreadPool.h @@ -64,10 +64,10 @@ public: return Dispatch(event, flags); } - NS_IMETHOD Dispatch(already_AddRefed&& event, uint32_t flags) override + NS_IMETHOD Dispatch(already_AddRefed event, uint32_t flags) override { return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->Dispatch(Move(event), flags); } - NS_IMETHOD DelayedDispatch(already_AddRefed&&, uint32_t) override + NS_IMETHOD DelayedDispatch(already_AddRefed, uint32_t) override { return NS_ERROR_NOT_IMPLEMENTED; } using nsIEventTarget::Dispatch; diff --git a/xpcom/threads/nsIEventTarget.idl b/xpcom/threads/nsIEventTarget.idl index 3d9d15817dbc..3088b72ccfa1 100644 --- a/xpcom/threads/nsIEventTarget.idl +++ b/xpcom/threads/nsIEventTarget.idl @@ -11,7 +11,7 @@ #include "mozilla/AlreadyAddRefed.h" %} -native alreadyAddRefed_nsIRunnable(already_AddRefed&&); +native alreadyAddRefed_nsIRunnable(already_AddRefed); [scriptable, uuid(88145945-3278-424e-9f37-d874cbdd9f6f)] interface nsIEventTarget : nsISupports diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 4e125c42992e..fe0b7b867456 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -636,7 +636,7 @@ nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget) } nsresult -nsThread::PutEvent(already_AddRefed&& aEvent, nsNestedEventTarget* aTarget) +nsThread::PutEvent(already_AddRefed aEvent, nsNestedEventTarget* aTarget) { // We want to leak the reference when we fail to dispatch it, so that // we won't release the event in a wrong thread. @@ -673,7 +673,7 @@ nsThread::PutEvent(already_AddRefed&& aEvent, nsNestedEventTarget* } nsresult -nsThread::DispatchInternal(already_AddRefed&& aEvent, uint32_t aFlags, +nsThread::DispatchInternal(already_AddRefed aEvent, uint32_t aFlags, nsNestedEventTarget* aTarget) { // We want to leak the reference when we fail to dispatch it, so that @@ -739,7 +739,7 @@ nsThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) } NS_IMETHODIMP -nsThread::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) +nsThread::Dispatch(already_AddRefed aEvent, uint32_t aFlags) { LOG(("THRD(%p) Dispatch [%p %x]\n", this, /* XXX aEvent */nullptr, aFlags)); @@ -747,7 +747,7 @@ nsThread::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) } NS_IMETHODIMP -nsThread::DelayedDispatch(already_AddRefed&& aEvent, uint32_t aDelayMs) +nsThread::DelayedDispatch(already_AddRefed aEvent, uint32_t aDelayMs) { NS_ENSURE_TRUE(!!aDelayMs, NS_ERROR_UNEXPECTED); @@ -1304,7 +1304,7 @@ nsThread::nsNestedEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t } NS_IMETHODIMP -nsThread::nsNestedEventTarget::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) +nsThread::nsNestedEventTarget::Dispatch(already_AddRefed aEvent, uint32_t aFlags) { LOG(("THRD(%p) Dispatch [%p %x] to nested loop %p\n", mThread.get().get(), /*XXX aEvent*/ nullptr, aFlags, this)); @@ -1313,7 +1313,7 @@ nsThread::nsNestedEventTarget::Dispatch(already_AddRefed&& aEvent, } NS_IMETHODIMP -nsThread::nsNestedEventTarget::DelayedDispatch(already_AddRefed&&, uint32_t) +nsThread::nsNestedEventTarget::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h index 457971683255..250e860d704a 100644 --- a/xpcom/threads/nsThread.h +++ b/xpcom/threads/nsThread.h @@ -111,10 +111,10 @@ protected: // Wrappers for event queue methods: nsresult PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget); - nsresult PutEvent(already_AddRefed&& aEvent, + nsresult PutEvent(already_AddRefed aEvent, nsNestedEventTarget* aTarget); - nsresult DispatchInternal(already_AddRefed&& aEvent, + nsresult DispatchInternal(already_AddRefed aEvent, uint32_t aFlags, nsNestedEventTarget* aTarget); struct nsThreadShutdownContext* ShutdownInternal(bool aSync); @@ -140,7 +140,7 @@ protected: mQueue.PutEvent(aEvent, aProofOfLock); } - void PutEvent(already_AddRefed&& aEvent, + void PutEvent(already_AddRefed aEvent, mozilla::MutexAutoLock& aProofOfLock) { mQueue.PutEvent(mozilla::Move(aEvent), aProofOfLock); diff --git a/xpcom/threads/nsThreadPool.cpp b/xpcom/threads/nsThreadPool.cpp index 6760e7bdadb9..55f5d9e7fc8f 100644 --- a/xpcom/threads/nsThreadPool.cpp +++ b/xpcom/threads/nsThreadPool.cpp @@ -68,7 +68,7 @@ nsThreadPool::PutEvent(nsIRunnable* aEvent) } nsresult -nsThreadPool::PutEvent(already_AddRefed&& aEvent, uint32_t aFlags) +nsThreadPool::PutEvent(already_AddRefed aEvent, uint32_t aFlags) { // Avoid spawning a new thread while holding the event queue lock... @@ -248,7 +248,7 @@ nsThreadPool::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) } NS_IMETHODIMP -nsThreadPool::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) +nsThreadPool::Dispatch(already_AddRefed aEvent, uint32_t aFlags) { LOG(("THRD-P(%p) dispatch [%p %x]\n", this, /* XXX aEvent*/ nullptr, aFlags)); @@ -279,7 +279,7 @@ nsThreadPool::Dispatch(already_AddRefed&& aEvent, uint32_t aFlags) } NS_IMETHODIMP -nsThreadPool::DelayedDispatch(already_AddRefed&&, uint32_t) +nsThreadPool::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/xpcom/threads/nsThreadPool.h b/xpcom/threads/nsThreadPool.h index e95c07359e21..3fcbd7c8e998 100644 --- a/xpcom/threads/nsThreadPool.h +++ b/xpcom/threads/nsThreadPool.h @@ -37,7 +37,7 @@ private: void ShutdownThread(nsIThread* aThread); nsresult PutEvent(nsIRunnable* aEvent); - nsresult PutEvent(already_AddRefed&& aEvent, uint32_t aFlags); + nsresult PutEvent(already_AddRefed aEvent, uint32_t aFlags); nsCOMArray mThreads; mozilla::Mutex mMutex; From d06abbe862b850e741d53577597041ef13786ade Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Tue, 31 May 2016 14:40:04 -0700 Subject: [PATCH 155/199] Bug 1275623 - Use "branch" instead of "revision" to pass symbolic revisions; r=jlund robustcheckout barfs on symbolic revisions when using "revision." MozReview-Commit-ID: B7YXqbWG0G1 --HG-- extra : rebase_source : d852930ac24be79004bce978c8ed6542ab58600f --- .../configs/single_locale/ash_android-api-15.py | 8 ++++---- .../mozharness/configs/single_locale/ash_android-api-9.py | 8 ++++---- .../single_locale/mozilla-aurora_android-api-15.py | 8 ++++---- .../configs/single_locale/mozilla-aurora_android-api-9.py | 8 ++++---- .../single_locale/mozilla-central_android-api-15.py | 8 ++++---- .../single_locale/mozilla-central_android-api-9.py | 8 ++++---- .../single_locale/release_mozilla-beta_android_api_15.py | 8 ++++---- .../single_locale/release_mozilla-beta_android_api_9.py | 8 ++++---- .../release_mozilla-release_android_api_15.py | 8 ++++---- .../release_mozilla-release_android_api_9.py | 8 ++++---- .../staging_release_mozilla-release_android_api_15.py | 6 +++--- .../staging_release_mozilla-release_android_api_9.py | 8 ++++---- 12 files changed, 47 insertions(+), 47 deletions(-) diff --git a/testing/mozharness/configs/single_locale/ash_android-api-15.py b/testing/mozharness/configs/single_locale/ash_android-api-15.py index 2f9b862f8cc0..34139181616d 100644 --- a/testing/mozharness/configs/single_locale/ash_android-api-15.py +++ b/testing/mozharness/configs/single_locale/ash_android-api-15.py @@ -28,19 +28,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/projects/ash", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/build/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/build/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/build/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/l10n-central", "hg_l10n_tag": "default", diff --git a/testing/mozharness/configs/single_locale/ash_android-api-9.py b/testing/mozharness/configs/single_locale/ash_android-api-9.py index bb92f1a35711..4d5b3b944173 100644 --- a/testing/mozharness/configs/single_locale/ash_android-api-9.py +++ b/testing/mozharness/configs/single_locale/ash_android-api-9.py @@ -28,19 +28,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/projects/ash", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/build/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/build/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/build/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/l10n-central", "hg_l10n_tag": "default", diff --git a/testing/mozharness/configs/single_locale/mozilla-aurora_android-api-15.py b/testing/mozharness/configs/single_locale/mozilla-aurora_android-api-15.py index 12c3fbc8ccb8..daec7e1e00ed 100644 --- a/testing/mozharness/configs/single_locale/mozilla-aurora_android-api-15.py +++ b/testing/mozharness/configs/single_locale/mozilla-aurora_android-api-15.py @@ -28,19 +28,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/releases/mozilla-aurora", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/build/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/build/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/build/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/releases/l10n/%s" % BRANCH, "hg_l10n_tag": "default", diff --git a/testing/mozharness/configs/single_locale/mozilla-aurora_android-api-9.py b/testing/mozharness/configs/single_locale/mozilla-aurora_android-api-9.py index 99049a82b078..802fbac7fd02 100644 --- a/testing/mozharness/configs/single_locale/mozilla-aurora_android-api-9.py +++ b/testing/mozharness/configs/single_locale/mozilla-aurora_android-api-9.py @@ -28,19 +28,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/releases/mozilla-aurora", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/build/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/build/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/build/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/releases/l10n/%s" % BRANCH, "hg_l10n_tag": "default", diff --git a/testing/mozharness/configs/single_locale/mozilla-central_android-api-15.py b/testing/mozharness/configs/single_locale/mozilla-central_android-api-15.py index 5eb152f416b5..6e4c439e2048 100644 --- a/testing/mozharness/configs/single_locale/mozilla-central_android-api-15.py +++ b/testing/mozharness/configs/single_locale/mozilla-central_android-api-15.py @@ -28,19 +28,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/mozilla-central", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/build/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/build/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/build/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/l10n-central", "hg_l10n_tag": "default", diff --git a/testing/mozharness/configs/single_locale/mozilla-central_android-api-9.py b/testing/mozharness/configs/single_locale/mozilla-central_android-api-9.py index 6252d27045fd..b681656f8123 100644 --- a/testing/mozharness/configs/single_locale/mozilla-central_android-api-9.py +++ b/testing/mozharness/configs/single_locale/mozilla-central_android-api-9.py @@ -28,19 +28,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/mozilla-central", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/build/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/build/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/build/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/l10n-central", "hg_l10n_tag": "default", diff --git a/testing/mozharness/configs/single_locale/release_mozilla-beta_android_api_15.py b/testing/mozharness/configs/single_locale/release_mozilla-beta_android_api_15.py index 891b30822b2b..c85ecb703a31 100644 --- a/testing/mozharness/configs/single_locale/release_mozilla-beta_android_api_15.py +++ b/testing/mozharness/configs/single_locale/release_mozilla-beta_android_api_15.py @@ -29,19 +29,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/releases/mozilla-beta", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/build/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/build/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/build/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/releases/l10n/%s" % BRANCH, "hg_l10n_tag": "default", diff --git a/testing/mozharness/configs/single_locale/release_mozilla-beta_android_api_9.py b/testing/mozharness/configs/single_locale/release_mozilla-beta_android_api_9.py index 8447041b0448..480ab5ffbf0b 100644 --- a/testing/mozharness/configs/single_locale/release_mozilla-beta_android_api_9.py +++ b/testing/mozharness/configs/single_locale/release_mozilla-beta_android_api_9.py @@ -29,19 +29,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/releases/mozilla-beta", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/build/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/build/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/build/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/releases/l10n/%s" % BRANCH, "hg_l10n_tag": "default", diff --git a/testing/mozharness/configs/single_locale/release_mozilla-release_android_api_15.py b/testing/mozharness/configs/single_locale/release_mozilla-release_android_api_15.py index e180edef77ac..7ef4559572f8 100644 --- a/testing/mozharness/configs/single_locale/release_mozilla-release_android_api_15.py +++ b/testing/mozharness/configs/single_locale/release_mozilla-release_android_api_15.py @@ -29,19 +29,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/releases/mozilla-release", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/build/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/build/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/build/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/releases/l10n/%s" % BRANCH, "hg_l10n_tag": "default", diff --git a/testing/mozharness/configs/single_locale/release_mozilla-release_android_api_9.py b/testing/mozharness/configs/single_locale/release_mozilla-release_android_api_9.py index 057d9fb92f57..2a6e2282e427 100644 --- a/testing/mozharness/configs/single_locale/release_mozilla-release_android_api_9.py +++ b/testing/mozharness/configs/single_locale/release_mozilla-release_android_api_9.py @@ -29,19 +29,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/releases/mozilla-release", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/build/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/build/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/build/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/releases/l10n/%s" % BRANCH, "hg_l10n_tag": "default", diff --git a/testing/mozharness/configs/single_locale/staging_release_mozilla-release_android_api_15.py b/testing/mozharness/configs/single_locale/staging_release_mozilla-release_android_api_15.py index ce021a06156f..aeb6dbd40453 100644 --- a/testing/mozharness/configs/single_locale/staging_release_mozilla-release_android_api_15.py +++ b/testing/mozharness/configs/single_locale/staging_release_mozilla-release_android_api_15.py @@ -32,15 +32,15 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/%(user_repo_override)s/mozilla-release", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/%(user_repo_override)s/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/%(user_repo_override)s/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/%(user_repo_override)s/compare-locales", diff --git a/testing/mozharness/configs/single_locale/staging_release_mozilla-release_android_api_9.py b/testing/mozharness/configs/single_locale/staging_release_mozilla-release_android_api_9.py index cc67e59c4ae8..5bfbe3cb0d1a 100644 --- a/testing/mozharness/configs/single_locale/staging_release_mozilla-release_android_api_9.py +++ b/testing/mozharness/configs/single_locale/staging_release_mozilla-release_android_api_9.py @@ -32,19 +32,19 @@ config = { }, "repos": [{ "repo": "https://hg.mozilla.org/%(user_repo_override)s/mozilla-release", - "revision": "default", + "branch": "default", "dest": MOZILLA_DIR, }, { "repo": "https://hg.mozilla.org/%(user_repo_override)s/buildbot-configs", - "revision": "default", + "branch": "default", "dest": "buildbot-configs" }, { "repo": "https://hg.mozilla.org/%(user_repo_override)s/tools", - "revision": "default", + "branch": "default", "dest": "tools" }, { "repo": "https://hg.mozilla.org/%(user_repo_override)s/compare-locales", - "revision": "RELEASE_AUTOMATION" + "branch": "RELEASE_AUTOMATION" }], "hg_l10n_base": "https://hg.mozilla.org/%(user_repo_override)s/", "hg_l10n_tag": "default", From 100757988af4cf17e7a7294d04946f6026c2dbf0 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Tue, 31 May 2016 23:18:04 +0900 Subject: [PATCH 156/199] Bug 1276948 - Remove IMEInputHandler::ConversationIdentifier. r=masayuki This method is for old NSTextInput API and unused now. MozReview-Commit-ID: thcbEExH58 --HG-- extra : rebase_source : 7d9f665d867366990c5aac5a54555cc2c3483828 --- widget/cocoa/TextInputHandler.h | 9 --------- widget/cocoa/TextInputHandler.mm | 27 --------------------------- 2 files changed, 36 deletions(-) diff --git a/widget/cocoa/TextInputHandler.h b/widget/cocoa/TextInputHandler.h index 1b8c9da5809e..7c19ff5a5561 100644 --- a/widget/cocoa/TextInputHandler.h +++ b/widget/cocoa/TextInputHandler.h @@ -777,15 +777,6 @@ public: NSRange& aSelectedRange, NSRange* aReplacementRange = nullptr); - /** - * ConversationIdentifier() returns an ID for the current editor. The ID is - * guaranteed to be unique among currently existing editors. But it might be - * the same as the ID of an editor that has already been destroyed. - * - * @return An identifier of current focused editor. - */ - NSInteger ConversationIdentifier(); - /** * GetAttributedSubstringFromRange() returns an NSAttributedString instance * which is allocated as autorelease for aRange. diff --git a/widget/cocoa/TextInputHandler.mm b/widget/cocoa/TextInputHandler.mm index 67e1f61db543..6fe0706f712a 100644 --- a/widget/cocoa/TextInputHandler.mm +++ b/widget/cocoa/TextInputHandler.mm @@ -3217,33 +3217,6 @@ IMEInputHandler::SetMarkedText(NSAttributedString* aAttrString, NS_OBJC_END_TRY_ABORT_BLOCK; } -NSInteger -IMEInputHandler::ConversationIdentifier() -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::ConversationIdentifier, Destroyed()=%s", - this, TrueOrFalse(Destroyed()))); - - if (Destroyed()) { - return reinterpret_cast(mView); - } - - RefPtr kungFuDeathGrip(this); - - // NOTE: The size of NSInteger is same as pointer size. - WidgetQueryContentEvent textContent(true, eQueryTextContent, mWidget); - textContent.InitForQueryTextContent(0, 0); - DispatchEvent(textContent); - if (!textContent.mSucceeded) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::ConversationIdentifier, Failed", this)); - return reinterpret_cast(mView); - } - // XXX This might return same ID as a previously existing editor if the - // deleted editor was created at the same address. Is there a better way? - return reinterpret_cast(textContent.mReply.mContentsRoot); -} - NSAttributedString* IMEInputHandler::GetAttributedSubstringFromRange(NSRange& aRange, NSRange* aActualRange) From e11a28e748044af56b6eb9d177ca3f2ccbb2a1b0 Mon Sep 17 00:00:00 2001 From: Andrew Comminos Date: Fri, 27 May 2016 18:41:40 -0400 Subject: [PATCH 157/199] Bug 1276734 - Use nsDisplayBackgroundImage for table cells without collapsed borders. r=mattwoodrow MozReview-Commit-ID: JrGVd2z7FFk --HG-- extra : rebase_source : b52f4651934673a22573393c4d2326191b635011 --- layout/tables/nsTableCellFrame.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index e4ca407ed097..6c488ebebab4 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -508,10 +508,10 @@ nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, // The cell background was not painted by the nsTablePainter, // so we need to do it. We have special background processing here // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline - nsDisplayTableItem* item = - new (aBuilder) nsDisplayTableCellBackground(aBuilder, this); - aLists.BorderBackground()->AppendNewToTop(item); - item->UpdateForFrameBackground(this); + nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, + this, + GetRectRelativeToSelf(), + aLists.BorderBackground()); } else { // The nsTablePainter will paint our background. Make sure it // knows if we're background-attachment:fixed. From 8941af6511fe45e4a51581ae3334fe112b217d8d Mon Sep 17 00:00:00 2001 From: Andrew Comminos Date: Mon, 30 May 2016 16:03:50 -0400 Subject: [PATCH 158/199] Bug 1276734 - Avoid unnecessary invalidations for a nsDisplayTableBorderBackground if it uses a fixed background with a separate display item. r=mattwoodrow MozReview-Commit-ID: 8zJnLCZsbzC --HG-- extra : rebase_source : 12200ae0fb8d51fbd46f10cca8227fa81d7499d2 --- layout/tables/nsTableFrame.cpp | 12 +++++++----- layout/tables/nsTableFrame.h | 7 +++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index d9a3cfd587ca..1c61ef7229ee 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1117,7 +1117,7 @@ nsDisplayTableItem::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, static_cast(aGeometry); bool invalidateForAttachmentFixed = false; - if (mPartHasFixedBackground) { + if (mDrawsBackground && mPartHasFixedBackground) { nsPoint frameOffsetToViewport = mFrame->GetOffsetTo( mFrame->PresContext()->PresShell()->GetRootFrame()); invalidateForAttachmentFixed = @@ -1137,8 +1137,9 @@ nsDisplayTableItem::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, class nsDisplayTableBorderBackground : public nsDisplayTableItem { public: nsDisplayTableBorderBackground(nsDisplayListBuilder* aBuilder, - nsTableFrame* aFrame) : - nsDisplayTableItem(aBuilder, aFrame) { + nsTableFrame* aFrame, + bool aDrawsBackground) : + nsDisplayTableItem(aBuilder, aFrame, aDrawsBackground) { MOZ_COUNT_CTOR(nsDisplayTableBorderBackground); } #ifdef NS_BUILD_REFCNT_LOGGING @@ -1321,8 +1322,8 @@ nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayTableItem* item = nullptr; if (IsVisibleInSelection(aBuilder)) { + nsMargin deflate = GetDeflationForBackground(PresContext()); if (StyleVisibility()->IsVisible()) { - nsMargin deflate = GetDeflationForBackground(PresContext()); // If 'deflate' is (0,0,0,0) then we can paint the table background // in its own display item, so do that to take advantage of // opacity and visibility optimizations @@ -1339,7 +1340,8 @@ nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, if (aBuilder->IsForEventDelivery() || AnyTablePartHasBorderOrBackground(this, GetNextSibling()) || AnyTablePartHasBorderOrBackground(mColGroups.FirstChild(), nullptr)) { - item = new (aBuilder) nsDisplayTableBorderBackground(aBuilder, this); + item = new (aBuilder) nsDisplayTableBorderBackground(aBuilder, this, + deflate != nsMargin(0, 0, 0, 0)); aLists.BorderBackground()->AppendNewToTop(item); } } diff --git a/layout/tables/nsTableFrame.h b/layout/tables/nsTableFrame.h index 942daba7883f..45fee6edbbed 100644 --- a/layout/tables/nsTableFrame.h +++ b/layout/tables/nsTableFrame.h @@ -48,9 +48,11 @@ static inline bool FrameHasBorderOrBackground(nsIFrame* f) { class nsDisplayTableItem : public nsDisplayItem { public: - nsDisplayTableItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) : + nsDisplayTableItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, + bool aDrawsBackground = true) : nsDisplayItem(aBuilder, aFrame), - mPartHasFixedBackground(false) {} + mPartHasFixedBackground(false), + mDrawsBackground(aDrawsBackground) {} // With collapsed borders, parts of the collapsed border can extend outside // the table part frames, so allow this display element to blow out to our @@ -67,6 +69,7 @@ public: private: bool mPartHasFixedBackground; + bool mDrawsBackground; }; class nsAutoPushCurrentTableItem From ef5e94c15625ffc8255111a6aced2519aeb8aa64 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Wed, 1 Jun 2016 11:35:53 +1000 Subject: [PATCH 159/199] Bug 1274520 part 1 - Add mozSystemGroup to EventListenerOptions for chrome and XBL to add listener in the system group. r=smaug MozReview-Commit-ID: 6DdLMEazWIC --HG-- extra : source : 3469098f26ccae51855666073cfbc3ff82b3685b --- dom/events/EventListenerManager.cpp | 27 ++++++++++++++++++++++----- dom/webidl/EventTarget.webidl | 3 +++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index 5263bba67317..0cfe3ee7b5a3 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -1338,6 +1338,19 @@ EventListenerManager::Disconnect() RemoveAllListeners(); } +static EventListenerFlags +GetEventListenerFlagsFromOptions(const EventListenerOptions& aOptions) +{ + EventListenerFlags flags; + flags.mCapture = aOptions.mCapture; + if (aOptions.mMozSystemGroup) { + JSContext* cx = nsContentUtils::GetCurrentJSContext(); + MOZ_ASSERT(cx, "Not being called from JS?"); + flags.mInSystemGroup = IsChromeOrXBL(cx, nullptr); + } + return flags; +} + void EventListenerManager::AddEventListener( const nsAString& aType, @@ -1362,8 +1375,9 @@ EventListenerManager::AddEventListener( if (aOptions.IsBoolean()) { flags.mCapture = aOptions.GetAsBoolean(); } else { - flags.mCapture = aOptions.GetAsAddEventListenerOptions().mCapture; - flags.mPassive = aOptions.GetAsAddEventListenerOptions().mPassive; + const auto& options = aOptions.GetAsAddEventListenerOptions(); + flags = GetEventListenerFlagsFromOptions(options); + flags.mPassive = options.mPassive; } flags.mAllowUntrustedEvents = aWantsUntrusted; return AddEventListenerByType(aListenerHolder, aType, flags); @@ -1387,9 +1401,12 @@ EventListenerManager::RemoveEventListener( const dom::EventListenerOptionsOrBoolean& aOptions) { EventListenerFlags flags; - flags.mCapture = - aOptions.IsBoolean() ? aOptions.GetAsBoolean() - : aOptions.GetAsEventListenerOptions().mCapture; + if (aOptions.IsBoolean()) { + flags.mCapture = aOptions.GetAsBoolean(); + } else { + const auto& options = aOptions.GetAsEventListenerOptions(); + flags = GetEventListenerFlagsFromOptions(options); + } RemoveEventListenerByType(aListenerHolder, aType, flags); } diff --git a/dom/webidl/EventTarget.webidl b/dom/webidl/EventTarget.webidl index abecbf906b97..e2251f29c696 100644 --- a/dom/webidl/EventTarget.webidl +++ b/dom/webidl/EventTarget.webidl @@ -13,6 +13,9 @@ dictionary EventListenerOptions { boolean capture = false; + /* This is a Mozilla extension only available in Chrome and XBL. + Setting to true make the listener be added to the system group. */ + boolean mozSystemGroup = false; }; dictionary AddEventListenerOptions : EventListenerOptions { From 8a433ea20059727623eb89b2cc967674ce49e52c Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Wed, 1 Jun 2016 11:35:53 +1000 Subject: [PATCH 160/199] Bug 1274520 part 2 - Listen control events in video controls on system group. r=gijs,jaws Listeners in the system group are invoked after listeners in the default group, so we can check defaultPrevented synchronously, and we have to change the play state according to the state of the play button. Due to the same reason above, we need to check the UI async in tests. MozReview-Commit-ID: Bs4MZvGefJj --HG-- extra : source : b28d589bb2675116fc7927381061fb0a03e39369 --- .../tests/widgets/test_videocontrols.html | 14 +++--- toolkit/content/widgets/videocontrols.xml | 50 +++++++++---------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/toolkit/content/tests/widgets/test_videocontrols.html b/toolkit/content/tests/widgets/test_videocontrols.html index 94996b0e5a5a..d444cf5ebf84 100644 --- a/toolkit/content/tests/widgets/test_videocontrols.html +++ b/toolkit/content/tests/widgets/test_videocontrols.html @@ -308,17 +308,19 @@ function runTest(event) { case 24: is(event.type, "mozfullscreenchange", "checking event type"); is(video.volume, 0.6, "Volume should still be 0.6"); - isVolumeSliderShowingCorrectVolume(video.volume); - - synthesizeKey("VK_ESCAPE", {}); + SimpleTest.executeSoon(function() { + isVolumeSliderShowingCorrectVolume(video.volume); + synthesizeKey("VK_ESCAPE", {}); + }); break; case 25: is(event.type, "mozfullscreenchange", "checking event type"); is(video.volume, 0.6, "Volume should still be 0.6"); - isVolumeSliderShowingCorrectVolume(video.volume); - - SimpleTest.finish(); + SimpleTest.executeSoon(function() { + isVolumeSliderShowingCorrectVolume(video.volume); + SimpleTest.finish(); + }); break; default: diff --git a/toolkit/content/widgets/videocontrols.xml b/toolkit/content/widgets/videocontrols.xml index dcf055b04077..b7a88d859437 100644 --- a/toolkit/content/widgets/videocontrols.xml +++ b/toolkit/content/widgets/videocontrols.xml @@ -634,8 +634,10 @@ for (let event of this.videoEvents) this.video.removeEventListener(event, this, false); - for (let element of this.controlListeners) - element.item.removeEventListener(element.event, element.func, false); + for (let element of this.controlListeners) { + element.item.removeEventListener(element.event, element.func, + { mozSystemGroup: true }); + } delete this.controlListeners; @@ -952,11 +954,15 @@ _triggeredByControls: false, + startPlay : function () { + this._triggeredByControls = true; + this.hideClickToPlay(); + this.video.play(); + }, + togglePause : function () { if (this.video.paused || this.video.ended) { - this._triggeredByControls = true; - this.hideClickToPlay(); - this.video.play(); + this.startPlay(); } else { this.video.pause(); } @@ -1033,21 +1039,13 @@ this.suppressError = true; return; } - - // Read defaultPrevented and the playback state asynchronously, - // since Web content may want to consume the "click" event - // but will only receive it after us. If web content - // doesn't use preventDefault but still toggles playback, - // we will treat that act the same as a call to preventDefault() - // so the web content-initiated toggle is not reverted. - let previousState = this.video.paused; - setTimeout(() => { - if (e.defaultPrevented || - this.video.paused != previousState) { - return; - } - this.togglePause(); - }, 0); + if (e.defaultPrevented) + return; + if (this.playButton.hasAttribute("paused")) { + this.startPlay(); + } else { + this.video.pause(); + } }, hideClickToPlay : function () { let videoHeight = this.video.clientHeight; @@ -1343,7 +1341,7 @@ function addListener(elem, eventName, func) { let boundFunc = func.bind(self); self.controlListeners.push({ item: elem, event: eventName, func: boundFunc }); - elem.addEventListener(eventName, boundFunc, false); + elem.addEventListener(eventName, boundFunc, { mozSystemGroup: true }); } addListener(this.muteButton, "command", this.toggleMute); @@ -1355,7 +1353,7 @@ addListener(this.videocontrols, "resizevideocontrols", this.adjustControlSize); addListener(this.videocontrols, "transitionend", this.onTransitionEnd); - addListener(this.video.ownerDocument, "mozfullscreenchange", this.onFullscreenChange); + addListener(this.video.ownerDocument, "fullscreenchange", this.onFullscreenChange); addListener(this.video, "keypress", this.keyHandler); addListener(this.videocontrols, "dragstart", function(event) { @@ -1671,8 +1669,10 @@ for (let event of this.videoEvents) this.video.removeEventListener(event, this, false); - for (let element of this.controlListeners) - element.item.removeEventListener(element.event, element.func, false); + for (let element of this.controlListeners) { + element.item.removeEventListener(element.event, element.func, + { mozSystemGroup: true }); + } delete this.controlListeners; }, @@ -1736,7 +1736,7 @@ function addListener(elem, eventName, func) { let boundFunc = func.bind(self); self.controlListeners.push({ item: elem, event: eventName, func: boundFunc }); - elem.addEventListener(eventName, boundFunc, false); + elem.addEventListener(eventName, boundFunc, { mozSystemGroup: true }); } addListener(this.clickToPlay, "click", this.clickToPlayClickHandler); addListener(this.video, "MozNoControlsBlockedVideo", this.blockedVideoHandler); From 7d48b7906b4b36192092753b27a5ff9028bffa88 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Wed, 1 Jun 2016 11:35:53 +1000 Subject: [PATCH 161/199] Bug 1274520 part 3 - Listen video events in system group. r=gijs,jaws Since the UI is now changed in listeners inside the system group, they are invoked after the listeners added by the content. Because of that, UI check needs to be asynchronously, otherwise we would be checking the state before videocontrols update. For the same reason, mouse/keyboard operations need to be invoked asynchronously as well, otherwise they would be applied based on the UI before update. This shouldn't change what is tested here, as user inputs are asynchronous by nature. MozReview-Commit-ID: 4h9Oa9qMVc5 --HG-- extra : source : ca67bc5ad3a3ddc6cf41dd4c4d6af647d94bc298 --- .../tests/widgets/test_videocontrols.html | 80 ++++++++++++------- .../widgets/test_videocontrols_audio.html | 8 +- toolkit/content/widgets/videocontrols.xml | 25 ++++-- 3 files changed, 75 insertions(+), 38 deletions(-) diff --git a/toolkit/content/tests/widgets/test_videocontrols.html b/toolkit/content/tests/widgets/test_videocontrols.html index d444cf5ebf84..d5d221e9075e 100644 --- a/toolkit/content/tests/widgets/test_videocontrols.html +++ b/toolkit/content/tests/widgets/test_videocontrols.html @@ -88,7 +88,9 @@ function runTest(event) { is(video.muted, false, "checking video mute state"); // Click the play button - synthesizeMouse(video, playButtonCenterX, playButtonCenterY, { }); + SimpleTest.executeSoon(() => { + synthesizeMouse(video, playButtonCenterX, playButtonCenterY, { }); + }); break; case 2: @@ -97,7 +99,9 @@ function runTest(event) { is(video.muted, false, "checking video mute state"); // Click the pause button - synthesizeMouse(video, playButtonCenterX, playButtonCenterY, { }); + SimpleTest.executeSoon(() => { + synthesizeMouse(video, playButtonCenterX, playButtonCenterY, { }); + }); break; case 3: @@ -105,7 +109,9 @@ function runTest(event) { is(video.paused, true, "checking video play state"); is(video.muted, false, "checking video mute state"); - synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); // Mute. + SimpleTest.executeSoon(() => { + synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); // Mute. + }); break; case 4: @@ -113,7 +119,9 @@ function runTest(event) { is(video.paused, true, "checking video play state"); is(video.muted, true, "checking video mute state"); - synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); // Unmute. + SimpleTest.executeSoon(() => { + synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); // Unmute. + }); break; /* @@ -131,7 +139,9 @@ function runTest(event) { // totals: top: 40px, right: 60px, bottom: 80px, left: 100px // Click the play button - synthesizeMouse(video, 100 + playButtonCenterX, 40 + playButtonCenterY, { }); + SimpleTest.executeSoon(() => { + synthesizeMouse(video, 100 + playButtonCenterX, 40 + playButtonCenterY, { }); + }); break; case 6: @@ -147,7 +157,9 @@ function runTest(event) { is(video.muted, false, "checking video mute state"); // Click the mute button - synthesizeMouse(video, 100 + muteButtonCenterX, 40 + muteButtonCenterY, { }); + SimpleTest.executeSoon(() => { + synthesizeMouse(video, 100 + muteButtonCenterX, 40 + muteButtonCenterY, { }); + }); break; case 8: @@ -187,11 +199,13 @@ function runTest(event) { // Bug 477434 -- sometimes we get 0.098999 here instead of 0! // is(video.currentTime, 0.0, "checking playback position"); - var beginDragX = scrubberOffsetX; - var endDragX = scrubberOffsetX + (scrubberWidth / 2); - synthesizeMouse(video, beginDragX, scrubberCenterY, { type: "mousedown", button: 0 }); - synthesizeMouse(video, endDragX, scrubberCenterY, { type: "mousemove", button: 0 }); - synthesizeMouse(video, endDragX, scrubberCenterY, { type: "mouseup", button: 0 }); + SimpleTest.executeSoon(() => { + var beginDragX = scrubberOffsetX; + var endDragX = scrubberOffsetX + (scrubberWidth / 2); + synthesizeMouse(video, beginDragX, scrubberCenterY, { type: "mousedown", button: 0 }); + synthesizeMouse(video, endDragX, scrubberCenterY, { type: "mousemove", button: 0 }); + synthesizeMouse(video, endDragX, scrubberCenterY, { type: "mouseup", button: 0 }); + }); break; case 12: @@ -208,7 +222,9 @@ function runTest(event) { var expectedTime = videoDuration / 2; ok(Math.abs(video.currentTime - expectedTime) < 0.1, "checking expected playback position"); - synthesizeMouse(video, scrubberOffsetX + (scrubberWidth / 4), scrubberCenterY, { }); + SimpleTest.executeSoon(() => { + synthesizeMouse(video, scrubberOffsetX + (scrubberWidth / 4), scrubberCenterY, { }); + }); break; case 14: @@ -239,34 +255,40 @@ function runTest(event) { ok(!video.muted, "Video is not muted."); video.focus(); - synthesizeKey("VK_DOWN", {}); + SimpleTest.executeSoon(() => synthesizeKey("VK_DOWN", {})); break; case 17: is(event.type, "volumechange", "checking event type"); is(video.volume, 0, "Volume should be 0."); ok(!video.muted, "Video is not muted."); - ok(isMuteButtonMuted(), "Mute button says it's muted"); - synthesizeKey("VK_UP", {}); + SimpleTest.executeSoon(() => { + ok(isMuteButtonMuted(), "Mute button says it's muted"); + synthesizeKey("VK_UP", {}); + }); break; case 18: is(event.type, "volumechange", "checking event type"); is(video.volume, 0.1, "Volume is increased."); ok(!video.muted, "Video is not muted."); - ok(!isMuteButtonMuted(), "Mute button says it's not muted"); - synthesizeKey("VK_DOWN", {}); + SimpleTest.executeSoon(() => { + ok(!isMuteButtonMuted(), "Mute button says it's not muted"); + synthesizeKey("VK_DOWN", {}); + }); break; case 19: is(event.type, "volumechange", "checking event type"); is(video.volume, 0, "Volume should be 0."); ok(!video.muted, "Video is not muted."); - ok(isMuteButtonMuted(), "Mute button says it's muted"); - synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); + SimpleTest.executeSoon(() => { + ok(isMuteButtonMuted(), "Mute button says it's muted"); + synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); + }); break; case 20: @@ -274,7 +296,7 @@ function runTest(event) { is(video.volume, 0.5, "Volume should be 0.5."); ok(!video.muted, "Video is not muted."); - synthesizeKey("VK_UP", {}); + SimpleTest.executeSoon(() => synthesizeKey("VK_UP", {})); break; case 21: @@ -282,7 +304,9 @@ function runTest(event) { is(video.volume, 0.6, "Volume should be 0.6."); ok(!video.muted, "Video is not muted."); - synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); + SimpleTest.executeSoon(() => { + synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); + }); break; case 22: @@ -290,9 +314,10 @@ function runTest(event) { is(video.volume, 0.6, "Volume should be 0.6."); ok(video.muted, "Video is muted."); - ok(isMuteButtonMuted(), "Mute button says it's muted"); - - synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); + SimpleTest.executeSoon(() => { + ok(isMuteButtonMuted(), "Mute button says it's muted"); + synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); + }); break; case 23: @@ -300,9 +325,10 @@ function runTest(event) { is(video.volume, 0.6, "Volume should be 0.6."); ok(!video.muted, "Video is not muted."); - ok(!isMuteButtonMuted(), "Mute button says it's not muted"); - - synthesizeMouse(video, fullscreenButtonCenterX, fullscreenButtonCenterY, { }); + SimpleTest.executeSoon(() => { + ok(!isMuteButtonMuted(), "Mute button says it's not muted"); + synthesizeMouse(video, fullscreenButtonCenterX, fullscreenButtonCenterY, { }); + }); break; case 24: diff --git a/toolkit/content/tests/widgets/test_videocontrols_audio.html b/toolkit/content/tests/widgets/test_videocontrols_audio.html index 3e9b6c0a875c..7d1dc32e3911 100644 --- a/toolkit/content/tests/widgets/test_videocontrols_audio.html +++ b/toolkit/content/tests/widgets/test_videocontrols_audio.html @@ -37,9 +37,11 @@ } function loadedmetadata(event) { - var controlBar = findElementByAttribute(video, "class", "controlBar"); - is(controlBar.getAttribute("fullscreen-unavailable"), "true", "Fullscreen button is hidden"); - SimpleTest.finish(); + SimpleTest.executeSoon(function() { + var controlBar = findElementByAttribute(video, "class", "controlBar"); + is(controlBar.getAttribute("fullscreen-unavailable"), "true", "Fullscreen button is hidden"); + SimpleTest.finish(); + }); } var video = document.getElementById("video"); diff --git a/toolkit/content/widgets/videocontrols.xml b/toolkit/content/widgets/videocontrols.xml index b7a88d859437..cdc15396e5c6 100644 --- a/toolkit/content/widgets/videocontrols.xml +++ b/toolkit/content/widgets/videocontrols.xml @@ -631,8 +631,12 @@ }, terminateEventListeners : function () { - for (let event of this.videoEvents) - this.video.removeEventListener(event, this, false); + for (let event of this.videoEvents) { + this.video.removeEventListener(event, this, { + capture: true, + mozSystemGroup: true + }); + } for (let element of this.controlListeners) { element.item.removeEventListener(element.event, element.func, @@ -1328,10 +1332,15 @@ this.setupNewLoadState(); // Use the handleEvent() callback for all media events. - // The "error" event listener must capture, so that it can trap error events - // from the children, which don't bubble. - for (let event of this.videoEvents) - this.video.addEventListener(event, this, (event == "error") ? true : false); + // Only the "error" event listener must capture, so that it can trap error + // events from children, which don't bubble. But we use capture + // for all events in order to simplify the event listener add/remove. + for (let event of this.videoEvents) { + this.video.addEventListener(event, this, { + capture: true, + mozSystemGroup: true + }); + } var self = this; @@ -1667,7 +1676,7 @@ controlListeners: [], terminateEventListeners : function () { for (let event of this.videoEvents) - this.video.removeEventListener(event, this, false); + this.video.removeEventListener(event, this, { mozSystemGroup: true }); for (let element of this.controlListeners) { element.item.removeEventListener(element.event, element.func, @@ -1742,7 +1751,7 @@ addListener(this.video, "MozNoControlsBlockedVideo", this.blockedVideoHandler); for (let event of this.videoEvents) { - this.video.addEventListener(event, this, false); + this.video.addEventListener(event, this, { mozSystemGroup: true }); } if (this.video.autoplay && !this.video.mozAutoplayEnabled) { From 80432e7d0ae3afc7b292d200873bd44ca784596d Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Tue, 31 May 2016 14:46:31 -0400 Subject: [PATCH 162/199] Bug 1276562 - Hang when inserting a bulleted or numbered list into a contentEditable, r=yzen --- accessible/html/HTMLListAccessible.cpp | 11 ++++++++ accessible/html/HTMLListAccessible.h | 2 ++ accessible/html/HTMLTableAccessible.h | 2 +- .../tests/mochitest/treeupdate/test_list.html | 27 ++++++++++++++++++- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/accessible/html/HTMLListAccessible.cpp b/accessible/html/HTMLListAccessible.cpp index 8e5a88fc63a0..d5de257181b3 100644 --- a/accessible/html/HTMLListAccessible.cpp +++ b/accessible/html/HTMLListAccessible.cpp @@ -92,6 +92,17 @@ HTMLLIAccessible::Bounds() const return rect; } +bool +HTMLLIAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild) +{ + // Adjust index if there's a bullet. + if (mBullet && aIndex == 0 && aChild != mBullet) { + return HyperTextAccessible::InsertChildAt(aIndex + 1, aChild); + } + + return HyperTextAccessible::InsertChildAt(aIndex, aChild); +} + //////////////////////////////////////////////////////////////////////////////// // HTMLLIAccessible: public diff --git a/accessible/html/HTMLListAccessible.h b/accessible/html/HTMLListAccessible.h index 26de44d1cf2c..ad4a2d425ed4 100644 --- a/accessible/html/HTMLListAccessible.h +++ b/accessible/html/HTMLListAccessible.h @@ -53,6 +53,8 @@ public: virtual a11y::role NativeRole() override; virtual uint64_t NativeState() override; + virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild) override; + // HTMLLIAccessible HTMLListBulletAccessible* Bullet() const { return mBullet; } void UpdateBullet(bool aHasBullet); diff --git a/accessible/html/HTMLTableAccessible.h b/accessible/html/HTMLTableAccessible.h index 4bab484962d7..830d34ae9dba 100644 --- a/accessible/html/HTMLTableAccessible.h +++ b/accessible/html/HTMLTableAccessible.h @@ -157,7 +157,7 @@ public: virtual already_AddRefed NativeAttributes() override; virtual Relation RelationByType(RelationType aRelationType) override; - bool InsertChildAt(uint32_t aIndex, Accessible* aChild) override; + virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild) override; protected: virtual ~HTMLTableAccessible() {} diff --git a/accessible/tests/mochitest/treeupdate/test_list.html b/accessible/tests/mochitest/treeupdate/test_list.html index 7d6af8b018ec..9196142d9be6 100644 --- a/accessible/tests/mochitest/treeupdate/test_list.html +++ b/accessible/tests/mochitest/treeupdate/test_list.html @@ -82,6 +82,28 @@ this.onProcessed = function showProcessor_onProcessed() { testLiAccessibleTree(); + gSequence.processNext(); + } + }; + + function textReplaceProcessor() + { + this.liNode = getNode("li"); + + this.process = function textReplaceProcessor_process() + { + this.liNode.textContent = "hey"; + } + + this.onProcessed = function textReplaceProcessor_onProcessed() + { + var tree = { + LISTITEM: [ + { STATICTEXT: [] }, + { TEXT_LEAF: [] } + ] + }; + testAccessibleTree(this.liNode, tree); SimpleTest.finish(); } }; @@ -89,8 +111,9 @@ //////////////////////////////////////////////////////////////////////////// // Test - var gSequence = null; + //gA11yEventDumpToConsole = true; + var gSequence = null; function doTest() { testLiAccessibleTree(); @@ -101,6 +124,8 @@ "hide HTML li"); gSequence.append(new showProcessor(), EVENT_SHOW, getNode("li"), "show HTML li"); + gSequence.append(new textReplaceProcessor(), EVENT_REORDER, getNode("li"), + "change text of HTML li"); gSequence.processNext(); // SimpleTest.finish() will be called in the end } From ec7b5a5b2bd9e05649d40df9eafe13b21c189a86 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 1 Jun 2016 11:45:16 +1000 Subject: [PATCH 163/199] No bug. Fix typo in name of mDiscveryTimeoutMs. r=me. --- dom/presentation/provider/MulticastDNSDeviceProvider.cpp | 8 ++++---- dom/presentation/provider/MulticastDNSDeviceProvider.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp index d8d5553ebf52..e1c9f7d04b98 100644 --- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp +++ b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp @@ -145,7 +145,7 @@ MulticastDNSDeviceProvider::Init() Preferences::AddStrongObservers(this, kObservedPrefs); mDiscoveryEnabled = Preferences::GetBool(PREF_PRESENTATION_DISCOVERY); - mDiscveryTimeoutMs = Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS); + mDiscoveryTimeoutMs = Preferences::GetUint(PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS); mDiscoverable = Preferences::GetBool(PREF_PRESENTATION_DISCOVERABLE); mServiceName = Preferences::GetCString(PREF_PRESENTATION_DEVICE_NAME); @@ -534,7 +534,7 @@ MulticastDNSDeviceProvider::ForceDiscovery() Unused << mDiscoveryTimer->Cancel(); if (NS_WARN_IF(NS_FAILED( rv = mDiscoveryTimer->Init(this, - mDiscveryTimeoutMs, + mDiscoveryTimeoutMs, nsITimer::TYPE_ONE_SHOT)))) { return rv; } @@ -565,7 +565,7 @@ MulticastDNSDeviceProvider::OnDiscoveryStarted(const nsACString& aServiceType) nsresult rv; if (NS_WARN_IF(NS_FAILED(rv = mDiscoveryTimer->Init(this, - mDiscveryTimeoutMs, + mDiscoveryTimeoutMs, nsITimer::TYPE_ONE_SHOT)))) { return rv; } @@ -924,7 +924,7 @@ MulticastDNSDeviceProvider::OnDiscoveryTimeoutChanged(uint32_t aTimeoutMs) LOG_I("OnDiscoveryTimeoutChanged = %d\n", aTimeoutMs); MOZ_ASSERT(NS_IsMainThread()); - mDiscveryTimeoutMs = aTimeoutMs; + mDiscoveryTimeoutMs = aTimeoutMs; return NS_OK; } diff --git a/dom/presentation/provider/MulticastDNSDeviceProvider.h b/dom/presentation/provider/MulticastDNSDeviceProvider.h index 2425255ca2f2..eb5865e0da58 100644 --- a/dom/presentation/provider/MulticastDNSDeviceProvider.h +++ b/dom/presentation/provider/MulticastDNSDeviceProvider.h @@ -186,7 +186,7 @@ private: bool mDiscoveryEnabled = false; bool mIsDiscovering = false; - uint32_t mDiscveryTimeoutMs; + uint32_t mDiscoveryTimeoutMs; nsCOMPtr mDiscoveryTimer; bool mDiscoverable = false; From 301b12190331f5d5bade71d15949cb8826acc155 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Wed, 1 Jun 2016 07:37:56 +0900 Subject: [PATCH 164/199] Bug 1277087 - Always add in-tree search paths when bootstrapping mach. r=gps --- build/mach_bootstrap.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index ce73bbc55aaf..fe769addb847 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -233,11 +233,8 @@ def bootstrap(topsrcdir, mozilla_dir=None): # case. For default behavior, we educate users and give them an opportunity # to react. We always exit after creating the directory because users don't # like surprises. - try: - import mach.main - except ImportError: - sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS] - import mach.main + sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS] + import mach.main def telemetry_handler(context, data): # We have not opted-in to telemetry From 42a30322ba7c7068b8f0c3657d4b8154ef6377ac Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 31 May 2016 22:00:17 -0400 Subject: [PATCH 165/199] Bug 1276400 part 1. Get rid of AutoDontReportUncaught and its one consumer. r=bkelly --- dom/base/nsJSUtils.h | 19 ------------------- dom/promise/Promise.cpp | 2 +- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h index 33955c901466..a88e0eb4bbac 100644 --- a/dom/base/nsJSUtils.h +++ b/dom/base/nsJSUtils.h @@ -134,25 +134,6 @@ private: void **aOffThreadToken); }; -class MOZ_STACK_CLASS AutoDontReportUncaught { - JSContext* mContext; - bool mWasSet; - -public: - explicit AutoDontReportUncaught(JSContext* aContext) : mContext(aContext) { - MOZ_ASSERT(aContext); - mWasSet = JS::ContextOptionsRef(mContext).dontReportUncaught(); - if (!mWasSet) { - JS::ContextOptionsRef(mContext).setDontReportUncaught(true); - } - } - ~AutoDontReportUncaught() { - if (!mWasSet) { - JS::ContextOptionsRef(mContext).setDontReportUncaught(false); - } - } -}; - template inline bool AssignJSString(JSContext *cx, T &dest, JSString *s) diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index a000fc2856b5..213330db9a94 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -2623,7 +2623,7 @@ Promise::ResolveInternal(JSContext* aCx, mResolvePending = true; if (aValue.isObject()) { - AutoDontReportUncaught silenceReporting(aCx); + MOZ_ASSERT(JS::ContextOptionsRef(aCx).autoJSAPIOwnsErrorReporting()); JS::Rooted valueObj(aCx, &aValue.toObject()); // Thenables. From 519200a8087e4ac4faf80d4526f4cbf0e5ab72e4 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 31 May 2016 22:01:30 -0400 Subject: [PATCH 166/199] Bug 1276400 part 2. Stop setting the dontReportUncaught option on the worker JSContext, since workers should be using AutoJSAPI/AutoEntryScript for everything. r=bkelly --- dom/workers/RuntimeService.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 183d1b3dbd84..98a0a33592dc 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -147,9 +147,6 @@ namespace { const uint32_t kNoIndex = uint32_t(-1); -const JS::ContextOptions kRequiredContextOptions = - JS::ContextOptions().setDontReportUncaught(true); - uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN; // Does not hold an owning reference. @@ -807,8 +804,6 @@ CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate, JSRuntime* aRuntime) js::SetCTypesActivityCallback(aRuntime, CTypesActivityCallback); - JS::ContextOptionsRef(workerCx) = kRequiredContextOptions; - #ifdef JS_GC_ZEAL JS_SetGCZeal(workerCx, settings.gcZeal, settings.gcZealFrequency); #endif From 39531c0bc83ce4900becb35b4f8e3e6cd6e6df6a Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 31 May 2016 22:04:33 -0400 Subject: [PATCH 167/199] Bug 1276400 part 3. Stop setting the dontReportUncaught option in IPC code, since we're working with an AutoJSAPI there. r=bkelly --- js/ipc/WrapperAnswer.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/ipc/WrapperAnswer.cpp b/js/ipc/WrapperAnswer.cpp index 223e73f905da..f525ca5b3001 100644 --- a/js/ipc/WrapperAnswer.cpp +++ b/js/ipc/WrapperAnswer.cpp @@ -410,9 +410,7 @@ WrapperAnswer::RecvCallOrConstruct(const ObjectId& objId, RootedValue rval(cx); { - AutoSaveContextOptions asco(cx); - ContextOptionsRef(cx).setDontReportUncaught(true); - + MOZ_ASSERT(JS::ContextOptionsRef(cx).autoJSAPIOwnsErrorReporting()); HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2); if (construct) { RootedObject obj(cx); From c22814a653f1708f5b16ecd2e2bac0b851b325e3 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 31 May 2016 22:04:34 -0400 Subject: [PATCH 168/199] Bug 1276400 part 4. Stop setting dontReportUncaught in XPConnect code, since all consumers got their JSContext from an AutoEntryScript or AutoJSAPI. r=bkelly --- js/xpconnect/loader/mozJSComponentLoader.cpp | 10 ---------- js/xpconnect/src/Sandbox.cpp | 2 -- js/xpconnect/src/XPCWrappedJSClass.cpp | 7 ++----- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index 997a57d0b067..2c7b7071871d 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -709,13 +709,6 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo, // The script wasn't in the cache , so compile it now. LOG(("Slow loading %s\n", nativePath.get())); - // If aPropagateExceptions is true, then our caller wants us to propagate - // any exceptions out to our caller. Ensure that the engine doesn't - // eagerly report the exception. - AutoSaveContextOptions asco(cx); - if (aPropagateExceptions) - ContextOptionsRef(cx).setDontReportUncaught(true); - // Note - if mReuseLoaderGlobal is true, then we can't do lazy source, // because we compile things as functions (rather than script), and lazy // source isn't supported in that configuration. That's ok though, @@ -938,9 +931,6 @@ mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo, dom::AutoEntryScript aes(CurrentGlobalOrNull(cx), "component loader load module"); JSContext* aescx = aes.cx(); - AutoSaveContextOptions asco(aescx); - if (aPropagateExceptions) - ContextOptionsRef(aescx).setDontReportUncaught(true); bool ok; if (script) { ok = JS_ExecuteScript(aescx, script); diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 3053d8c19b65..65348a06f587 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -1766,8 +1766,6 @@ xpc::EvalInSandbox(JSContext* cx, HandleObject sandboxArg, const nsAString& sour // This is clearly Gecko-specific and not in any spec. mozilla::dom::AutoEntryScript aes(priv, "XPConnect sandbox evaluation"); JSContext* sandcx = aes.cx(); - AutoSaveContextOptions savedOptions(sandcx); - JS::ContextOptionsRef(sandcx).setDontReportUncaught(true); JSAutoCompartment ac(sandcx, sandbox); JS::CompileOptions options(sandcx); diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index 4e599f5ddcd2..36007358d663 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -239,8 +239,7 @@ nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx, // to eat all exceptions either. { - AutoSaveContextOptions asco(cx); - ContextOptionsRef(cx).setDontReportUncaught(true); + MOZ_ASSERT(JS::ContextOptionsRef(cx).autoJSAPIOwnsErrorReporting()); RootedValue arg(cx, JS::ObjectValue(*id)); success = JS_CallFunctionValue(cx, jsobj, fun, HandleValueArray(arg), &retval); } @@ -1214,9 +1213,7 @@ pre_call_clean_up: success = JS_SetProperty(cx, obj, name, rval); } else { if (!fval.isPrimitive()) { - AutoSaveContextOptions asco(cx); - ContextOptionsRef(cx).setDontReportUncaught(true); - + MOZ_ASSERT(JS::ContextOptionsRef(cx).autoJSAPIOwnsErrorReporting()); success = JS_CallFunctionValue(cx, thisObj, fval, args, &rval); } else { // The property was not an object so can't be a function. From 94297c4fad434d4cf42e786dcf369304fac37876 Mon Sep 17 00:00:00 2001 From: Liang-Heng Chen Date: Thu, 26 May 2016 14:34:51 +0800 Subject: [PATCH 169/199] Bug 1258977 - Part 1: separate match function; r=bagder MozReview-Commit-ID: J4DVAzI0bZm --HG-- extra : rebase_source : 0a989b57d440c968f17a339282ca577e6af9fa60 --- toolkit/system/windowsproxy/ProxyUtils.cpp | 52 +++++++++++++++++++ toolkit/system/windowsproxy/ProxyUtils.h | 21 ++++++++ toolkit/system/windowsproxy/moz.build | 1 + .../nsWindowsSystemProxySettings.cpp | 33 +----------- 4 files changed, 76 insertions(+), 31 deletions(-) create mode 100644 toolkit/system/windowsproxy/ProxyUtils.cpp create mode 100644 toolkit/system/windowsproxy/ProxyUtils.h diff --git a/toolkit/system/windowsproxy/ProxyUtils.cpp b/toolkit/system/windowsproxy/ProxyUtils.cpp new file mode 100644 index 000000000000..e72bf72b95c5 --- /dev/null +++ b/toolkit/system/windowsproxy/ProxyUtils.cpp @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ProxyUtils.h" + +namespace mozilla { +namespace toolkit { +namespace system { + +bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) +{ + nsAutoCString host(aHost); + nsAutoCString override(aOverride); + int32_t overrideLength = override.Length(); + int32_t tokenStart = 0; + int32_t offset = 0; + bool star = false; + + while (tokenStart < overrideLength) { + int32_t tokenEnd = override.FindChar('*', tokenStart); + if (tokenEnd == tokenStart) { + star = true; + tokenStart++; + // If the character following the '*' is a '.' character then skip + // it so that "*.foo.com" allows "foo.com". + if (override.FindChar('.', tokenStart) == tokenStart) { + tokenStart++; + } + } else { + if (tokenEnd == -1) { + tokenEnd = overrideLength; + } + nsAutoCString token(Substring(override, tokenStart, + tokenEnd - tokenStart)); + offset = host.Find(token, offset); + if (offset == -1 || (!star && offset)) { + return false; + } + star = false; + tokenStart = tokenEnd; + offset += token.Length(); + } + } + + return (star || (offset == host.Length())); +} + +} // namespace system +} // namespace toolkit +} // namespace mozilla diff --git a/toolkit/system/windowsproxy/ProxyUtils.h b/toolkit/system/windowsproxy/ProxyUtils.h new file mode 100644 index 000000000000..7d6ae220ff0e --- /dev/null +++ b/toolkit/system/windowsproxy/ProxyUtils.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_toolkit_system_windowsproxy_ProxyUtils_h +#define mozilla_toolkit_system_windowsproxy_ProxyUtils_h + +#include "nsStringGlue.h" + +namespace mozilla { +namespace toolkit { +namespace system { + +bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride); + +} // namespace system +} // namespace toolkit +} // namespace mozilla + +#endif // mozilla_toolkit_system_windowsproxy_ProxyUtils_h diff --git a/toolkit/system/windowsproxy/moz.build b/toolkit/system/windowsproxy/moz.build index 17a816bb2825..321a361b0755 100644 --- a/toolkit/system/windowsproxy/moz.build +++ b/toolkit/system/windowsproxy/moz.build @@ -6,6 +6,7 @@ SOURCES += [ 'nsWindowsSystemProxySettings.cpp', + 'ProxyUtils.cpp' ] FINAL_LIBRARY = 'xul' diff --git a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp index 7b8a87d80df4..a496f8106dc4 100644 --- a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp +++ b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp @@ -17,6 +17,7 @@ #include "nsISupportsPrimitives.h" #include "nsIURI.h" #include "GeckoProfiler.h" +#include "ProxyUtils.h" class nsWindowsSystemProxySettings final : public nsISystemProxySettings { @@ -168,37 +169,7 @@ bool nsWindowsSystemProxySettings::PatternMatch(const nsACString& aHost, const nsACString& aOverride) { - nsAutoCString host(aHost); - nsAutoCString override(aOverride); - int32_t overrideLength = override.Length(); - int32_t tokenStart = 0; - int32_t offset = 0; - bool star = false; - - while (tokenStart < overrideLength) { - int32_t tokenEnd = override.FindChar('*', tokenStart); - if (tokenEnd == tokenStart) { - star = true; - tokenStart++; - // If the character following the '*' is a '.' character then skip - // it so that "*.foo.com" allows "foo.com". - if (override.FindChar('.', tokenStart) == tokenStart) - tokenStart++; - } else { - if (tokenEnd == -1) - tokenEnd = overrideLength; - nsAutoCString token(Substring(override, tokenStart, - tokenEnd - tokenStart)); - offset = host.Find(token, offset); - if (offset == -1 || (!star && offset)) - return false; - star = false; - tokenStart = tokenEnd; - offset += token.Length(); - } - } - - return (star || (offset == host.Length())); + return mozilla::toolkit::system::IsHostProxyEntry(aHost, aOverride); } nsresult From 9e816df3f6f3e77ab3d70a3950186f5cf919eb79 Mon Sep 17 00:00:00 2001 From: Liang-Heng Chen Date: Thu, 26 May 2016 14:48:27 +0800 Subject: [PATCH 170/199] Bug 1258977 - Part 2: add proxy exception tests; r=bagder MozReview-Commit-ID: HS8Y41TtxvF --HG-- extra : rebase_source : 5db1ed69a56786e329f2ce9a928f793a51171b3d --- .../tests/gtest/TestProxyBypassRules.cpp | 1 + toolkit/system/windowsproxy/moz.build | 2 + .../tests/gtest/TestProxyBypassRules.cpp | 42 +++++++++++++++++++ .../system/windowsproxy/tests/gtest/moz.build | 18 ++++++++ 4 files changed, 63 insertions(+) create mode 100644 toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp create mode 100644 toolkit/system/windowsproxy/tests/gtest/moz.build diff --git a/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp b/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp index 2a55d9bfce1b..6d8000d71d51 100644 --- a/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp +++ b/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp @@ -14,6 +14,7 @@ TEST(OSXProxy, TestProxyBypassRules) EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("mozilla.org"))); EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"),NS_LITERAL_CSTRING("*mozilla.org"))); EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("notmozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*mozilla.org"))); EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.com"), NS_LITERAL_CSTRING("*.mozilla.*"))); diff --git a/toolkit/system/windowsproxy/moz.build b/toolkit/system/windowsproxy/moz.build index 321a361b0755..02e8f84377ec 100644 --- a/toolkit/system/windowsproxy/moz.build +++ b/toolkit/system/windowsproxy/moz.build @@ -4,6 +4,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +TEST_DIRS += ['tests/gtest'] + SOURCES += [ 'nsWindowsSystemProxySettings.cpp', 'ProxyUtils.cpp' diff --git a/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp b/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp new file mode 100644 index 000000000000..9cd6bf981b51 --- /dev/null +++ b/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "ProxyUtils.h" + +using namespace mozilla::toolkit::system; + +TEST(WindowsProxy, TestProxyBypassRules) +{ + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"),NS_LITERAL_CSTRING("*mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("notmozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.com"), NS_LITERAL_CSTRING("*.mozilla.*"))); +} + +TEST(WindowsProxy, TestProxyBypassRulesIPv4) +{ + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.*"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.2.*"))); + + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("10.1.2.3"), NS_LITERAL_CSTRING("10.0.0.0/8"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/16"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/17"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168.128/17"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.1/32"))); +} + +TEST(WindowsProxy, TestProxyBypassRulesIPv6) +{ + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/64"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:0000:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96"))); +} diff --git a/toolkit/system/windowsproxy/tests/gtest/moz.build b/toolkit/system/windowsproxy/tests/gtest/moz.build new file mode 100644 index 000000000000..7c77c317cf3a --- /dev/null +++ b/toolkit/system/windowsproxy/tests/gtest/moz.build @@ -0,0 +1,18 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + 'TestProxyBypassRules.cpp', +] + +LOCAL_INCLUDES += [ + '/toolkit/system/windowsproxy', +] + +FINAL_LIBRARY = 'xul-gtest' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] From 1e3f941f784ad6f3f10e2871a282f8d643817186 Mon Sep 17 00:00:00 2001 From: Liang-Heng Chen Date: Thu, 26 May 2016 17:48:51 +0800 Subject: [PATCH 171/199] Bug 1258977 - Part 3: handle special case to follow the test; r=bagder MozReview-Commit-ID: ImGSoBMwQvx --HG-- extra : rebase_source : 405e43f404cfabbce26709ab789b33de88df5e0f --- toolkit/system/osxproxy/ProxyUtils.mm | 19 ++- toolkit/system/windowsproxy/ProxyUtils.cpp | 142 ++++++++++++++++++++- 2 files changed, 150 insertions(+), 11 deletions(-) diff --git a/toolkit/system/osxproxy/ProxyUtils.mm b/toolkit/system/osxproxy/ProxyUtils.mm index 0d47a4c5c8c3..4e59f226a0bb 100644 --- a/toolkit/system/osxproxy/ProxyUtils.mm +++ b/toolkit/system/osxproxy/ProxyUtils.mm @@ -49,8 +49,9 @@ MaskIPv4Addr(PRUint32 aAddr, uint16_t aMaskLen) static void MaskIPv6Addr(PRIPv6Addr& aAddr, uint16_t aMaskLen) { - if (aMaskLen == 128) + if (aMaskLen == 128) { return; + } if (aMaskLen > 96) { aAddr.pr_s6_addr32[3] = PR_htonl( @@ -144,15 +145,23 @@ IsMatchWildcard(const nsACString& aHost, const nsACString& aOverride) tokenStart++; // If the character following the '*' is a '.' character then skip // it so that "*.foo.com" allows "foo.com". - if (override.FindChar('.', tokenStart) == tokenStart) - tokenStart++; + if (override.FindChar('.', tokenStart) == tokenStart) { + nsAutoCString token(Substring(override, + tokenStart + 1, + overrideLength - tokenStart - 1)); + if (host.Equals(token)) { + return true; + } + } } else { - if (tokenEnd == -1) + if (tokenEnd == -1) { tokenEnd = overrideLength; // no '*' char, match rest of string + } nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart)); offset = host.Find(token, offset); - if (offset == -1 || (!star && offset)) + if (offset == -1 || (!star && offset)) { return false; + } star = false; tokenStart = tokenEnd; offset += token.Length(); diff --git a/toolkit/system/windowsproxy/ProxyUtils.cpp b/toolkit/system/windowsproxy/ProxyUtils.cpp index e72bf72b95c5..4e59f226a0bb 100644 --- a/toolkit/system/windowsproxy/ProxyUtils.cpp +++ b/toolkit/system/windowsproxy/ProxyUtils.cpp @@ -4,15 +4,134 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ProxyUtils.h" +#include "nsTArray.h" +#include "prnetdb.h" +#include "prtypes.h" namespace mozilla { namespace toolkit { namespace system { -bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) +/** + * Normalize the short IP form into the complete form. + * For example, it converts "192.168" into "192.168.0.0" + */ +static bool +NormalizeAddr(const nsACString& aAddr, nsCString& aNormalized) +{ + nsTArray addr; + if (!ParseString(aAddr, '.', addr)) { + return false; + } + aNormalized = ""; + for (uint32_t i = 0; i < 4; ++i) { + if (i != 0) { + aNormalized.Append("."); + } + if (i < addr.Length()) { + aNormalized.Append(addr[i]); + } else { + aNormalized.Append("0"); + } + } + return true; +} + +static PRUint32 +MaskIPv4Addr(PRUint32 aAddr, uint16_t aMaskLen) +{ + if (aMaskLen == 32) { + return aAddr; + } + return PR_htonl(PR_ntohl(aAddr) & (~0L << (32 - aMaskLen))); +} + +static void +MaskIPv6Addr(PRIPv6Addr& aAddr, uint16_t aMaskLen) +{ + if (aMaskLen == 128) { + return; + } + + if (aMaskLen > 96) { + aAddr.pr_s6_addr32[3] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[3]) & (~0L << (128 - aMaskLen))); + } else if (aMaskLen > 64) { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[2]) & (~0L << (96 - aMaskLen))); + } else if (aMaskLen > 32) { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = 0; + aAddr.pr_s6_addr32[1] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[1]) & (~0L << (64 - aMaskLen))); + } else { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = 0; + aAddr.pr_s6_addr32[1] = 0; + aAddr.pr_s6_addr32[0] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[0]) & (~0L << (32 - aMaskLen))); + } + + return; +} + +static bool +IsMatchMask(const nsACString& aHost, const nsACString& aOverride) +{ + nsresult rv; + + auto tokenEnd = aOverride.FindChar('/'); + if (tokenEnd == -1) { + return false; + } + + nsAutoCString prefixStr(Substring(aOverride, + tokenEnd + 1, + aOverride.Length() - tokenEnd - 1)); + auto maskLen = prefixStr.ToInteger(&rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + nsAutoCString override(aOverride); + if (!NormalizeAddr(Substring(aOverride, 0, tokenEnd), override)) { + return false; + } + + PRNetAddr prAddrHost; + PRNetAddr prAddrOverride; + if (PR_SUCCESS != PR_StringToNetAddr(PromiseFlatCString(aHost).get(), + &prAddrHost) || + PR_SUCCESS != PR_StringToNetAddr(override.get(), + &prAddrOverride)) { + return false; + } + + if (prAddrHost.raw.family == PR_AF_INET && + prAddrOverride.raw.family == PR_AF_INET) { + return MaskIPv4Addr(prAddrHost.inet.ip, maskLen) == + MaskIPv4Addr(prAddrOverride.inet.ip, maskLen); + } + else if (prAddrHost.raw.family == PR_AF_INET6 && + prAddrOverride.raw.family == PR_AF_INET6) { + MaskIPv6Addr(prAddrHost.ipv6.ip, maskLen); + MaskIPv6Addr(prAddrOverride.ipv6.ip, maskLen); + + return memcmp(&prAddrHost.ipv6.ip, + &prAddrOverride.ipv6.ip, + sizeof(PRIPv6Addr)) == 0; + } + + return false; +} + +static bool +IsMatchWildcard(const nsACString& aHost, const nsACString& aOverride) { nsAutoCString host(aHost); nsAutoCString override(aOverride); + int32_t overrideLength = override.Length(); int32_t tokenStart = 0; int32_t offset = 0; @@ -21,19 +140,24 @@ bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) while (tokenStart < overrideLength) { int32_t tokenEnd = override.FindChar('*', tokenStart); if (tokenEnd == tokenStart) { + // Star is the first character in the token. star = true; tokenStart++; // If the character following the '*' is a '.' character then skip // it so that "*.foo.com" allows "foo.com". if (override.FindChar('.', tokenStart) == tokenStart) { - tokenStart++; + nsAutoCString token(Substring(override, + tokenStart + 1, + overrideLength - tokenStart - 1)); + if (host.Equals(token)) { + return true; + } } } else { if (tokenEnd == -1) { - tokenEnd = overrideLength; + tokenEnd = overrideLength; // no '*' char, match rest of string } - nsAutoCString token(Substring(override, tokenStart, - tokenEnd - tokenStart)); + nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart)); offset = host.Find(token, offset); if (offset == -1 || (!star && offset)) { return false; @@ -44,7 +168,13 @@ bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) } } - return (star || (offset == host.Length())); + return (star || (offset == static_cast(host.Length()))); +} + +bool +IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) +{ + return IsMatchMask(aHost, aOverride) || IsMatchWildcard(aHost, aOverride); } } // namespace system From fa47d77e116a080c3249ab3f30f3352d7945cacb Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Wed, 1 Jun 2016 10:21:13 +0800 Subject: [PATCH 172/199] Bug 1257738 - part1 : implement the audio competing mechanism. MozReview-Commit-ID: GZw7P0kbhOa --- b2g/app/b2g.js | 2 + dom/audiochannel/AudioChannelService.cpp | 197 ++++++++++++++++++++++- dom/audiochannel/AudioChannelService.h | 25 ++- dom/audiochannel/moz.build | 4 + mobile/android/app/mobile.js | 1 + modules/libpref/init/all.js | 2 + 6 files changed, 224 insertions(+), 7 deletions(-) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 4a39d77ca026..f1193cd547c8 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -1087,6 +1087,8 @@ pref("dom.performance.enable_notify_performance_timing", true); pref("b2g.multiscreen.chrome_remote_url", "chrome://b2g/content/shell_remote.html"); pref("b2g.multiscreen.system_remote_url", "index_remote.html"); +// Audio competing between tabs +pref("dom.audiochannel.audioCompeting", false); // Because we can't have nice things. #ifdef MOZ_GRAPHENE diff --git a/dom/audiochannel/AudioChannelService.cpp b/dom/audiochannel/AudioChannelService.cpp index 7a06281bff2d..2592485ecc8c 100644 --- a/dom/audiochannel/AudioChannelService.cpp +++ b/dom/audiochannel/AudioChannelService.cpp @@ -22,6 +22,7 @@ #include "nsThreadUtils.h" #include "nsHashPropertyBag.h" #include "nsComponentManagerUtils.h" +#include "nsGlobalWindow.h" #include "nsPIDOMWindow.h" #include "nsServiceManagerUtils.h" #include "mozilla/dom/SettingChangeNotificationBinding.h" @@ -41,6 +42,7 @@ namespace { // If true, any new AudioChannelAgent will be muted when created. bool sAudioChannelMutedByDefault = false; +bool sAudioChannelCompeting = false; bool sXPCOMShuttingDown = false; class NotifyChannelActiveRunnable final : public Runnable @@ -210,6 +212,13 @@ AudioChannelService::Shutdown() } } +/* static */ bool +AudioChannelService::IsEnableAudioCompeting() +{ + CreateServiceIfNeeded(); + return sAudioChannelCompeting; +} + NS_INTERFACE_MAP_BEGIN(AudioChannelService) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAudioChannelService) NS_INTERFACE_MAP_ENTRY(nsIAudioChannelService) @@ -241,6 +250,8 @@ AudioChannelService::AudioChannelService() Preferences::AddBoolVarCache(&sAudioChannelMutedByDefault, "dom.audiochannel.mutedByDefault"); + Preferences::AddBoolVarCache(&sAudioChannelCompeting, + "dom.audiochannel.audioCompeting"); } AudioChannelService::~AudioChannelService() @@ -336,14 +347,15 @@ AudioChannelService::GetMediaConfig(nsPIDOMWindowOuter* aWindow, if (winData) { config.mVolume *= winData->mChannels[aAudioChannel].mVolume; config.mMuted = config.mMuted || winData->mChannels[aAudioChannel].mMuted; + config.mSuspend = winData->mOwningAudioFocus ? + config.mSuspend : nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE; } config.mVolume *= window->GetAudioVolume(); config.mMuted = config.mMuted || window->GetAudioMuted(); - - // If the mSuspend is already suspended, we don't need to set it again. - config.mSuspend = (config.mSuspend == nsISuspendedTypes::NONE_SUSPENDED) ? - window->GetMediaSuspend() : config.mSuspend; + if (window->GetMediaSuspend() != nsISuspendedTypes::NONE_SUSPENDED) { + config.mSuspend = window->GetMediaSuspend(); + } nsCOMPtr win = window->GetScriptableParentOrNull(); if (!win) { @@ -994,6 +1006,176 @@ AudioChannelService::ChildStatusReceived(uint64_t aChildID, data->mActiveContentOrNormalChannel = aContentOrNormalChannel; } +void +AudioChannelService::RefreshAgentsAudioFocusChanged(AudioChannelAgent* aAgent, + bool aActive) +{ + MOZ_ASSERT(aAgent); + + nsTObserverArray>::ForwardIterator + iter(mWindows); + while (iter.HasMore()) { + AudioChannelWindow* winData = iter.GetNext(); + if (winData->mOwningAudioFocus) { + winData->AudioFocusChanged(aAgent, aActive); + } + } +} + +void +AudioChannelService::AudioChannelWindow::RequestAudioFocus(AudioChannelAgent* aAgent) +{ + MOZ_ASSERT(aAgent); + + // We already have the audio focus. No operation is needed. + if (mOwningAudioFocus) { + return; + } + + // Only foreground window can request audio focus, but it would still own the + // audio focus even it goes to background. Audio focus would be abandoned + // only when other foreground window starts audio competing. + mOwningAudioFocus = !(aAgent->Window()->IsBackground()); + + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("AudioChannelWindow, RequestAudioFocus, this = %p, " + "agent = %p, owning audio focus = %d\n", + this, aAgent, mOwningAudioFocus)); +} + +void +AudioChannelService::AudioChannelWindow::NotifyAudioCompetingChanged(AudioChannelAgent* aAgent, + bool aActive) +{ + // This function may be called after RemoveAgentAndReduceAgentsNum(), so the + // agent may be not contained in mAgent. In addition, the agent would still + // be alive because we have kungFuDeathGrip in UnregisterAudioChannelAgent(). + MOZ_ASSERT(aAgent); + + RefPtr service = AudioChannelService::GetOrCreate(); + MOZ_ASSERT(service); + + if (!service->IsEnableAudioCompeting()) { + return; + } + + if (!IsAgentInvolvingInAudioCompeting(aAgent)) { + return; + } + + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("AudioChannelWindow, NotifyAudioCompetingChanged, this = %p, " + "agent = %p, active = %d\n", + this, aAgent, aActive)); + + service->RefreshAgentsAudioFocusChanged(aAgent, aActive); +} + +bool +AudioChannelService::AudioChannelWindow::IsAgentInvolvingInAudioCompeting(AudioChannelAgent* aAgent) const +{ + MOZ_ASSERT(aAgent); + + if(!mOwningAudioFocus) { + return false; + } + + if (IsAudioCompetingInSameTab()) { + return false; + } + + // TODO : add MediaSession::ambient kind, because it doens't interact with + // other kinds. + return true; +} + +bool +AudioChannelService::AudioChannelWindow::IsAudioCompetingInSameTab() const +{ + return (mOwningAudioFocus && mAudibleAgents.Length() > 1); +} + +void +AudioChannelService::AudioChannelWindow::AudioFocusChanged(AudioChannelAgent* aNewPlayingAgent, + bool aActive) +{ + // This agent isn't always known for the current window, because it can comes + // from other window. + MOZ_ASSERT(aNewPlayingAgent); + + if (mAudibleAgents.IsEmpty()) { + // These would happen in two situations, + // (1) Audio in page A was ended, and another page B want to play audio. + // Page A should abandon its focus. + // (2) Audio was paused by remote-control, page should still own the focus. + mOwningAudioFocus = IsContainingPlayingAgent(aNewPlayingAgent); + } else { + nsTObserverArray::ForwardIterator iter(mAudibleAgents); + while (iter.HasMore()) { + AudioChannelAgent* agent = iter.GetNext(); + MOZ_ASSERT(agent); + + // Don't need to update the playing state of new playing agent. + if (agent == aNewPlayingAgent) { + continue; + } + + uint32_t type = GetCompetingBehavior(agent, + aNewPlayingAgent->AudioChannelType(), + aActive); + + // If window will be suspended, it needs to abandon the audio focus + // because only one window can own audio focus at a time. However, we + // would support multiple audio focus at the same time in the future. + mOwningAudioFocus = (type == nsISuspendedTypes::NONE_SUSPENDED); + + // TODO : support other behaviors which are definded in MediaSession API. + switch (type) { + case nsISuspendedTypes::NONE_SUSPENDED: + case nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE: + agent->WindowSuspendChanged(type); + break; + } + } + } + + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("AudioChannelWindow, AudioFocusChanged, this = %p, " + "OwningAudioFocus = %d\n", this, mOwningAudioFocus)); +} + +bool +AudioChannelService::AudioChannelWindow::IsContainingPlayingAgent(AudioChannelAgent* aAgent) const +{ + return (aAgent->WindowID() == mWindowID); +} + +uint32_t +AudioChannelService::AudioChannelWindow::GetCompetingBehavior(AudioChannelAgent* aAgent, + int32_t aIncomingChannelType, + bool aIncomingChannelActive) const +{ + MOZ_ASSERT(aAgent); + MOZ_ASSERT(mAudibleAgents.Contains(aAgent)); + + uint32_t competingBehavior = nsISuspendedTypes::NONE_SUSPENDED; + int32_t presentChannelType = aAgent->AudioChannelType(); + + // TODO : add other competing cases for MediaSession API + if (presentChannelType == int32_t(AudioChannel::Normal) && + aIncomingChannelType == int32_t(AudioChannel::Normal) && + aIncomingChannelActive) { + competingBehavior = nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE; + } + + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("AudioChannelWindow, GetCompetingBehavior, this = %p, " + "present type = %d, incoming channel = %d, behavior = %d\n", + this, presentChannelType, aIncomingChannelType, competingBehavior)); + + return competingBehavior; +} + /* static */ bool AudioChannelService::IsAudioChannelMutedByDefault() { @@ -1007,9 +1189,12 @@ AudioChannelService::AudioChannelWindow::AppendAgent(AudioChannelAgent* aAgent, { MOZ_ASSERT(aAgent); + RequestAudioFocus(aAgent); AppendAgentAndIncreaseAgentsNum(aAgent); AudioCapturedChanged(aAgent, AudioCaptureState::eCapturing); - AudioAudibleChanged(aAgent, aAudible); + if (aAudible) { + AudioAudibleChanged(aAgent, AudibleState::eAudible); + } } void @@ -1082,6 +1267,8 @@ AudioChannelService::AudioChannelWindow::AudioAudibleChanged(AudioChannelAgent* } else { RemoveAudibleAgentIfContained(aAgent); } + + NotifyAudioCompetingChanged(aAgent, aAudible); } void diff --git a/dom/audiochannel/AudioChannelService.h b/dom/audiochannel/AudioChannelService.h index 3b4fd19f0293..0cadb15fd72e 100644 --- a/dom/audiochannel/AudioChannelService.h +++ b/dom/audiochannel/AudioChannelService.h @@ -89,6 +89,8 @@ public: static PRLogModuleInfo* GetAudioChannelLog(); + static bool IsEnableAudioCompeting(); + /** * Any audio channel agent that starts playing should register itself to * this service, sharing the AudioChannel. @@ -220,6 +222,9 @@ private: void SetDefaultVolumeControlChannelInternal(int32_t aChannel, bool aVisible, uint64_t aChildID); + void RefreshAgentsAudioFocusChanged(AudioChannelAgent* aAgent, + bool aActive); + class AudioChannelConfig final : public AudioPlaybackConfig { public: @@ -236,13 +241,15 @@ private: { public: explicit AudioChannelWindow(uint64_t aWindowID) - : mWindowID(aWindowID), - mIsAudioCaptured(false) + : mWindowID(aWindowID) + , mIsAudioCaptured(false) + , mOwningAudioFocus(!AudioChannelService::IsEnableAudioCompeting()) { // Workaround for bug1183033, system channel type can always playback. mChannels[(int16_t)AudioChannel::System].mMuted = false; } + void AudioFocusChanged(AudioChannelAgent* aNewPlayingAgent, bool aActive); void AudioAudibleChanged(AudioChannelAgent* aAgent, AudibleState aAudible); void AppendAgent(AudioChannelAgent* aAgent, AudibleState aAudible); @@ -256,6 +263,10 @@ private: nsTObserverArray mAgents; nsTObserverArray mAudibleAgents; + // Owning audio focus when the window starts playing audible sound, and + // lose audio focus when other windows starts playing. + bool mOwningAudioFocus; + private: void AudioCapturedChanged(AudioChannelAgent* aAgent, AudioCaptureState aCapture); @@ -273,6 +284,16 @@ private: AudibleState aAudible); void NotifyChannelActive(uint64_t aWindowID, AudioChannel aChannel, bool aActive); + + void RequestAudioFocus(AudioChannelAgent* aAgent); + void NotifyAudioCompetingChanged(AudioChannelAgent* aAgent, bool aActive); + + uint32_t GetCompetingBehavior(AudioChannelAgent* aAgent, + int32_t aIncomingChannelType, + bool aIncomingChannelActive) const; + bool IsAgentInvolvingInAudioCompeting(AudioChannelAgent* aAgent) const; + bool IsAudioCompetingInSameTab() const; + bool IsContainingPlayingAgent(AudioChannelAgent* aAgent) const; }; AudioChannelWindow* diff --git a/dom/audiochannel/moz.build b/dom/audiochannel/moz.build index 0c6a489a2ddf..8a929d33ddad 100644 --- a/dom/audiochannel/moz.build +++ b/dom/audiochannel/moz.build @@ -21,6 +21,10 @@ UNIFIED_SOURCES += [ 'AudioChannelService.cpp', ] +LOCAL_INCLUDES += [ + '/dom/base/', +] + include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index eeebb9ebf2a8..20a63a50c5d8 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -923,6 +923,7 @@ pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sy pref("dom.presentation.enabled", true); pref("dom.presentation.discovery.enabled", true); +pref("dom.audiochannel.audioCompeting", true); // TODO : remove it after landing bug1242874 because now it's the only way to // suspend the MediaElement. pref("media.useAudioChannelAPI", true); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index e7c8d0d83d58..c33e635cf019 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -5404,6 +5404,8 @@ pref("dom.mozBrowserFramesEnabled", false); // Is support for 'color-adjust' CSS property enabled? pref("layout.css.color-adjust.enabled", true); +pref("dom.audiochannel.audioCompeting", false); + // Disable Node.rootNode in release builds. #ifdef RELEASE_BUILD pref("dom.node.rootNode.enabled", false); From b76ee4b13bb49328a1a6fa67535337bedf120b6e Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Wed, 1 Jun 2016 10:21:58 +0800 Subject: [PATCH 173/199] Bug 1257738 - part2 : modify logic of requesting audio focus in Android. MozReview-Commit-ID: AG095CIgnA5 --- .../mozilla/gecko/media/AudioFocusAgent.java | 23 ++++++-------- widget/android/AndroidBridge.cpp | 30 ++++++++++++++++--- widget/android/AndroidBridge.h | 4 +++ 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/media/AudioFocusAgent.java b/mobile/android/base/java/org/mozilla/gecko/media/AudioFocusAgent.java index 1401f4cc3acd..d3e7b7067d3f 100644 --- a/mobile/android/base/java/org/mozilla/gecko/media/AudioFocusAgent.java +++ b/mobile/android/base/java/org/mozilla/gecko/media/AudioFocusAgent.java @@ -16,7 +16,8 @@ public class AudioFocusAgent { private static Context mContext; private AudioManager mAudioManager; private OnAudioFocusChangeListener mAfChangeListener; - private int mAudibleElementCounts; + + private boolean mIsOwningAudioFocus = false; @WrapForJNI public static void notifyStartedPlaying() { @@ -83,12 +84,10 @@ public class AudioFocusAgent { GeckoAppShell.notifyObservers(topic, data); } - private AudioFocusAgent() { - mAudibleElementCounts = 0; - } + private AudioFocusAgent() {} private void requestAudioFocusIfNeeded() { - if (!isFirstAudibleElement()) { + if (mIsOwningAudioFocus) { return; } @@ -100,22 +99,18 @@ public class AudioFocusAgent { "AudioFocus request granted" : "AudioFoucs request failed"; Log.d(LOGTAG, focusMsg); // TODO : Enable media control when get the AudioFocus, see bug1240423. + if (result == AudioManager.AUDIOFOCUS_GAIN) { + mIsOwningAudioFocus = true; + } } private void abandonAudioFocusIfNeeded() { - if (!isLastAudibleElement()) { + if (!mIsOwningAudioFocus) { return; } Log.d(LOGTAG, "Abandon AudioFocus"); mAudioManager.abandonAudioFocus(mAfChangeListener); - } - - private boolean isFirstAudibleElement() { - return (++mAudibleElementCounts == 1); - } - - private boolean isLastAudibleElement() { - return (--mAudibleElementCounts == 0); + mIsOwningAudioFocus = false; } } \ No newline at end of file diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 4f568246cdf1..beccc1e610e7 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -1646,15 +1646,37 @@ nsAndroidBridge::Observe(nsISupports* aSubject, const char* aTopic, RemoveObservers(); } else if (!strcmp(aTopic, "audio-playback")) { ALOG_BRIDGE("nsAndroidBridge::Observe, get audio-playback event."); + + nsCOMPtr window = do_QueryInterface(aSubject); + MOZ_ASSERT(window); + nsAutoString activeStr(aData); - if (activeStr.EqualsLiteral("active")) { + bool isPlaying = activeStr.EqualsLiteral("active"); + + UpdateAudioPlayingWindows(window, isPlaying); + } + return NS_OK; +} + +void +nsAndroidBridge::UpdateAudioPlayingWindows(nsPIDOMWindowOuter* aWindow, + bool aPlaying) +{ + // Request audio focus for the first audio playing window and abandon focus + // for the last audio playing window. + if (aPlaying && !mAudioPlayingWindows.Contains(aWindow)) { + mAudioPlayingWindows.AppendElement(aWindow); + if (mAudioPlayingWindows.Length() == 1) { + ALOG_BRIDGE("nsAndroidBridge, request audio focus."); AudioFocusAgent::NotifyStartedPlaying(); - } else { + } + } else if (!aPlaying && mAudioPlayingWindows.Contains(aWindow)) { + mAudioPlayingWindows.RemoveElement(aWindow); + if (mAudioPlayingWindows.Length() == 0) { + ALOG_BRIDGE("nsAndroidBridge, abandon audio focus."); AudioFocusAgent::NotifyStoppedPlaying(); } } - - return NS_OK; } void diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index a8d732f63f4f..bd0e4444d119 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -600,6 +600,10 @@ private: void AddObservers(); void RemoveObservers(); + void UpdateAudioPlayingWindows(nsPIDOMWindowOuter* aWindow, bool aPlaying); + + nsTArray mAudioPlayingWindows; + protected: }; From eaa6e6ced4efa26f6c260fa00d065307c7598095 Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Wed, 1 Jun 2016 10:22:17 +0800 Subject: [PATCH 174/199] Bug 1257738 - part3 : add test. MozReview-Commit-ID: AJ4Ua5Hnups --- toolkit/content/tests/browser/browser.ini | 5 + .../tests/browser/browser_audioCompeting.js | 114 ++++++++++++++++++ .../tests/browser/file_multipleAudio.html | 18 ++- 3 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 toolkit/content/tests/browser/browser_audioCompeting.js diff --git a/toolkit/content/tests/browser/browser.ini b/toolkit/content/tests/browser/browser.ini index 2a785ead9d75..12139b6412ff 100644 --- a/toolkit/content/tests/browser/browser.ini +++ b/toolkit/content/tests/browser/browser.ini @@ -3,6 +3,11 @@ support-files = head.js file_contentTitle.html audio.ogg + +[browser_audioCompeting.js] +tags = audiochannel +support-files = + file_multipleAudio.html [browser_autoscroll_disabled.js] [browser_bug295977_autoscroll_overflow.js] [browser_bug451286.js] diff --git a/toolkit/content/tests/browser/browser_audioCompeting.js b/toolkit/content/tests/browser/browser_audioCompeting.js new file mode 100644 index 000000000000..624e97885afb --- /dev/null +++ b/toolkit/content/tests/browser/browser_audioCompeting.js @@ -0,0 +1,114 @@ +const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_multipleAudio.html"; + +function* wait_for_tab_playing_event(tab, expectPlaying) { + if (tab.soundPlaying == expectPlaying) { + ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing"); + } else { + yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => { + if (event.detail.changed.indexOf("soundplaying") >= 0) { + is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing"); + return true; + } + return false; + }); + } +} + +function play_audio_from_invisible_tab () { + return new Promise(resolve => { + var autoPlay = content.document.getElementById('autoplay'); + if (!autoPlay) { + ok(false, "Can't get the audio element!"); + } + + is(autoPlay.paused, true, "Audio in tab 1 was paused by audio competing."); + autoPlay.play(); + autoPlay.onpause = function() { + autoPlay.onpause = null; + ok(true, "Audio in tab 1 can't playback when other tab is playing in foreground."); + resolve(); + }; + }); +} + +function audio_should_keep_playing_even_go_to_background () { + var autoPlay = content.document.getElementById('autoplay'); + if (!autoPlay) { + ok(false, "Can't get the audio element!"); + } + + is(autoPlay.paused, false, "Audio in tab 2 is still playing in the background."); +} + +function play_non_autoplay_audio () { + return new Promise(resolve => { + var autoPlay = content.document.getElementById('autoplay'); + var nonAutoPlay = content.document.getElementById('nonautoplay'); + if (!autoPlay || !nonAutoPlay) { + ok(false, "Can't get the audio element!"); + } + + is(nonAutoPlay.paused, true, "Non-autoplay audio isn't started playing yet."); + nonAutoPlay.play(); + + nonAutoPlay.onplay = function() { + nonAutoPlay.onplay = null; + is(nonAutoPlay.paused, false, "Start Non-autoplay audio."); + is(autoPlay.paused, false, "Autoplay audio is still playing."); + resolve(); + }; + }); +} + +add_task(function* setup_test_preference() { + yield new Promise(resolve => { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.audiochannel.audioCompeting", true] + ]}, resolve); + }); +}); + +add_task(function* cross_tabs_audio_competing () { + info("- open tab 1 in foreground -"); + let tab1 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser, + "about:blank"); + tab1.linkedBrowser.loadURI(PAGE); + yield wait_for_tab_playing_event(tab1, true); + + info("- open tab 2 in foreground -"); + let tab2 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser, + "about:blank"); + tab2.linkedBrowser.loadURI(PAGE); + yield wait_for_tab_playing_event(tab1, false); + + info("- open tab 3 in foreground -"); + let tab3 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser, + "about:blank"); + yield ContentTask.spawn(tab2.linkedBrowser, null, + audio_should_keep_playing_even_go_to_background); + + info("- play audio from background tab 1 -"); + yield ContentTask.spawn(tab1.linkedBrowser, null, + play_audio_from_invisible_tab); + + info("- remove tabs -"); + yield BrowserTestUtils.removeTab(tab1); + yield BrowserTestUtils.removeTab(tab2); + yield BrowserTestUtils.removeTab(tab3); +}); + +add_task(function* within_one_tab_audio_competing () { + info("- open tab and play audio1 -"); + let tab = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser, + "about:blank"); + tab.linkedBrowser.loadURI(PAGE); + yield wait_for_tab_playing_event(tab, true); + + info("- play audio2 in the same tab -"); + yield ContentTask.spawn(tab.linkedBrowser, null, + play_non_autoplay_audio); + + info("- remove tab -"); + yield BrowserTestUtils.removeTab(tab); +}); + diff --git a/toolkit/content/tests/browser/file_multipleAudio.html b/toolkit/content/tests/browser/file_multipleAudio.html index fb860aac1944..5dc37febb48d 100644 --- a/toolkit/content/tests/browser/file_multipleAudio.html +++ b/toolkit/content/tests/browser/file_multipleAudio.html @@ -1,7 +1,19 @@ + + + + - - + + From 20401f453e37ee2b45a7f84b6eb042bbcf48dd5f Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Tue, 31 May 2016 19:17:52 -0700 Subject: [PATCH 175/199] Bug 1276093 - Warn on slow CPU format conversions. - r=jrmuizel --- dom/canvas/TexUnpackBlob.cpp | 19 +++++++++++++++---- dom/canvas/TexUnpackBlob.h | 2 +- dom/canvas/WebGLTexelConversions.cpp | 7 ++++++- dom/canvas/WebGLTexelConversions.h | 3 ++- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/dom/canvas/TexUnpackBlob.cpp b/dom/canvas/TexUnpackBlob.cpp index d577149f59bf..b4801c3c7e2a 100644 --- a/dom/canvas/TexUnpackBlob.cpp +++ b/dom/canvas/TexUnpackBlob.cpp @@ -218,10 +218,11 @@ TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun // And go!: MOZ_ASSERT(srcOrigin != dstOrigin || srcPremultiplied != dstPremultiplied); + bool unused_wasTrivial; if (!ConvertImage(mWidth, mHeight, mBytes, rowStride, srcOrigin, texelFormat, srcPremultiplied, tempBuffer.get(), rowStride, dstOrigin, texelFormat, - dstPremultiplied)) + dstPremultiplied, &unused_wasTrivial)) { MOZ_ASSERT(false, "ConvertImage failed unexpectedly."); *out_glError = LOCAL_GL_OUT_OF_MEMORY; @@ -662,7 +663,7 @@ TexUnpackSurface::ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackI gfx::DataSourceSurface* surf, bool isSurfAlphaPremult, UniqueBuffer* const out_convertedBuffer, uint8_t* const out_convertedAlignment, - bool* const out_outOfMemory) + bool* const out_wasTrivial, bool* const out_outOfMemory) { *out_outOfMemory = false; @@ -719,9 +720,11 @@ TexUnpackSurface::ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackI const bool dstPremultiplied = webgl->mPixelStore_PremultiplyAlpha; // And go!: + bool wasTrivial; if (!ConvertImage(width, height, srcBegin, srcStride, srcOrigin, srcFormat, srcPremultiplied, - dstBegin, dstStride, dstOrigin, dstFormat, dstPremultiplied)) + dstBegin, dstStride, dstOrigin, dstFormat, dstPremultiplied, + &wasTrivial)) { MOZ_ASSERT(false, "ConvertImage failed unexpectedly."); NS_ERROR("ConvertImage failed unexpectedly."); @@ -731,6 +734,7 @@ TexUnpackSurface::ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackI *out_convertedBuffer = Move(dstBuffer); *out_convertedAlignment = dstAlignment; + *out_wasTrivial = wasTrivial; return true; } @@ -785,9 +789,10 @@ TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* f UniqueBuffer convertedBuffer; uint8_t convertedAlignment; + bool wasTrivial; bool outOfMemory; if (!ConvertSurface(webgl, dui, dataSurf, mIsAlphaPremult, &convertedBuffer, - &convertedAlignment, &outOfMemory)) + &convertedAlignment, &wasTrivial, &outOfMemory)) { if (outOfMemory) { *out_glError = LOCAL_GL_OUT_OF_MEMORY; @@ -798,6 +803,12 @@ TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* f return; } + if (!wasTrivial) { + webgl->GenerateWarning("%s: Chosen format/type incured an expensive reformat:" + " 0x%04x/0x%04x", + funcName, dui->unpackFormat, dui->unpackType); + } + MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() ); ScopedUnpackReset scopedReset(webgl); webgl->gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, convertedAlignment); diff --git a/dom/canvas/TexUnpackBlob.h b/dom/canvas/TexUnpackBlob.h index ed9589d1bb20..b9497164533c 100644 --- a/dom/canvas/TexUnpackBlob.h +++ b/dom/canvas/TexUnpackBlob.h @@ -147,7 +147,7 @@ protected: gfx::DataSourceSurface* surf, bool isSurfAlphaPremult, UniqueBuffer* const out_convertedBuffer, uint8_t* const out_convertedAlignment, - bool* const out_outOfMemory); + bool* const out_wasTrivial, bool* const out_outOfMemory); static bool UploadDataSurface(bool isSubImage, WebGLContext* webgl, TexImageTarget target, GLint level, const webgl::DriverUnpackInfo* dui, GLint xOffset, diff --git a/dom/canvas/WebGLTexelConversions.cpp b/dom/canvas/WebGLTexelConversions.cpp index 8a91e5edd8de..86c0ae60cace 100644 --- a/dom/canvas/WebGLTexelConversions.cpp +++ b/dom/canvas/WebGLTexelConversions.cpp @@ -349,8 +349,11 @@ ConvertImage(size_t width, size_t height, const void* srcBegin, size_t srcStride, gl::OriginPos srcOrigin, WebGLTexelFormat srcFormat, bool srcPremultiplied, void* dstBegin, size_t dstStride, gl::OriginPos dstOrigin, - WebGLTexelFormat dstFormat, bool dstPremultiplied) + WebGLTexelFormat dstFormat, bool dstPremultiplied, + bool* const out_wasTrivial) { + *out_wasTrivial = true; + if (srcFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion || dstFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion) { @@ -410,6 +413,8 @@ ConvertImage(size_t width, size_t height, return true; } + *out_wasTrivial = false; + WebGLImageConverter converter(width, height, srcItr, dstItr, srcStride, dstItrStride); converter.run(srcFormat, dstFormat, premultOp); diff --git a/dom/canvas/WebGLTexelConversions.h b/dom/canvas/WebGLTexelConversions.h index 07c1627d7ff9..19af9f65e7a1 100644 --- a/dom/canvas/WebGLTexelConversions.h +++ b/dom/canvas/WebGLTexelConversions.h @@ -43,7 +43,8 @@ bool ConvertImage(size_t width, size_t height, const void* srcBegin, size_t srcStride, gl::OriginPos srcOrigin, WebGLTexelFormat srcFormat, bool srcPremultiplied, void* dstBegin, size_t dstStride, gl::OriginPos dstOrigin, - WebGLTexelFormat dstFormat, bool dstPremultiplied); + WebGLTexelFormat dstFormat, bool dstPremultiplied, + bool* out_wasTrivial); ////////////////////////////////////////////////////////////////////////////////////////// From cb9b33550e6ae05f954fc789391e5e6a2003a0c6 Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Tue, 31 May 2016 19:18:31 -0700 Subject: [PATCH 176/199] Bug 1276093 - Warn when we fail to hit the GPU-copy fast-path. - r=jrmuizel --- dom/canvas/TexUnpackBlob.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dom/canvas/TexUnpackBlob.cpp b/dom/canvas/TexUnpackBlob.cpp index b4801c3c7e2a..8d9a22b9e157 100644 --- a/dom/canvas/TexUnpackBlob.cpp +++ b/dom/canvas/TexUnpackBlob.cpp @@ -312,6 +312,10 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun return; // Blitting was successful, so we're done! } while (false); + webgl->GenerateWarning("%s: Failed to hit GPU-copy fast-path. Falling back to CPU" + " upload.", + funcName); + RefPtr surface = mImage->GetAsSourceSurface(); if (!surface) { *out_glError = LOCAL_GL_OUT_OF_MEMORY; From a6496367019ab6ab282544abfa6cbd581dc7f323 Mon Sep 17 00:00:00 2001 From: Ting-Yu Lin Date: Tue, 31 May 2016 17:40:31 +0800 Subject: [PATCH 177/199] Bug 1276870 - Rename parentReflowState to mParentReflowState in nsHTMLReflowState. r=dholbert MozReview-Commit-ID: ETfT1Iow7kF --HG-- extra : rebase_source : 0f679077ccb91a1873cb9be9def4008d55579725 --- layout/generic/nsBlockFrame.cpp | 14 +++---- layout/generic/nsColumnSetFrame.cpp | 4 +- layout/generic/nsFlexContainerFrame.cpp | 4 +- layout/generic/nsFrame.cpp | 4 +- layout/generic/nsHTMLReflowState.cpp | 46 ++++++++++----------- layout/generic/nsHTMLReflowState.h | 2 +- layout/generic/nsRubyBaseContainerFrame.cpp | 2 +- layout/svg/nsSVGForeignObjectFrame.cpp | 2 +- layout/tables/nsTableCellFrame.cpp | 10 ++--- layout/tables/nsTableFrame.cpp | 8 ++-- layout/tables/nsTableRowGroupFrame.cpp | 4 +- 11 files changed, 50 insertions(+), 50 deletions(-) diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 1200576cc9b8..3ca1586705ac 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -969,11 +969,11 @@ CalculateContainingBlockSizeForAbsolutes(WritingMode aWM, // the correct padding area with the legend taken into account. const nsHTMLReflowState* aLastRS = &aReflowState; const nsHTMLReflowState* lastButOneRS = &aReflowState; - while (aLastRS->parentReflowState && - aLastRS->parentReflowState->frame->GetContent() == frame->GetContent() && - aLastRS->parentReflowState->frame->GetType() != nsGkAtoms::fieldSetFrame) { + while (aLastRS->mParentReflowState && + aLastRS->mParentReflowState->frame->GetContent() == frame->GetContent() && + aLastRS->mParentReflowState->frame->GetType() != nsGkAtoms::fieldSetFrame) { lastButOneRS = aLastRS; - aLastRS = aLastRS->parentReflowState; + aLastRS = aLastRS->mParentReflowState; } if (aLastRS != &aReflowState) { // Scrollbars need to be specifically excluded, if present, because they are outside the @@ -1550,7 +1550,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, if (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedBSize() && (GetParent()->GetType() != nsGkAtoms::columnSetFrame || - aReflowState.parentReflowState->AvailableBSize() == NS_UNCONSTRAINEDSIZE)) { + aReflowState.mParentReflowState->AvailableBSize() == NS_UNCONSTRAINEDSIZE)) { ComputeFinalBSize(aReflowState, &aState.mReflowStatus, aState.mBCoord + nonCarriedOutBDirMargin, borderPadding, finalSize, aState.mConsumedBSize); @@ -1956,8 +1956,8 @@ nsBlockFrame::PropagateFloatDamage(nsBlockReflowState& aState, nscoord aDeltaBCoord) { nsFloatManager *floatManager = aState.mReflowState.mFloatManager; - NS_ASSERTION((aState.mReflowState.parentReflowState && - aState.mReflowState.parentReflowState->mFloatManager == floatManager) || + NS_ASSERTION((aState.mReflowState.mParentReflowState && + aState.mReflowState.mParentReflowState->mFloatManager == floatManager) || aState.mReflowState.mBlockDelta == 0, "Bad block delta passed in"); // Check to see if there are any floats; if there aren't, there can't diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp index eaeafbbc32eb..421926e1e3ac 100644 --- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -236,8 +236,8 @@ nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState, if (isBalancing) { const uint32_t MAX_NESTED_COLUMN_BALANCING = 2; uint32_t cnt = 0; - for (const nsHTMLReflowState* rs = aReflowState.parentReflowState; - rs && cnt < MAX_NESTED_COLUMN_BALANCING; rs = rs->parentReflowState) { + for (const nsHTMLReflowState* rs = aReflowState.mParentReflowState; + rs && cnt < MAX_NESTED_COLUMN_BALANCING; rs = rs->mParentReflowState) { if (rs->mFlags.mIsColumnBalancing) { ++cnt; } diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp index 6eb4de8e2891..8954352a49df 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -1517,7 +1517,7 @@ nsFlexContainerFrame:: // have a single-line (nowrap) flex container which itself has a definite // cross-size. Otherwise, we'll wait to do stretching, since (in other // cases) we don't know how much the item should stretch yet. - const nsHTMLReflowState* flexContainerRS = aItemReflowState.parentReflowState; + const nsHTMLReflowState* flexContainerRS = aItemReflowState.mParentReflowState; MOZ_ASSERT(flexContainerRS, "flex item's reflow state should have ptr to container's state"); if (NS_STYLE_FLEX_WRAP_NOWRAP == flexContainerRS->mStylePosition->mFlexWrap) { @@ -1705,7 +1705,7 @@ FlexItem::FlexItem(nsHTMLReflowState& aFlexItemReflowState, MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW), "out-of-flow frames should not be treated as flex items"); - const nsHTMLReflowState* containerRS = aFlexItemReflowState.parentReflowState; + const nsHTMLReflowState* containerRS = aFlexItemReflowState.mParentReflowState; if (IsLegacyBox(containerRS->mStyleDisplay, containerRS->frame->StyleContext())) { // For -webkit-box/-webkit-inline-box, we need to: diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index bb389a1e70d6..bb1e4057f077 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -10251,7 +10251,7 @@ DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame, // find the frame of the parent reflow state (usually just the parent of aFrame) nsIFrame* parentFrame; if (aReflowState) { - const nsHTMLReflowState* parentRS = aReflowState->parentReflowState; + const nsHTMLReflowState* parentRS = aReflowState->mParentReflowState; parentFrame = (parentRS) ? parentRS->frame : nullptr; } else { parentFrame = aFrame->GetParent(); @@ -10626,7 +10626,7 @@ nsHTMLReflowState::DisplayInitConstraintsEnter(nsIFrame* aFrame, DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); printf("InitConstraints parent=%p", - (void*)aState->parentReflowState); + (void*)aState->mParentReflowState); char width[16]; char height[16]; diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index 6cd8f936412f..c8b0444a6925 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -67,7 +67,7 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, MOZ_ASSERT(aPresContext, "no pres context"); MOZ_ASSERT(aFrame, "no frame"); MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); - parentReflowState = nullptr; + mParentReflowState = nullptr; AvailableISize() = aAvailableSpace.ISize(mWritingMode); AvailableBSize() = aAvailableSpace.BSize(mWritingMode); mFloatManager = nullptr; @@ -191,13 +191,13 @@ nsHTMLReflowState::nsHTMLReflowState( !NS_SUBTREE_DIRTY(aFrame), "frame should be clean when getting special bsize reflow"); - parentReflowState = &aParentReflowState; + mParentReflowState = &aParentReflowState; // If the parent is dirty, then the child is as well. // XXX Are the other cases where the parent reflows a child a second // time, as a resize? if (!mFlags.mSpecialBSizeReflow) - frame->AddStateBits(parentReflowState->frame->GetStateBits() & + frame->AddStateBits(mParentReflowState->frame->GetStateBits() & NS_FRAME_IS_DIRTY); AvailableISize() = aAvailableSpace.ISize(mWritingMode); @@ -238,7 +238,7 @@ nsHTMLReflowState::nsHTMLReflowState( ? aParentReflowState.mPercentBSizeObserver : nullptr; if ((aFlags & DUMMY_PARENT_REFLOW_STATE) || - (parentReflowState->mFlags.mDummyParentReflowState && + (mParentReflowState->mFlags.mDummyParentReflowState && frame->GetType() == nsGkAtoms::tableFrame)) { mFlags.mDummyParentReflowState = true; } @@ -368,8 +368,8 @@ nsHTMLReflowState::Init(nsPresContext* aPresContext, if (AvailableISize() == NS_UNCONSTRAINEDSIZE) { // Look up the parent chain for an orthogonal inline limit, // and reset AvailableISize() if found. - for (const nsHTMLReflowState *parent = parentReflowState; - parent != nullptr; parent = parent->parentReflowState) { + for (const nsHTMLReflowState *parent = mParentReflowState; + parent != nullptr; parent = parent->mParentReflowState) { if (parent->GetWritingMode().IsOrthogonalTo(mWritingMode) && parent->mOrthogonalLimit != NS_UNCONSTRAINEDSIZE) { AvailableISize() = parent->mOrthogonalLimit; @@ -460,8 +460,8 @@ nsHTMLReflowState::Init(nsPresContext* aPresContext, } } - if (parentReflowState && - parentReflowState->GetWritingMode().IsOrthogonalTo(mWritingMode)) { + if (mParentReflowState && + mParentReflowState->GetWritingMode().IsOrthogonalTo(mWritingMode)) { // Orthogonal frames are always reflowed with an unconstrained // dimension to avoid incomplete reflow across an orthogonal // boundary. Normally this is the block-size, but for column sets @@ -486,21 +486,21 @@ nsHTMLReflowState::Init(nsPresContext* aPresContext, void nsHTMLReflowState::InitCBReflowState() { - if (!parentReflowState) { + if (!mParentReflowState) { mCBReflowState = nullptr; return; } - if (parentReflowState->frame == frame->GetContainingBlock()) { + if (mParentReflowState->frame == frame->GetContainingBlock()) { // Inner table frames need to use the containing block of the outer // table frame. if (frame->GetType() == nsGkAtoms::tableFrame) { - mCBReflowState = parentReflowState->mCBReflowState; + mCBReflowState = mParentReflowState->mCBReflowState; } else { - mCBReflowState = parentReflowState; + mCBReflowState = mParentReflowState; } } else { - mCBReflowState = parentReflowState->mCBReflowState; + mCBReflowState = mParentReflowState->mCBReflowState; } } @@ -732,7 +732,7 @@ nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameT const nsHTMLReflowState *rs = this; bool hitCBReflowState = false; do { - rs = rs->parentReflowState; + rs = rs->mParentReflowState; if (!rs) { break; } @@ -1053,8 +1053,8 @@ nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, If so, use the info from the reflow state. */ const nsHTMLReflowState* state; if (aFrame->GetStateBits() & NS_FRAME_IN_REFLOW) { - for (state = parentReflowState; state && state->frame != aFrame; - state = state->parentReflowState) { + for (state = mParentReflowState; state && state->frame != aFrame; + state = state->mParentReflowState) { /* do nothing */ } } else { @@ -1908,7 +1908,7 @@ CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState) nscoord result = NS_AUTOHEIGHT; const nsHTMLReflowState* rs = aCBReflowState; - for (; rs; rs = rs->parentReflowState) { + for (; rs; rs = rs->mParentReflowState) { nsIAtom* frameType = rs->frame->GetType(); // if the ancestor is auto height then skip it and continue up if it // is the first block frame and possibly the body/html @@ -1982,9 +1982,9 @@ CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState) } // if we got to the html frame (a block child of the canvas) ... else if (nsGkAtoms::blockFrame == frameType && - rs->parentReflowState && + rs->mParentReflowState && nsGkAtoms::canvasFrame == - rs->parentReflowState->frame->GetType()) { + rs->mParentReflowState->frame->GetType()) { // ... then subtract out margin/border/padding for the BODY element result -= GetBlockMarginBorderPadding(secondAncestorRS); } @@ -2123,7 +2123,7 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, // If this is a reflow root, then set the computed width and // height equal to the available space - if (nullptr == parentReflowState || mFlags.mDummyParentReflowState) { + if (nullptr == mParentReflowState || mFlags.mDummyParentReflowState) { // XXXldb This doesn't mean what it used to! InitOffsets(wm, OffsetPercentBasis(frame, wm, aContainingBlockSize), aFrameType, aBorder, aPadding); @@ -2167,7 +2167,7 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, // See if the containing block is a cell frame which needs // to use the mComputedHeight of the cell instead of what the cell block passed in. // XXX It seems like this could lead to bugs with min-height and friends - if (cbrs->parentReflowState) { + if (cbrs->mParentReflowState) { fType = cbrs->frame->GetType(); if (IS_TABLE_CELL(fType)) { // use the cell's computed block size @@ -2614,13 +2614,13 @@ nsHTMLReflowState::CalculateBlockSideMargins(nsIAtom* aFrameType) // 'direction' property of the parent to tell which margin to // ignore // First check if there is an HTML alignment that we should honor - const nsHTMLReflowState* prs = parentReflowState; + const nsHTMLReflowState* prs = mParentReflowState; if (aFrameType == nsGkAtoms::tableFrame) { NS_ASSERTION(prs->frame->GetType() == nsGkAtoms::tableOuterFrame, "table not inside outer table"); // Center the table within the outer table based on the alignment // of the outer table's parent. - prs = prs->parentReflowState; + prs = prs->mParentReflowState; } if (prs && (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT || diff --git a/layout/generic/nsHTMLReflowState.h b/layout/generic/nsHTMLReflowState.h index efd5ed151d8e..0956c9b135cf 100644 --- a/layout/generic/nsHTMLReflowState.h +++ b/layout/generic/nsHTMLReflowState.h @@ -269,7 +269,7 @@ protected: struct nsHTMLReflowState : public nsCSSOffsetState { // the reflow states are linked together. this is the pointer to the // parent's reflow state - const nsHTMLReflowState* parentReflowState; + const nsHTMLReflowState* mParentReflowState; // pointer to the float manager associated with this area nsFloatManager* mFloatManager; diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp index a4170d624407..a434edb06860 100644 --- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -348,7 +348,7 @@ nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext, } nsHTMLReflowState* reflowState = new nsHTMLReflowState( - aPresContext, *aReflowState.parentReflowState, textContainer, + aPresContext, *aReflowState.mParentReflowState, textContainer, availSize.ConvertTo(textContainer->GetWritingMode(), lineWM)); reflowStates.AppendElement(reflowState); nsLineLayout* lineLayout = new nsLineLayout(aPresContext, diff --git a/layout/svg/nsSVGForeignObjectFrame.cpp b/layout/svg/nsSVGForeignObjectFrame.cpp index b38d3669e98a..0e016764feaa 100644 --- a/layout/svg/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/nsSVGForeignObjectFrame.cpp @@ -141,7 +141,7 @@ nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext, // ReflowSVG makes sure mRect is up to date before we're called. - NS_ASSERTION(!aReflowState.parentReflowState, + NS_ASSERTION(!aReflowState.mParentReflowState, "should only get reflow from being reflow root"); NS_ASSERTION(aReflowState.ComputedWidth() == GetSize().width && aReflowState.ComputedHeight() == GetSize().height, diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index 6c488ebebab4..992351f259ea 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -133,12 +133,12 @@ nsTableCellFrame::NotifyPercentBSize(const nsHTMLReflowState& aReflowState) if (nsTableFrame::AncestorsHaveStyleBSize(*cellRS) || (GetTableFrame()->GetEffectiveRowSpan(*this) == 1 && - cellRS->parentReflowState->frame-> + cellRS->mParentReflowState->frame-> HasAnyStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE))) { - for (const nsHTMLReflowState *rs = aReflowState.parentReflowState; + for (const nsHTMLReflowState *rs = aReflowState.mParentReflowState; rs != cellRS; - rs = rs->parentReflowState) { + rs = rs->mParentReflowState) { rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } @@ -151,7 +151,7 @@ nsTableCellFrame::NotifyPercentBSize(const nsHTMLReflowState& aReflowState) bool nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState& aReflowState) { - const nsHTMLReflowState *rs = aReflowState.parentReflowState; + const nsHTMLReflowState *rs = aReflowState.mParentReflowState; if (!rs) return false; if (rs->frame == this) { @@ -160,7 +160,7 @@ nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState& aReflowState) // propagated to its kids. return true; } - rs = rs->parentReflowState; + rs = rs->mParentReflowState; if (!rs) { return false; } diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 1c61ef7229ee..0d710b1b6139 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1678,7 +1678,7 @@ nsTableFrame::AncestorsHaveStyleBSize(const nsHTMLReflowState& aParentReflowStat { WritingMode wm = aParentReflowState.GetWritingMode(); for (const nsHTMLReflowState* rs = &aParentReflowState; - rs && rs->frame; rs = rs->parentReflowState) { + rs && rs->frame; rs = rs->mParentReflowState) { nsIAtom* frameType = rs->frame->GetType(); if (IS_TABLE_CELL(frameType) || (nsGkAtoms::tableRowFrame == frameType) || @@ -1713,7 +1713,7 @@ nsTableFrame::CheckRequestSpecialBSizeReflow(const nsHTMLReflowState& aReflowSta (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedBSize() || // no computed bsize 0 == aReflowState.ComputedBSize()) && eStyleUnit_Percent == aReflowState.mStylePosition->BSize(wm).GetUnit() && // pct bsize - nsTableFrame::AncestorsHaveStyleBSize(*aReflowState.parentReflowState)) { + nsTableFrame::AncestorsHaveStyleBSize(*aReflowState.mParentReflowState)) { nsTableFrame::RequestSpecialBSizeReflow(aReflowState); } } @@ -1727,7 +1727,7 @@ void nsTableFrame::RequestSpecialBSizeReflow(const nsHTMLReflowState& aReflowState) { // notify the frame and its ancestors of the special reflow, stopping at the containing table - for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->parentReflowState) { + for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->mParentReflowState) { nsIAtom* frameType = rs->frame->GetType(); NS_ASSERTION(IS_TABLE_CELL(frameType) || nsGkAtoms::tableRowFrame == frameType || @@ -2762,7 +2762,7 @@ nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState) aReflowState.Init(presContext, nullptr, pCollapseBorder, &padding); NS_ASSERTION(!mBits.mResizedColumns || - !aReflowState.parentReflowState->mFlags.mSpecialBSizeReflow, + !aReflowState.mParentReflowState->mFlags.mSpecialBSizeReflow, "should not resize columns on special bsize reflow"); if (mBits.mResizedColumns) { aReflowState.SetIResize(true); diff --git a/layout/tables/nsTableRowGroupFrame.cpp b/layout/tables/nsTableRowGroupFrame.cpp index d70ee2d0f7c1..62ad52c59021 100644 --- a/layout/tables/nsTableRowGroupFrame.cpp +++ b/layout/tables/nsTableRowGroupFrame.cpp @@ -1563,9 +1563,9 @@ nsTableRowGroupFrame::GetBSizeBasis(const nsHTMLReflowState& aReflowState) result = aReflowState.ComputedBSize() - cellSpacing; } else { - const nsHTMLReflowState* parentRS = aReflowState.parentReflowState; + const nsHTMLReflowState* parentRS = aReflowState.mParentReflowState; if (parentRS && (tableFrame != parentRS->frame)) { - parentRS = parentRS->parentReflowState; + parentRS = parentRS->mParentReflowState; } if (parentRS && (tableFrame == parentRS->frame) && (parentRS->ComputedBSize() > 0) && (parentRS->ComputedBSize() < NS_UNCONSTRAINEDSIZE)) { From 21cc78c1a6d6756f2e9603e0b14fe15d8de904a2 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Tue, 31 May 2016 23:39:34 -0400 Subject: [PATCH 178/199] Bug 1276156: Stop encoder ProcessThreads before deleting the channel r=pkerr To avoid deadlocks between DeleteChannel() and Process() code MozReview-Commit-ID: G650SWDH8s0 --- .../webrtc/video_engine/vie_channel_group.cc | 12 ++++++++++++ .../webrtc/video_engine/vie_channel_group.h | 1 + .../webrtc/video_engine/vie_channel_manager.cc | 17 +++++++++++++++++ .../trunk/webrtc/video_engine/vie_encoder.cc | 11 +++++++---- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.cc b/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.cc index 3b752cc72266..5c81dd830bca 100644 --- a/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.cc +++ b/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.cc @@ -281,6 +281,18 @@ bool ChannelGroup::CreateChannel(int channel_id, return true; } +void ChannelGroup::Stop(int channel_id) { + ViEEncoder* vie_encoder = GetEncoder(channel_id); + DCHECK(vie_encoder != NULL); + + // If we're owning the encoder, remove the feedback and stop all encoding + // threads and processing. This must be done before deleting the channel. + if (vie_encoder->channel_id() == channel_id) { + encoder_state_feedback_->RemoveEncoder(vie_encoder); + vie_encoder->StopThreadsAndRemoveSharedMembers(); + } +} + void ChannelGroup::DeleteChannel(int channel_id) { ViEChannel* vie_channel = PopChannel(channel_id); diff --git a/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.h b/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.h index 6db1a98bc907..e3d06af08028 100644 --- a/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.h +++ b/media/webrtc/trunk/webrtc/video_engine/vie_channel_group.h @@ -52,6 +52,7 @@ class ChannelGroup : public BitrateObserver { int base_channel_id, int number_of_cores, bool disable_default_encoder); + void Stop(int channel_id); void DeleteChannel(int channel_id); void AddChannel(int channel_id); void RemoveChannel(int channel_id); diff --git a/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.cc b/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.cc index 669f9213cda2..1528c1baa603 100644 --- a/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.cc +++ b/media/webrtc/trunk/webrtc/video_engine/vie_channel_manager.cc @@ -147,6 +147,23 @@ int ViEChannelManager::CreateChannel(int* channel_id, int ViEChannelManager::DeleteChannel(int channel_id) { ChannelGroup* group = NULL; + { + // Read lock to make sure no one tries to delete it on us + ViEChannelManagerScoped(*this); + + // Protect the maps. + CriticalSectionScoped cs(channel_id_critsect_); + + group = FindGroup(channel_id); + if (group == NULL) + return -1; + group->Stop(channel_id); + } + // Now release the read lock, and get a write lock since we know the + // threads are stopped. Otherwise we can deadlock if we hold a write + // lock and the thread is processing and needs a read lock, and we try to + // stop the thread (ProcessThread::DeRegisterModule(). See Mozilla + // bug 1276156. { // Write lock to make sure no one is using the channel. ViEManagerWriteScoped wl(this); diff --git a/media/webrtc/trunk/webrtc/video_engine/vie_encoder.cc b/media/webrtc/trunk/webrtc/video_engine/vie_encoder.cc index 84b68fc16076..bc60a09bb5c8 100644 --- a/media/webrtc/trunk/webrtc/video_engine/vie_encoder.cc +++ b/media/webrtc/trunk/webrtc/video_engine/vie_encoder.cc @@ -220,10 +220,13 @@ void ViEEncoder::StartThreadsAndSetSharedMembers( } void ViEEncoder::StopThreadsAndRemoveSharedMembers() { - vcm_.RegisterProtectionCallback(NULL); - vcm_protection_callback_ = NULL; - module_process_thread_.DeRegisterModule(&vcm_); - module_process_thread_.DeRegisterModule(&vpm_); + // Avoid trying to stop the threads/etc twice + if (vcm_protection_callback_) { + vcm_.RegisterProtectionCallback(NULL); + vcm_protection_callback_ = NULL; + module_process_thread_.DeRegisterModule(&vcm_); + module_process_thread_.DeRegisterModule(&vpm_); + } } void ViEEncoder::SetLoadManager(CPULoadStateCallbackInvoker* load_manager) { From 420b8a9b0e7e2d7591cf645b86ad72c2197c89a3 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Tue, 31 May 2016 23:45:17 -0400 Subject: [PATCH 179/199] Bug 1240209: use buffer-half-empty callbacks to send buffered data instead of a timer r=tuexen,drno --- netwerk/sctp/datachannel/DataChannel.cpp | 146 ++++++++++------------- netwerk/sctp/datachannel/DataChannel.h | 34 +++--- 2 files changed, 77 insertions(+), 103 deletions(-) diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index dc6f2b39ee56..4fa4ce492016 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -148,6 +148,42 @@ receive_cb(struct socket* sock, union sctp_sockstore addr, return connection->ReceiveCallback(sock, data, datalen, rcv, flags); } +static +DataChannelConnection * +GetConnectionFromSocket(struct socket* sock) +{ + struct sockaddr *addrs = nullptr; + int naddrs = usrsctp_getladdrs(sock, 0, &addrs); + if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) { + return nullptr; + } + // usrsctp_getladdrs() returns the addresses bound to this socket, which + // contains the SctpDataMediaChannel* as sconn_addr. Read the pointer, + // then free the list of addresses once we have the pointer. We only open + // AF_CONN sockets, and they should all have the sconn_addr set to the + // pointer that created them, so [0] is as good as any other. + struct sockaddr_conn *sconn = reinterpret_cast(&addrs[0]); + DataChannelConnection *connection = + reinterpret_cast(sconn->sconn_addr); + usrsctp_freeladdrs(addrs); + + return connection; +} + +// called when the buffer empties to the threshold value +static int +threshold_event(struct socket* sock, uint32_t sb_free) +{ + DataChannelConnection *connection = GetConnectionFromSocket(sock); + if (connection) { + LOG(("SendDeferred()")); + connection->SendDeferredMessages(); + } else { + LOG(("Can't find connection for socket %p", sock)); + } + return 0; +} + static void debug_printf(const char *format, ...) { @@ -176,8 +212,6 @@ DataChannelConnection::DataChannelConnection(DataConnectionListener *listener) : mListener = listener; mLocalPort = 0; mRemotePort = 0; - mDeferTimeout = 10; - mTimerRunning = false; LOG(("Constructor DataChannelConnection=%p, listener=%p", this, mListener.get())); mInternalIOThread = nullptr; } @@ -267,9 +301,6 @@ void DataChannelConnection::DestroyOnSTS(struct socket *aMasterSocket, disconnect_all(); } -NS_IMPL_ISUPPORTS(DataChannelConnection, - nsITimerCallback) - bool DataChannelConnection::Init(unsigned short aPort, uint16_t aNumStreams, bool aUsingDtls) { @@ -334,7 +365,8 @@ DataChannelConnection::Init(unsigned short aPort, uint16_t aNumStreams, bool aUs // Open sctp with a callback if ((mMasterSocket = usrsctp_socket( aUsingDtls ? AF_CONN : AF_INET, - SOCK_STREAM, IPPROTO_SCTP, receive_cb, nullptr, 0, this)) == nullptr) { + SOCK_STREAM, IPPROTO_SCTP, receive_cb, threshold_event, + usrsctp_sysctl_get_sctp_sendspace() / 2, this)) == nullptr) { return false; } @@ -445,60 +477,6 @@ error_cleanup: return false; } -void -DataChannelConnection::StartDefer() -{ - nsresult rv; - if (!NS_IsMainThread()) { - NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( - DataChannelOnMessageAvailable::START_DEFER, - this, (DataChannel *) nullptr))); - return; - } - - ASSERT_WEBRTC(NS_IsMainThread()); - if (!mDeferredTimer) { - mDeferredTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); - MOZ_ASSERT(mDeferredTimer); - } - - if (!mTimerRunning) { - rv = mDeferredTimer->InitWithCallback(this, mDeferTimeout, - nsITimer::TYPE_ONE_SHOT); - NS_ENSURE_TRUE_VOID(rv == NS_OK); - - mTimerRunning = true; - } -} - -// nsITimerCallback - -NS_IMETHODIMP -DataChannelConnection::Notify(nsITimer *timer) -{ - ASSERT_WEBRTC(NS_IsMainThread()); - LOG(("%s: %p [%p] (%dms), sending deferred messages", __FUNCTION__, this, timer, mDeferTimeout)); - - if (timer == mDeferredTimer) { - if (SendDeferredMessages()) { - // Still blocked - // we don't need a lock, since this must be main thread... - nsresult rv = mDeferredTimer->InitWithCallback(this, mDeferTimeout, - nsITimer::TYPE_ONE_SHOT); - if (NS_FAILED(rv)) { - LOG(("%s: cannot initialize open timer", __FUNCTION__)); - // XXX and do....? - return rv; - } - mTimerRunning = true; - } else { - LOG(("Turned off deferred send timer")); - mTimerRunning = false; - } - } - return NS_OK; -} - #ifdef MOZ_PEERCONNECTION void DataChannelConnection::SetEvenOdd() @@ -1030,7 +1008,6 @@ DataChannelConnection::SendDeferredMessages() uint32_t i; RefPtr channel; // we may null out the refs to this bool still_blocked = false; - bool sent = false; // This may block while something is modifying channels, but should not block for IO MutexAutoLock lock(mLock); @@ -1056,7 +1033,6 @@ DataChannelConnection::SendDeferredMessages() NS_DispatchToMainThread(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this, channel))); - sent = true; } else { if (errno == EAGAIN || errno == EWOULDBLOCK) { still_blocked = true; @@ -1077,7 +1053,6 @@ DataChannelConnection::SendDeferredMessages() if (channel->mFlags & DATA_CHANNEL_FLAGS_SEND_ACK) { if (SendOpenAckMessage(channel->mStream)) { channel->mFlags &= ~DATA_CHANNEL_FLAGS_SEND_ACK; - sent = true; } else { if (errno == EAGAIN || errno == EWOULDBLOCK) { still_blocked = true; @@ -1099,7 +1074,7 @@ DataChannelConnection::SendDeferredMessages() channel->mBufferedData.Clear(); } - uint32_t buffered_amount = channel->GetBufferedAmount(); + uint32_t buffered_amount = channel->GetBufferedAmountLocked(); uint32_t threshold = channel->GetBufferedAmountLowThreshold(); bool was_over_threshold = buffered_amount >= threshold; @@ -1126,7 +1101,6 @@ DataChannelConnection::SendDeferredMessages() } } else { LOG(("Resent buffer of %d bytes (%d)", len, result)); - sent = true; // In theory this could underflow if >4GB was buffered and re // truncated in GetBufferedAmount(), but this won't cause any problems. buffered_amount -= channel->mBufferedData[0]->mLength; @@ -1160,18 +1134,7 @@ DataChannelConnection::SendDeferredMessages() break; } - if (!still_blocked) { - // mDeferTimeout becomes an estimate of how long we need to wait next time we block - return false; - } - // adjust time? More time for next wait if we didn't send anything, less if did - // Pretty crude, but better than nothing; just to keep CPU use down - if (!sent && mDeferTimeout < 50) - mDeferTimeout++; - else if (sent && mDeferTimeout > 10) - mDeferTimeout--; - - return true; + return still_blocked; } void @@ -1268,7 +1231,8 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_ if (!SendOpenAckMessage(stream)) { // XXX Only on EAGAIN!? And if not, then close the channel?? channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_ACK; - StartDefer(); + // Note: we're locked, so there's no danger of a race with the + // buffer-threshold callback } // Now process any queued data messages for the channel (which will @@ -1882,8 +1846,8 @@ DataChannelConnection::HandleStreamChangeEvent(const struct sctp_stream_change_e channel->mStream = stream; mStreams[stream] = channel; channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_REQ; - /// XXX fix - StartDefer(); + // Note: we're locked, so there's no danger of a race with the + // buffer-threshold callback } else { /* We will not find more ... */ break; @@ -2147,8 +2111,8 @@ DataChannelConnection::OpenFinish(already_AddRefed&& aChannel) LOG(("SendOpenRequest failed, errno = %d", errno)); if (errno == EAGAIN || errno == EWOULDBLOCK) { channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_REQ; - StartDefer(); - + // Note: we're locked, so there's no danger of a race with the + // buffer-threshold callback return channel.forget(); } else { if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) { @@ -2237,6 +2201,19 @@ DataChannelConnection::SendMsgInternal(DataChannel *channel, const char *data, // SCTP will return EMSGSIZE if the message is bigger than the buffer // size (or EAGAIN if there isn't space) + + // Avoid a race between buffer-full-failure (where we have to add the + // packet to the buffered-data queue) and the buffer-now-only-half-full + // callback, which happens on a different thread. Otherwise we might + // fail here, then before we add it to the queue get the half-full + // callback, find nothing to do, then on this thread add it to the + // queue - which would sit there. Also, if we later send more data, it + // would arrive ahead of the buffered message, but if the buffer ever + // got to 1/2 full, the message would get sent - but at a semi-random + // time, after other data it was supposed to be in front of. + + // Must lock before empty check for similar reasons! + MutexAutoLock lock(mLock); if (channel->mBufferedData.IsEmpty()) { result = usrsctp_sendv(mSocket, data, length, nullptr, 0, @@ -2250,12 +2227,12 @@ DataChannelConnection::SendMsgInternal(DataChannel *channel, const char *data, } if (result < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { + // queue data for resend! And queue any further data for the stream until it is... BufferedMsg *buffered = new BufferedMsg(spa, data, length); // infallible malloc channel->mBufferedData.AppendElement(buffered); // owned by mBufferedData array channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_DATA; LOG(("Queued %u buffers (len=%u)", channel->mBufferedData.Length(), length)); - StartDefer(); return 0; } LOG(("error %d sending string", errno)); @@ -2624,9 +2601,10 @@ DataChannel::AppReady() } uint32_t -DataChannel::GetBufferedAmount() +DataChannel::GetBufferedAmountLocked() const { size_t buffered = 0; + for (auto& buffer : mBufferedData) { buffered += buffer->mLength; } diff --git a/netwerk/sctp/datachannel/DataChannel.h b/netwerk/sctp/datachannel/DataChannel.h index f0817909c404..e8693580d23a 100644 --- a/netwerk/sctp/datachannel/DataChannel.h +++ b/netwerk/sctp/datachannel/DataChannel.h @@ -21,7 +21,6 @@ #include "nsTArray.h" #include "nsDeque.h" #include "nsIInputStream.h" -#include "nsITimer.h" #include "mozilla/Mutex.h" #include "DataChannelProtocol.h" #include "DataChannelListener.h" @@ -92,16 +91,15 @@ public: }; // One per PeerConnection -class DataChannelConnection: public nsITimerCallback +class DataChannelConnection #ifdef SCTP_DTLS_SUPPORTED - , public sigslot::has_slots<> + : public sigslot::has_slots<> #endif { virtual ~DataChannelConnection(); public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSITIMERCALLBACK + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataChannelConnection) class DataConnectionListener : public SupportsWeakPtr { @@ -189,6 +187,8 @@ public: void GetStreamIds(std::vector* aStreamList); + bool SendDeferredMessages(); + protected: friend class DataChannelOnMessageAvailable; // Avoid cycles with PeerConnectionImpl @@ -222,8 +222,6 @@ private: already_AddRefed OpenFinish(already_AddRefed&& aChannel); - void StartDefer(); - bool SendDeferredMessages(); void ProcessQueuedOpens(); void ClearResets(); void SendOutgoingStreamReset(); @@ -266,7 +264,7 @@ private: AutoTArray,16> mStreams; nsDeque mPending; // Holds addref'ed DataChannel's -- careful! // holds data that's come in before a channel is open - nsTArray > mQueuedData; + nsTArray> mQueuedData; // Streams pending reset AutoTArray mStreamsResetting; @@ -283,10 +281,6 @@ private: uint16_t mRemotePort; bool mUsingDtls; - // Timer to control when we try to resend blocked messages - nsCOMPtr mDeferredTimer; - uint32_t mDeferTimeout; // in ms - bool mTimerRunning; nsCOMPtr mInternalIOThread; }; @@ -387,7 +381,12 @@ public: bool GetOrdered() { return !(mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED); } // Amount of data buffered to send - uint32_t GetBufferedAmount(); + uint32_t GetBufferedAmount() + { + MutexAutoLock lock(mConnection->mLock); + return GetBufferedAmountLocked(); + } + // Trigger amount for generating BufferedAmountLow events uint32_t GetBufferedAmountLowThreshold(); @@ -423,6 +422,7 @@ private: friend class DataChannelConnection; nsresult AddDataToBinaryMsg(const char *data, uint32_t size); + uint32_t GetBufferedAmountLocked() const; RefPtr mConnection; nsCString mLabel; @@ -437,8 +437,8 @@ private: bool mIsRecvBinary; size_t mBufferedThreshold; nsCString mRecvBuffer; - nsTArray > mBufferedData; - nsTArray > mQueuedMessages; + nsTArray> mBufferedData; // GUARDED_BY(mConnection->mLock) + nsTArray> mQueuedMessages; }; // used to dispatch notifications of incoming data to the main thread @@ -454,7 +454,6 @@ public: ON_CHANNEL_OPEN, ON_CHANNEL_CLOSED, ON_DATA, - START_DEFER, BUFFER_LOW_THRESHOLD, NO_LONGER_BUFFERED, }; /* types */ @@ -555,9 +554,6 @@ public: break; } break; - case START_DEFER: - mConnection->StartDefer(); - break; } return NS_OK; } From f587ac226e8e0cbc7b14f16a67a6c76adbeef883 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Tue, 31 May 2016 11:54:30 +0800 Subject: [PATCH 180/199] Bug 1276840 - Remove AudioStream::RUNNING. r=kinetik. MozReview-Commit-ID: 3sCGYifUxJO --HG-- extra : rebase_source : f20ee5647bb92b7bf1a35fde9bb131f4585cb32c --- dom/media/AudioStream.cpp | 7 +------ dom/media/AudioStream.h | 3 +-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/dom/media/AudioStream.cpp b/dom/media/AudioStream.cpp index 8ed269a7c2ab..7db8f457c878 100644 --- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -441,7 +441,7 @@ AudioStream::Pause() return; } - if (mState != STARTED && mState != RUNNING) { + if (mState != STARTED) { mState = STOPPED; // which also tells async OpenCubeb not to start, just init return; } @@ -626,11 +626,6 @@ AudioStream::DataCallback(void* aBuffer, long aFrames) // NOTE: wasapi (others?) can call us back *after* stop()/Shutdown() (mState == SHUTDOWN) // Bug 996162 - // callback tells us cubeb succeeded initializing - if (mState == STARTED) { - mState = RUNNING; - } - if (mInRate == mOutRate) { GetUnprocessed(writer); } else { diff --git a/dom/media/AudioStream.h b/dom/media/AudioStream.h index f7e1c08e790b..7942ecc57b60 100644 --- a/dom/media/AudioStream.h +++ b/dom/media/AudioStream.h @@ -363,8 +363,7 @@ private: enum StreamState { INITIALIZED, // Initialized, playback has not begun. - STARTED, // cubeb started, but callbacks haven't started - RUNNING, // DataCallbacks have started after STARTED, or after Resume(). + STARTED, // cubeb started. STOPPED, // Stopped by a call to Pause(). DRAINED, // StateCallback has indicated that the drain is complete. ERRORED, // Stream disabled due to an internal error. From 23317c963f5641ead42d808301a0a8a37ccb8182 Mon Sep 17 00:00:00 2001 From: Astley Chen Date: Wed, 1 Jun 2016 11:00:58 +0800 Subject: [PATCH 181/199] Bug 1258286: Part 1 - add layer types to nsStyleImageLayers and layer initialization. r=heycam MozReview-Commit-ID: ENLXAEA62Bm --HG-- extra : histedit_source : a93d2a0d1603026dfc94e38e8e754646cd307b6d --- layout/style/nsComputedDOMStyle.cpp | 4 +- layout/style/nsRuleNode.cpp | 6 +- layout/style/nsStyleStruct.cpp | 230 ++++++++++++++++--------- layout/style/nsStyleStruct.h | 32 ++-- layout/style/test/property_database.js | 4 +- 5 files changed, 171 insertions(+), 105 deletions(-) diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index cec13022c7e1..1df47dee6cad 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -6104,10 +6104,10 @@ nsComputedDOMStyle::DoGetMask() firstLayer.mComposite != NS_STYLE_MASK_COMPOSITE_ADD || firstLayer.mMaskMode != NS_STYLE_MASK_MODE_MATCH_SOURCE || !firstLayer.mPosition.IsInitialValue() || - !firstLayer.mRepeat.IsInitialValue() || + !firstLayer.mRepeat.IsInitialValue(nsStyleImageLayers::LayerType::Mask) || !firstLayer.mSize.IsInitialValue() || !(firstLayer.mImage.GetType() == eStyleImageType_Null || - firstLayer.mImage.GetType() == eStyleImageType_Image)){ + firstLayer.mImage.GetType() == eStyleImageType_Image)) { return nullptr; } diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index ffd5bb4099c9..09a89dfd7315 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -7088,7 +7088,7 @@ nsRuleNode::ComputeBackgroundData(void* aStartStruct, // background-repeat: enum, inherit, initial [pair list] nsStyleImageLayers::Repeat initialRepeat; - initialRepeat.SetInitialValues(); + initialRepeat.SetInitialValues(nsStyleImageLayers::LayerType::Background); SetImageLayerPairList(aContext, *aRuleData->ValueForBackgroundRepeat(), bg->mImage.mLayers, parentBG->mImage.mLayers, @@ -9897,7 +9897,7 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, // mask-repeat: enum, inherit, initial [pair list] nsStyleImageLayers::Repeat initialRepeat; - initialRepeat.SetInitialValues(); + initialRepeat.SetInitialValues(nsStyleImageLayers::LayerType::Mask); SetImageLayerPairList(aContext, *aRuleData->ValueForMaskRepeat(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, @@ -9921,7 +9921,7 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &nsStyleImageLayers::Layer::mOrigin, - uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_PADDING), + uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_BORDER), parentSVGReset->mMask.mOriginCount, svgReset->mMask.mOriginCount, maxItemCount, rebuild, conditions); diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index a27e4b7294e3..16c5e37bd46a 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -47,7 +47,8 @@ static_assert((((1 << nsStyleStructID_Length) - 1) & const int32_t nsStyleGridLine::kMinLine = -10000; const int32_t nsStyleGridLine::kMaxLine = 10000; -static bool EqualURIs(nsIURI *aURI1, nsIURI *aURI2) +static bool +EqualURIs(nsIURI *aURI1, nsIURI *aURI2) { bool eq; return aURI1 == aURI2 || // handle null==null, and optimize @@ -56,13 +57,15 @@ static bool EqualURIs(nsIURI *aURI1, nsIURI *aURI2) eq); } -static bool EqualURIs(mozilla::css::URLValue *aURI1, mozilla::css::URLValue *aURI2) +static bool +EqualURIs(mozilla::css::URLValue *aURI1, mozilla::css::URLValue *aURI2) { return aURI1 == aURI2 || // handle null==null, and optimize (aURI1 && aURI2 && aURI1->URIEquals(*aURI2)); } -static bool EqualImages(imgIRequest *aImage1, imgIRequest* aImage2) +static bool +EqualImages(imgIRequest *aImage1, imgIRequest* aImage2) { if (aImage1 == aImage2) { return true; @@ -79,7 +82,8 @@ static bool EqualImages(imgIRequest *aImage1, imgIRequest* aImage2) } // A nullsafe wrapper for strcmp. We depend on null-safety. -static int safe_strcmp(const char16_t* a, const char16_t* b) +static int +safe_strcmp(const char16_t* a, const char16_t* b) { if (!a || !b) { return (int)(a - b); @@ -103,8 +107,7 @@ StyleStructContext::HackilyFindSomeDeviceContext() return nsLayoutUtils::GetDeviceContextForScreenInfo(static_cast(win.get())); } -static bool AreShadowArraysEqual(nsCSSShadowArray* lhs, - nsCSSShadowArray* rhs); +static bool AreShadowArraysEqual(nsCSSShadowArray* lhs, nsCSSShadowArray* rhs); // -------------------- // nsStyleFont @@ -180,7 +183,8 @@ nsStyleFont::EnableZoom(nsPresContext* aContext, bool aEnable) } } -nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aOther) const +nsChangeHint +nsStyleFont::CalcDifference(const nsStyleFont& aOther) const { MOZ_ASSERT(mAllowZoom == aOther.mAllowZoom, "expected mAllowZoom to be the same on both nsStyleFonts"); @@ -237,9 +241,8 @@ nsStyleFont::GetLanguage(StyleStructContext aContext) return language.forget(); } -static nscoord CalcCoord(const nsStyleCoord& aCoord, - const nscoord* aEnumTable, - int32_t aNumEnums) +static nscoord +CalcCoord(const nsStyleCoord& aCoord, const nscoord* aEnumTable, int32_t aNumEnums) { if (aCoord.GetUnit() == eStyleUnit_Enumerated) { MOZ_ASSERT(aEnumTable, "must have enum table"); @@ -276,7 +279,8 @@ nsStyleMargin::Destroy(nsPresContext* aContext) { FreeByObjectID(eArenaObjectID_nsStyleMargin, this); } -nsChangeHint nsStyleMargin::CalcDifference(const nsStyleMargin& aOther) const +nsChangeHint +nsStyleMargin::CalcDifference(const nsStyleMargin& aOther) const { if (mMargin == aOther.mMargin) { return NS_STYLE_HINT_NONE; @@ -310,7 +314,8 @@ nsStylePadding::Destroy(nsPresContext* aContext) { FreeByObjectID(eArenaObjectID_nsStylePadding, this); } -nsChangeHint nsStylePadding::CalcDifference(const nsStylePadding& aOther) const +nsChangeHint +nsStylePadding::CalcDifference(const nsStylePadding& aOther) const { if (mPadding == aOther.mPadding) { return NS_STYLE_HINT_NONE; @@ -323,13 +328,13 @@ nsChangeHint nsStylePadding::CalcDifference(const nsStylePadding& aOther) const } nsStyleBorder::nsStyleBorder(StyleStructContext aContext) - : mBorderColors(nullptr), - mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL), - mBorderImageRepeatH(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH), - mBorderImageRepeatV(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH), - mFloatEdge(NS_STYLE_FLOAT_EDGE_CONTENT_BOX), - mBoxDecorationBreak(NS_STYLE_BOX_DECORATION_BREAK_SLICE), - mComputedBorder(0, 0, 0, 0) + : mBorderColors(nullptr) + , mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL) + , mBorderImageRepeatH(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) + , mBorderImageRepeatV(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) + , mFloatEdge(NS_STYLE_FLOAT_EDGE_CONTENT_BOX) + , mBoxDecorationBreak(NS_STYLE_BOX_DECORATION_BREAK_SLICE) + , mComputedBorder(0, 0, 0, 0) { MOZ_COUNT_CTOR(nsStyleBorder); @@ -369,20 +374,20 @@ nsBorderColors::Clone(bool aDeep) const } nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) - : mBorderColors(nullptr), - mBorderRadius(aSrc.mBorderRadius), - mBorderImageSource(aSrc.mBorderImageSource), - mBorderImageSlice(aSrc.mBorderImageSlice), - mBorderImageWidth(aSrc.mBorderImageWidth), - mBorderImageOutset(aSrc.mBorderImageOutset), - mBorderImageFill(aSrc.mBorderImageFill), - mBorderImageRepeatH(aSrc.mBorderImageRepeatH), - mBorderImageRepeatV(aSrc.mBorderImageRepeatV), - mFloatEdge(aSrc.mFloatEdge), - mBoxDecorationBreak(aSrc.mBoxDecorationBreak), - mComputedBorder(aSrc.mComputedBorder), - mBorder(aSrc.mBorder), - mTwipsPerPixel(aSrc.mTwipsPerPixel) + : mBorderColors(nullptr) + , mBorderRadius(aSrc.mBorderRadius) + , mBorderImageSource(aSrc.mBorderImageSource) + , mBorderImageSlice(aSrc.mBorderImageSlice) + , mBorderImageWidth(aSrc.mBorderImageWidth) + , mBorderImageOutset(aSrc.mBorderImageOutset) + , mBorderImageFill(aSrc.mBorderImageFill) + , mBorderImageRepeatH(aSrc.mBorderImageRepeatH) + , mBorderImageRepeatV(aSrc.mBorderImageRepeatV) + , mFloatEdge(aSrc.mFloatEdge) + , mBoxDecorationBreak(aSrc.mBoxDecorationBreak) + , mComputedBorder(aSrc.mComputedBorder) + , mBorder(aSrc.mBorder) + , mTwipsPerPixel(aSrc.mTwipsPerPixel) { MOZ_COUNT_CTOR(nsStyleBorder); if (aSrc.mBorderColors) { @@ -445,7 +450,8 @@ nsStyleBorder::Destroy(nsPresContext* aContext) { FreeByObjectID(eArenaObjectID_nsStyleBorder, this); } -nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const +nsChangeHint +nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const { // XXXbz we should be able to return a more specific change hint for // at least GetComputedBorder() differences... @@ -561,7 +567,8 @@ nsStyleOutline::RecalcData() } } -nsChangeHint nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const +nsChangeHint +nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const { if (mActualOutlineWidth != aOther.mActualOutlineWidth || (mActualOutlineWidth > 0 && @@ -592,8 +599,8 @@ nsChangeHint nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const // nsStyleList // nsStyleList::nsStyleList(StyleStructContext aContext) - : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE), - mCounterStyle(aContext.BuildCounterStyle(NS_LITERAL_STRING("disc"))) + : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE) + , mCounterStyle(aContext.BuildCounterStyle(NS_LITERAL_STRING("disc"))) { MOZ_COUNT_CTOR(nsStyleList); SetQuotesInitial(); @@ -605,10 +612,10 @@ nsStyleList::~nsStyleList() } nsStyleList::nsStyleList(const nsStyleList& aSource) - : mListStylePosition(aSource.mListStylePosition), - mCounterStyle(aSource.mCounterStyle), - mQuotes(aSource.mQuotes), - mImageRegion(aSource.mImageRegion) + : mListStylePosition(aSource.mListStylePosition) + , mCounterStyle(aSource.mCounterStyle) + , mQuotes(aSource.mQuotes) + , mImageRegion(aSource.mImageRegion) { SetListStyleImage(aSource.GetListStyleImage()); MOZ_COUNT_CTOR(nsStyleList); @@ -728,7 +735,8 @@ nsStyleXUL::nsStyleXUL(const nsStyleXUL& aSource) MOZ_COUNT_CTOR(nsStyleXUL); } -nsChangeHint nsStyleXUL::CalcDifference(const nsStyleXUL& aOther) const +nsChangeHint +nsStyleXUL::CalcDifference(const nsStyleXUL& aOther) const { if (mBoxAlign == aOther.mBoxAlign && mBoxDirection == aOther.mBoxDirection && @@ -782,7 +790,8 @@ nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource) MOZ_COUNT_CTOR(nsStyleColumn); } -nsChangeHint nsStyleColumn::CalcDifference(const nsStyleColumn& aOther) const +nsChangeHint +nsStyleColumn::CalcDifference(const nsStyleColumn& aOther) const { if ((mColumnWidth.GetUnit() == eStyleUnit_Auto) != (aOther.mColumnWidth.GetUnit() == eStyleUnit_Auto) || @@ -820,8 +829,7 @@ nsStyleSVG::nsStyleSVG(StyleStructContext aContext) : mFill(eStyleSVGPaintType_Color) // Will be initialized to NS_RGB(0, 0, 0) , mStroke(eStyleSVGPaintType_None) , mStrokeDashoffset(0, nsStyleCoord::CoordConstructor) - , mStrokeWidth(nsPresContext::CSSPixelsToAppUnits(1), - nsStyleCoord::CoordConstructor) + , mStrokeWidth(nsPresContext::CSSPixelsToAppUnits(1), nsStyleCoord::CoordConstructor) , mFillOpacity(1.0f) , mStrokeMiterlimit(4.0f) , mStrokeOpacity(1.0f) @@ -878,8 +886,8 @@ nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource) MOZ_COUNT_CTOR(nsStyleSVG); } -static bool PaintURIChanged(const nsStyleSVGPaint& aPaint1, - const nsStyleSVGPaint& aPaint2) +static bool +PaintURIChanged(const nsStyleSVGPaint& aPaint1, const nsStyleSVGPaint& aPaint2) { if (aPaint1.mType != aPaint2.mType) { return aPaint1.mType == eStyleSVGPaintType_Server || @@ -889,7 +897,8 @@ static bool PaintURIChanged(const nsStyleSVGPaint& aPaint1, !EqualURIs(aPaint1.mPaint.mPaintServer, aPaint2.mPaint.mPaintServer); } -nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const +nsChangeHint +nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const { nsChangeHint hint = nsChangeHint(0); @@ -1036,7 +1045,6 @@ nsStyleClipPath::operator=(const nsStyleClipPath& aOther) return *this; } - bool nsStyleClipPath::operator==(const nsStyleClipPath& aOther) const { @@ -1150,7 +1158,6 @@ nsStyleFilter::operator=(const nsStyleFilter& aOther) return *this; } - bool nsStyleFilter::operator==(const nsStyleFilter& aOther) const { @@ -1215,7 +1222,8 @@ nsStyleFilter::SetDropShadow(nsCSSShadowArray* aDropShadow) // nsStyleSVGReset // nsStyleSVGReset::nsStyleSVGReset(StyleStructContext aContext) - : mStopColor(NS_RGB(0, 0, 0)) + : mMask(nsStyleImageLayers::LayerType::Mask) + , mStopColor(NS_RGB(0, 0, 0)) , mFloodColor(NS_RGB(0, 0, 0)) , mLightingColor(NS_RGB(255, 255, 255)) , mStopOpacity(1.0f) @@ -1247,7 +1255,9 @@ nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource) MOZ_COUNT_CTOR(nsStyleSVGReset); } -void nsStyleSVGReset::Destroy(nsPresContext* aContext) { +void +nsStyleSVGReset::Destroy(nsPresContext* aContext) +{ mMask.UntrackImages(aContext); this->~nsStyleSVGReset(); @@ -1255,7 +1265,8 @@ void nsStyleSVGReset::Destroy(nsPresContext* aContext) { FreeByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, this); } -nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) const +nsChangeHint +nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) const { nsChangeHint hint = nsChangeHint(0); @@ -1339,7 +1350,8 @@ nsStyleSVGPaint::SetType(nsStyleSVGPaintType aType) mType = aType; } -nsStyleSVGPaint& nsStyleSVGPaint::operator=(const nsStyleSVGPaint& aOther) +nsStyleSVGPaint& +nsStyleSVGPaint::operator=(const nsStyleSVGPaint& aOther) { if (this == &aOther) return *this; @@ -1762,7 +1774,8 @@ nsStyleTableBorder::nsStyleTableBorder(const nsStyleTableBorder& aSource) MOZ_COUNT_CTOR(nsStyleTableBorder); } -nsChangeHint nsStyleTableBorder::CalcDifference(const nsStyleTableBorder& aOther) const +nsChangeHint +nsStyleTableBorder::CalcDifference(const nsStyleTableBorder& aOther) const { // Border-collapse changes need a reframe, because we use a different frame // class for table cells in the collapsed border model. This is used to @@ -1799,7 +1812,8 @@ nsStyleColor::nsStyleColor(const nsStyleColor& aSource) MOZ_COUNT_CTOR(nsStyleColor); } -nsChangeHint nsStyleColor::CalcDifference(const nsStyleColor& aOther) const +nsChangeHint +nsStyleColor::CalcDifference(const nsStyleColor& aOther) const { if (mColor == aOther.mColor) return NS_STYLE_HINT_NONE; @@ -2246,7 +2260,7 @@ const nsCSSProperty nsStyleImageLayers::kMaskLayerTable[] = { }; #endif -nsStyleImageLayers::nsStyleImageLayers() +nsStyleImageLayers::nsStyleImageLayers(nsStyleImageLayers::LayerType aType) : mAttachmentCount(1) , mClipCount(1) , mOriginCount(1) @@ -2261,6 +2275,9 @@ nsStyleImageLayers::nsStyleImageLayers() , mLayers(nsStyleAutoArray::WITH_SINGLE_INITIAL_ELEMENT) { MOZ_COUNT_CTOR(nsStyleImageLayers); + + // Ensure first layer is initialized as specified layer type + mLayers[0].Initialize(aType); } nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers &aSource) @@ -2500,24 +2517,42 @@ nsStyleImageLayers::Size::operator==(const Size& aOther) const (mHeightType != eLengthPercentage || mHeight == aOther.mHeight); } -void -nsStyleImageLayers::Repeat::SetInitialValues() +bool +nsStyleImageLayers::Repeat::IsInitialValue(LayerType aType) const { - mXRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT; - mYRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT; + if (aType == LayerType::Background || + aType == LayerType::Mask) { + // bug 1258623 - mask-repeat initial value should be no-repeat + return mXRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT && + mYRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT; + } + + MOZ_ASSERT_UNREACHABLE("unsupported layer type."); + return false; +} + +void +nsStyleImageLayers::Repeat::SetInitialValues(LayerType aType) +{ + if (aType == LayerType::Background || + aType == LayerType::Mask) { + // bug 1258623 - mask-repeat initial value should be no-repeat + mXRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT; + mYRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT; + } else { + MOZ_ASSERT_UNREACHABLE("unsupported layer type."); + } } nsStyleImageLayers::Layer::Layer() -: mClip(NS_STYLE_IMAGELAYER_CLIP_BORDER), - mOrigin(NS_STYLE_IMAGELAYER_ORIGIN_PADDING), - mAttachment(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL), - mBlendMode(NS_STYLE_BLEND_NORMAL), - mComposite(NS_STYLE_MASK_COMPOSITE_ADD), - mMaskMode(NS_STYLE_MASK_MODE_MATCH_SOURCE) + : mClip(NS_STYLE_IMAGELAYER_CLIP_BORDER) + , mAttachment(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL) + , mBlendMode(NS_STYLE_BLEND_NORMAL) + , mComposite(NS_STYLE_MASK_COMPOSITE_ADD) + , mMaskMode(NS_STYLE_MASK_MODE_MATCH_SOURCE) { mPosition.SetInitialPercentValues(0.0f); // Initial value is "0% 0%" mImage.SetNull(); - mRepeat.SetInitialValues(); mSize.SetInitialValues(); } @@ -2525,6 +2560,19 @@ nsStyleImageLayers::Layer::~Layer() { } +void +nsStyleImageLayers::Layer::Initialize(nsStyleImageLayers::LayerType aType) +{ + mRepeat.SetInitialValues(aType); + + if (aType == LayerType::Background) { + mOrigin = NS_STYLE_IMAGELAYER_ORIGIN_PADDING; + } else { + MOZ_ASSERT(aType == LayerType::Mask, "unsupported layer type."); + mOrigin = NS_STYLE_IMAGELAYER_ORIGIN_BORDER; + } +} + bool nsStyleImageLayers::Layer::RenderingMightDependOnPositioningAreaSizeChange() const { @@ -2587,7 +2635,8 @@ nsStyleImageLayers::Layer::CalcDifference(const nsStyleImageLayers::Layer& aOthe // nsStyleBackground::nsStyleBackground(StyleStructContext aContext) - : mBackgroundColor(NS_RGBA(0, 0, 0, 0)) + : mImage(nsStyleImageLayers::LayerType::Background) + , mBackgroundColor(NS_RGBA(0, 0, 0, 0)) { MOZ_COUNT_CTOR(nsStyleBackground); } @@ -2597,7 +2646,6 @@ nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource) , mBackgroundColor(aSource.mBackgroundColor) { MOZ_COUNT_CTOR(nsStyleBackground); - } nsStyleBackground::~nsStyleBackground() @@ -2616,7 +2664,8 @@ nsStyleBackground::Destroy(nsPresContext* aContext) FreeByObjectID(eArenaObjectID_nsStyleBackground, this); } -nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const +nsChangeHint +nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const { nsChangeHint hint = nsChangeHint(0); if (mBackgroundColor != aOther.mBackgroundColor) { @@ -2628,7 +2677,8 @@ nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) return hint; } -bool nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const +bool +nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const { NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) { const nsStyleImageLayers::Layer &layer = mImage.mLayers[i]; @@ -2641,14 +2691,16 @@ bool nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const return false; } -bool nsStyleBackground::IsTransparent() const +bool +nsStyleBackground::IsTransparent() const { return BottomLayer().mImage.IsEmpty() && mImage.mImageCount == 1 && NS_GET_A(mBackgroundColor) == 0; } -void nsTimingFunction::AssignFromKeyword(int32_t aTimingFunctionType) +void +nsTimingFunction::AssignFromKeyword(int32_t aTimingFunctionType) { switch (aTimingFunctionType) { case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START: @@ -2902,7 +2954,8 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) mPerspectiveOrigin[1] = aSource.mPerspectiveOrigin[1]; } -nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const +nsChangeHint +nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const { nsChangeHint hint = nsChangeHint(0); @@ -3138,7 +3191,8 @@ nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource) MOZ_COUNT_CTOR(nsStyleVisibility); } -nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) const +nsChangeHint +nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) const { nsChangeHint hint = nsChangeHint(0); @@ -3188,7 +3242,8 @@ nsStyleContentData::~nsStyleContentData() } } -nsStyleContentData& nsStyleContentData::operator=(const nsStyleContentData& aOther) +nsStyleContentData& +nsStyleContentData::operator=(const nsStyleContentData& aOther) { if (this == &aOther) return *this; @@ -3211,7 +3266,8 @@ nsStyleContentData& nsStyleContentData::operator=(const nsStyleContentData& aOth return *this; } -bool nsStyleContentData::operator==(const nsStyleContentData& aOther) const +bool +nsStyleContentData::operator==(const nsStyleContentData& aOther) const { if (mType != aOther.mType) return false; @@ -3352,7 +3408,8 @@ nsStyleContent::nsStyleContent(const nsStyleContent& aSource) } } -nsChangeHint nsStyleContent::CalcDifference(const nsStyleContent& aOther) const +nsChangeHint +nsStyleContent::CalcDifference(const nsStyleContent& aOther) const { // In ReResolveStyleContext we assume that if there's no existing // ::before or ::after and we don't have to restyle children of the @@ -3401,7 +3458,8 @@ nsChangeHint nsStyleContent::CalcDifference(const nsStyleContent& aOther) const return NS_STYLE_HINT_NONE; } -nsresult nsStyleContent::AllocateContents(uint32_t aCount) +nsresult +nsStyleContent::AllocateContents(uint32_t aCount) { // We need to run the destructors of the elements of mContents, so we // delete and reallocate even if aCount == mContentCount. (If @@ -3428,8 +3486,7 @@ nsresult nsStyleContent::AllocateContents(uint32_t aCount) nsStyleTextReset::nsStyleTextReset(StyleStructContext aContext) : mTextDecorationLine(NS_STYLE_TEXT_DECORATION_LINE_NONE) , mUnicodeBidi(NS_STYLE_UNICODE_BIDI_NORMAL) - , mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID | - BORDER_COLOR_FOREGROUND) + , mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID | BORDER_COLOR_FOREGROUND) , mTextDecorationColor(NS_RGB(0, 0, 0)) { MOZ_COUNT_CTOR(nsStyleTextReset); @@ -3583,7 +3640,8 @@ nsStyleText::~nsStyleText() MOZ_COUNT_DTOR(nsStyleText); } -nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aOther) const +nsChangeHint +nsStyleText::CalcDifference(const nsStyleText& aOther) const { if (WhiteSpaceOrNewlineIsSignificant() != aOther.WhiteSpaceOrNewlineIsSignificant()) { @@ -3755,7 +3813,8 @@ nsStyleUserInterface::~nsStyleUserInterface() delete [] mCursorArray; } -nsChangeHint nsStyleUserInterface::CalcDifference(const nsStyleUserInterface& aOther) const +nsChangeHint +nsStyleUserInterface::CalcDifference(const nsStyleUserInterface& aOther) const { nsChangeHint hint = nsChangeHint(0); if (mCursor != aOther.mCursor) @@ -3837,7 +3896,8 @@ nsStyleUIReset::~nsStyleUIReset() MOZ_COUNT_DTOR(nsStyleUIReset); } -nsChangeHint nsStyleUIReset::CalcDifference(const nsStyleUIReset& aOther) const +nsChangeHint +nsStyleUIReset::CalcDifference(const nsStyleUIReset& aOther) const { // ignore mIMEMode if (mForceBrokenImageIcon != aOther.mForceBrokenImageIcon) diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index c50c27596160..b492be0fb7ba 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -472,12 +472,6 @@ private: }; struct nsStyleImageLayers { - nsStyleImageLayers(); - nsStyleImageLayers(const nsStyleImageLayers &aSource); - ~nsStyleImageLayers() { - MOZ_COUNT_DTOR(nsStyleImageLayers); - } - // Indices into kBackgroundLayerTable and kMaskLayerTable enum { shorthand = 0, @@ -494,6 +488,17 @@ struct nsStyleImageLayers { composite }; + enum class LayerType : uint8_t { + Background = 0, + Mask + }; + + explicit nsStyleImageLayers(LayerType aType); + nsStyleImageLayers(const nsStyleImageLayers &aSource); + ~nsStyleImageLayers() { + MOZ_COUNT_DTOR(nsStyleImageLayers); + } + struct Position; friend struct Position; struct Position { @@ -598,13 +603,10 @@ struct nsStyleImageLayers { // Initialize nothing Repeat() {} - bool IsInitialValue() const { - return mXRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT && - mYRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT; - } + bool IsInitialValue(LayerType aType) const; // Initialize to initial values - void SetInitialValues(); + void SetInitialValues(LayerType aType); bool operator==(const Repeat& aOther) const { return mXRepeat == aOther.mXRepeat && @@ -656,10 +658,14 @@ struct nsStyleImageLayers { // NS_STYLE_MASK_MODE_MATCH_SOURCE. Repeat mRepeat; // [reset] See nsStyleConsts.h - // Initializes only mImage + // This constructor does not initialize mRepeat or mOrigin and Initialize() + // must be called to do that. Layer(); ~Layer(); + // Initialize mRepeat and mOrigin by specified layer type + void Initialize(LayerType aType); + // Register/unregister images with the document. We do this only // after the dust has settled in ComputeBackgroundData. void TrackImages(nsPresContext* aContext) { @@ -3541,7 +3547,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVGReset } nsStyleImageLayers mMask; - nsStyleClipPath mClipPath; // [reset] + nsStyleClipPath mClipPath; // [reset] nscolor mStopColor; // [reset] nscolor mFloodColor; // [reset] nscolor mLightingColor; // [reset] diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index f0c16a390ae5..1f9215ac2053 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -6989,8 +6989,8 @@ if (SupportsMaskShorthand()) { domProp: "maskOrigin", inherited: false, type: CSS_TYPE_LONGHAND, - initial_values: [ "padding-box" ], - other_values: [ "border-box", "content-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ], + initial_values: [ "border-box" ], + other_values: [ "padding-box", "content-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ], invalid_values: [ "margin-box", "padding-box padding-box" ] }; gCSSProperties["mask-position"] = { From 3102a18f881114d4f4dd9cec5e13c6388813a3bc Mon Sep 17 00:00:00 2001 From: Astley Chen Date: Wed, 1 Jun 2016 11:13:48 +0800 Subject: [PATCH 182/199] Bug 1258286: Part 2 - update w3c css masking mask-repeat ref test case. r=heycam MozReview-Commit-ID: 44YCmdFrmok --HG-- extra : histedit_source : caa06b6d50b61e7d256cd88a425b824546ee2e8a --- .../submitted/masking/mask-repeat-1-ref.html | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html index fa386cdac1ef..fe36924f932b 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html @@ -13,33 +13,39 @@ border: 1px solid black; } - div.inner { - width: 50px; - height: 50px; - position: absolute; + .color { background-color: purple; } - #pos-top-left { left: 0; top: 0; } - #pos-top-right { right: 0; top: 0; } - #pos-bottom-left { left: 0; bottom: 0; } - #pos-bottom-right { right: 0; bottom: 0; } + #default { + position: absolute; + width: 50px; height: 50px; + } + + #repeat-x { + position: absolute; + width: 100%; height: 50px; + } + + #repeat-y { + position: absolute; + width: 50px; height: 100%; + }
-
-
-
-
+
-
-
+
+
+
+
+
-
-
+
From 5dc395709dcb0ffb97815ab177b37b85bb1c3471 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 31 May 2016 21:08:31 +1200 Subject: [PATCH 183/199] Bug 1265281 - Blacklist atiumd64.dll versions for D3D9 DXVA which are suspected to crash. r=kentuckyfriedtakahe MozReview-Commit-ID: 1dfmProc7ck --HG-- extra : rebase_source : 14835b1634d56c0399c3b84b094a95f871acbe12 --- modules/libpref/init/all.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index c33e635cf019..fe4af50fd14d 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -326,7 +326,7 @@ pref("media.wmf.low-latency.enabled", false); pref("media.wmf.skip-blacklist", false); pref("media.windows-media-foundation.allow-d3d11-dxva", true); pref("media.wmf.disable-d3d11-for-dlls", "igd10iumd32.dll: 20.19.15.4444, 20.19.15.4424, 20.19.15.4409, 20.19.15.4390, 20.19.15.4380, 20.19.15.4360, 10.18.10.4358, 20.19.15.4331, 20.19.15.4312, 20.19.15.4300, 10.18.15.4281, 10.18.15.4279, 10.18.10.4276, 10.18.15.4268, 10.18.15.4256, 10.18.10.4252, 10.18.15.4248, 10.18.14.4112, 10.18.10.3958, 10.18.10.3496, 10.18.10.3431, 10.18.10.3412, 10.18.10.3355, 9.18.10.3234, 9.18.10.3071, 9.18.10.3055, 9.18.10.3006; igd10umd32.dll: 9.17.10.4229, 9.17.10.3040, 9.17.10.2857, 8.15.10.2274, 8.15.10.2272, 8.15.10.2246, 8.15.10.1840, 8.15.10.1808; igd10umd64.dll: 9.17.10.4229, 10.18.10.3496; isonyvideoprocessor.dll: 4.1.2247.8090, 4.1.2153.6200; tosqep.dll: 1.2.15.526, 1.1.12.201, 1.0.11.318, 1.0.11.215, 1.0.10.1224; tosqep64.dll: 1.1.12.201, 1.0.11.215; nvwgf2um.dll: 10.18.13.6510, 10.18.13.5891, 10.18.13.5887, 10.18.13.5582, 10.18.13.5382, 9.18.13.4195, 9.18.13.3165; atidxx32.dll: 8.17.10.671, 8.17.10.661, 8.17.10.648, 8.17.10.644, 8.17.10.625, 8.17.10.605, 8.17.10.581, 8.17.10.569, 8.17.10.560, 8.17.10.545, 8.17.10.539, 8.17.10.531, 8.17.10.525, 8.17.10.520, 8.17.10.519, 8.17.10.514, 8.17.10.511, 8.17.10.494, 8.17.10.489, 8.17.10.483, 8.17.10.453, 8.17.10.451, 8.17.10.441, 8.17.10.436, 8.17.10.432, 8.17.10.425, 8.17.10.418, 8.17.10.414, 8.17.10.401, 8.17.10.395, 8.17.10.385, 8.17.10.378, 8.17.10.362, 8.17.10.355, 8.17.10.342, 8.17.10.331, 8.17.10.318, 8.17.10.310, 8.17.10.286, 8.17.10.269, 8.17.10.261, 8.17.10.247, 8.17.10.240, 8.15.10.212; atidxx64.dll: 8.17.10.661, 8.17.10.644"); -pref("media.wmf.disable-d3d9-for-dlls", "igdumd64.dll: 8.15.10.2189, 8.771.1.0"); +pref("media.wmf.disable-d3d9-for-dlls", "igdumd64.dll: 8.15.10.2189, 8.771.1.0; atiumd64.dll: 7.14.10.833, 7.14.10.867, 7.14.10.885, 7.14.10.903, 7.14.10.911, 8.14.10.768, 9.14.10.1001, 9.14.10.1017, 9.14.10.1080, 9.14.10.1128, 9.14.10.1162, 9.14.10.1171, 9.14.10.1183, 9.14.10.1197, 9.14.10.945, 9.14.10.972, 9.14.10.984, 9.14.10.996"); #endif #if defined(MOZ_FFMPEG) #if defined(XP_MACOSX) From a27e168838ef7563f1bf6548039ef9090c8cd860 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 31 May 2016 21:49:31 -0700 Subject: [PATCH 184/199] Ignore SIGINT on all subprocesses. (bug 1277068, r=billm) --- toolkit/xre/nsSigHandlers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/xre/nsSigHandlers.cpp b/toolkit/xre/nsSigHandlers.cpp index 312d3fe9db8c..098d9ae7e045 100644 --- a/toolkit/xre/nsSigHandlers.cpp +++ b/toolkit/xre/nsSigHandlers.cpp @@ -264,7 +264,7 @@ void InstallSignalHandlers(const char *aProgname) sigaction(SIGFPE, &sa, &osa); #endif - if (XRE_IsContentProcess()) { + if (!XRE_IsParentProcess()) { /* * If the user is debugging a Gecko parent process in gdb and hits ^C to * suspend, a SIGINT signal will be sent to the child. We ignore this signal From 978954c755983c71624af1c7c930300aad75bf17 Mon Sep 17 00:00:00 2001 From: Kit Cambridge Date: Fri, 27 May 2016 17:30:39 -0700 Subject: [PATCH 185/199] No bug - Use the correct protocol for insecure Push server URLs. r=me MozReview-Commit-ID: FmYTQMSIhkP --HG-- extra : rebase_source : 9a17f7678d6e676e7aff85efbd0cbad7d6cf312a --- dom/push/PushServiceWebSocket.jsm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dom/push/PushServiceWebSocket.jsm b/dom/push/PushServiceWebSocket.jsm index 2c1815cf4667..b1c5e36b5978 100644 --- a/dom/push/PushServiceWebSocket.jsm +++ b/dom/push/PushServiceWebSocket.jsm @@ -477,8 +477,10 @@ this.PushServiceWebSocket = { console.warn("makeWebSocket: Network is offline."); return null; } - let socket = Cc["@mozilla.org/network/protocol;1?name=wss"] - .createInstance(Ci.nsIWebSocketChannel); + let contractId = uri.scheme == "ws" ? + "@mozilla.org/network/protocol;1?name=ws" : + "@mozilla.org/network/protocol;1?name=wss"; + let socket = Cc[contractId].createInstance(Ci.nsIWebSocketChannel); socket.initLoadInfo(null, // aLoadingNode Services.scriptSecurityManager.getSystemPrincipal(), From 02d8a1e5d9d2346c40f148ffa3c644d20d9af0e4 Mon Sep 17 00:00:00 2001 From: Kit Cambridge Date: Tue, 22 Mar 2016 12:09:31 -0700 Subject: [PATCH 186/199] Bug 1265593 - Forward app server keys to Autopush on Android. r=nalexander MozReview-Commit-ID: 3J4mM1k0pcY --HG-- extra : rebase_source : c5a33f2f7043321307da17a6915dedfac66f1fc9 extra : histedit_source : 0357740fc62df416635c90a1ad075f6ee5e492e8 --- dom/push/PushServiceAndroidGCM.jsm | 7 ++++ .../org/mozilla/gecko/push/PushClient.java | 5 +-- .../org/mozilla/gecko/push/PushManager.java | 9 +++--- .../org/mozilla/gecko/push/PushService.java | 3 +- .../gecko/push/autopush/AutopushClient.java | 3 +- .../mozilla/gecko/push/TestPushManager.java | 11 ++++--- .../autopush/test/TestLiveAutopushClient.java | 32 ++++++++++++++++++- 7 files changed, 56 insertions(+), 14 deletions(-) diff --git a/dom/push/PushServiceAndroidGCM.jsm b/dom/push/PushServiceAndroidGCM.jsm index 6b9575164ad2..d65c012dde9e 100644 --- a/dom/push/PushServiceAndroidGCM.jsm +++ b/dom/push/PushServiceAndroidGCM.jsm @@ -208,9 +208,15 @@ this.PushServiceAndroidGCM = { register: function(record) { console.debug("register:", record); let ctime = Date.now(); + let appServerKey = record.appServerKey ? + ChromeUtils.base64URLEncode(record.appServerKey, { + // The Push server requires padding. + pad: true, + }) : null; // Caller handles errors. return Messaging.sendRequestForResult({ type: "PushServiceAndroidGCM:SubscribeChannel", + appServerKey: appServerKey, }).then(data => { console.debug("Got data:", data); return PushCrypto.generateKeys() @@ -227,6 +233,7 @@ this.PushServiceAndroidGCM = { p256dhPublicKey: exportedKeys[0], p256dhPrivateKey: exportedKeys[1], authenticationSecret: PushCrypto.generateAuthenticationSecret(), + appServerKey: record.appServerKey, }) ); }); diff --git a/mobile/android/base/java/org/mozilla/gecko/push/PushClient.java b/mobile/android/base/java/org/mozilla/gecko/push/PushClient.java index 37a022293f97..9c1fab5f9917 100644 --- a/mobile/android/base/java/org/mozilla/gecko/push/PushClient.java +++ b/mobile/android/base/java/org/mozilla/gecko/push/PushClient.java @@ -6,6 +6,7 @@ package org.mozilla.gecko.push; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import org.mozilla.gecko.push.RegisterUserAgentResponse; import org.mozilla.gecko.push.SubscribeChannelResponse; @@ -95,9 +96,9 @@ public class PushClient { delegate.responseOrThrow(); // For side-effects only. } - public SubscribeChannelResponse subscribeChannel(@NonNull String uaid, @NonNull String secret) throws LocalException, AutopushClientException { + public SubscribeChannelResponse subscribeChannel(@NonNull String uaid, @NonNull String secret, @Nullable String appServerKey) throws LocalException, AutopushClientException { final Delegate delegate = new Delegate<>(); - autopushClient.subscribeChannel(uaid, secret, delegate); + autopushClient.subscribeChannel(uaid, secret, appServerKey, delegate); return delegate.responseOrThrow(); } diff --git a/mobile/android/base/java/org/mozilla/gecko/push/PushManager.java b/mobile/android/base/java/org/mozilla/gecko/push/PushManager.java index 03d143c3ab97..42ef60b61904 100644 --- a/mobile/android/base/java/org/mozilla/gecko/push/PushManager.java +++ b/mobile/android/base/java/org/mozilla/gecko/push/PushManager.java @@ -6,6 +6,7 @@ package org.mozilla.gecko.push; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; import org.json.JSONObject; @@ -101,14 +102,14 @@ public class PushManager { return registration; } - public PushSubscription subscribeChannel(final @NonNull String profileName, final @NonNull String service, final @NonNull JSONObject serviceData, final long now) throws ProfileNeedsConfigurationException, AutopushClientException, PushClient.LocalException, GcmTokenClient.NeedsGooglePlayServicesException, IOException { + public PushSubscription subscribeChannel(final @NonNull String profileName, final @NonNull String service, final @NonNull JSONObject serviceData, @Nullable String appServerKey, final long now) throws ProfileNeedsConfigurationException, AutopushClientException, PushClient.LocalException, GcmTokenClient.NeedsGooglePlayServicesException, IOException { Log.i(LOG_TAG, "Subscribing to channel for service: " + service + "; for profile named: " + profileName); final PushRegistration registration = advanceRegistration(profileName, now); - final PushSubscription subscription = subscribeChannel(registration, profileName, service, serviceData, System.currentTimeMillis()); + final PushSubscription subscription = subscribeChannel(registration, profileName, service, serviceData, appServerKey, System.currentTimeMillis()); return subscription; } - protected PushSubscription subscribeChannel(final @NonNull PushRegistration registration, final @NonNull String profileName, final @NonNull String service, final @NonNull JSONObject serviceData, final long now) throws AutopushClientException, PushClient.LocalException { + protected PushSubscription subscribeChannel(final @NonNull PushRegistration registration, final @NonNull String profileName, final @NonNull String service, final @NonNull JSONObject serviceData, @Nullable String appServerKey, final long now) throws AutopushClientException, PushClient.LocalException { final String uaid = registration.uaid.value; final String secret = registration.secret; if (uaid == null || secret == null) { @@ -118,7 +119,7 @@ public class PushManager { // Verify endpoint is not null? final PushClient pushClient = pushClientFactory.getPushClient(registration.autopushEndpoint, registration.debug); - final SubscribeChannelResponse result = pushClient.subscribeChannel(uaid, secret); + final SubscribeChannelResponse result = pushClient.subscribeChannel(uaid, secret, appServerKey); if (registration.debug) { Log.i(LOG_TAG, "Got chid: " + result.channelID + " and endpoint: " + result.endpoint); } else { diff --git a/mobile/android/base/java/org/mozilla/gecko/push/PushService.java b/mobile/android/base/java/org/mozilla/gecko/push/PushService.java index 3c099eea08e0..3a975fece9ad 100644 --- a/mobile/android/base/java/org/mozilla/gecko/push/PushService.java +++ b/mobile/android/base/java/org/mozilla/gecko/push/PushService.java @@ -322,6 +322,7 @@ public class PushService implements BundleEventListener { if ("PushServiceAndroidGCM:SubscribeChannel".equals(event)) { final String service = SERVICE_WEBPUSH; final JSONObject serviceData; + final String appServerKey = message.getString("appServerKey"); try { serviceData = new JSONObject(); serviceData.put("profileName", geckoProfile.getName()); @@ -334,7 +335,7 @@ public class PushService implements BundleEventListener { final PushSubscription subscription; try { - subscription = pushManager.subscribeChannel(geckoProfile.getName(), service, serviceData, System.currentTimeMillis()); + subscription = pushManager.subscribeChannel(geckoProfile.getName(), service, serviceData, appServerKey, System.currentTimeMillis()); } catch (PushManager.ProfileNeedsConfigurationException | AutopushClientException | PushClient.LocalException | IOException e) { Log.e(LOG_TAG, "Got exception in " + event, e); callback.sendError("Got exception handling message [" + event + "]: " + e.toString()); diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/push/autopush/AutopushClient.java b/mobile/android/services/src/main/java/org/mozilla/gecko/push/autopush/AutopushClient.java index 80822a76d8ea..8edd92f9eb52 100644 --- a/mobile/android/services/src/main/java/org/mozilla/gecko/push/autopush/AutopushClient.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/push/autopush/AutopushClient.java @@ -340,7 +340,7 @@ public class AutopushClient { } - public void subscribeChannel(final String uaid, final String secret, RequestDelegate delegate) { + public void subscribeChannel(final String uaid, final String secret, final String appServerKey, RequestDelegate delegate) { final BaseResource resource; try { resource = new BaseResource(new URI(serverURI + "registration/" + uaid + "/subscription")); @@ -366,6 +366,7 @@ public class AutopushClient { }; final ExtendedJSONObject body = new ExtendedJSONObject(); + body.put("key", appServerKey); resource.post(body); } diff --git a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/push/TestPushManager.java b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/push/TestPushManager.java index e7aaf3faab4d..42ae0f543fa7 100644 --- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/push/TestPushManager.java +++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/push/TestPushManager.java @@ -19,6 +19,7 @@ import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -46,7 +47,7 @@ public class TestPushManager { doReturn(new SubscribeChannelResponse("opaque-chid", "https://localhost:8085/opaque-push-endpoint")) .when(pushClient) - .subscribeChannel(anyString(), anyString()); + .subscribeChannel(anyString(), anyString(), isNull(String.class)); PushManager.PushClientFactory pushClientFactory = mock(PushManager.PushClientFactory.class); doReturn(pushClient).when(pushClientFactory).getPushClient(anyString(), anyBoolean()); @@ -140,7 +141,7 @@ public class TestPushManager { // We should be able to register with non-null serviceData. final JSONObject webpushData = new JSONObject(); webpushData.put("version", 5); - PushSubscription subscription = manager.subscribeChannel("default", "webpush", webpushData, System.currentTimeMillis()); + PushSubscription subscription = manager.subscribeChannel("default", "webpush", webpushData, null, System.currentTimeMillis()); assertSubscribed(subscription); subscription = manager.registrationForSubscription(subscription.chid).getSubscription(subscription.chid); @@ -148,7 +149,7 @@ public class TestPushManager { Assert.assertEquals(5, subscription.serviceData.get("version")); // We should be able to register with null serviceData. - subscription = manager.subscribeChannel("default", "sync", null, System.currentTimeMillis()); + subscription = manager.subscribeChannel("default", "sync", null, null, System.currentTimeMillis()); assertSubscribed(subscription); subscription = manager.registrationForSubscription(subscription.chid).getSubscription(subscription.chid); @@ -165,7 +166,7 @@ public class TestPushManager { // We should be able to register with non-null serviceData. final JSONObject webpushData = new JSONObject(); webpushData.put("version", 5); - PushSubscription subscription = manager.subscribeChannel("default", "webpush", webpushData, System.currentTimeMillis()); + PushSubscription subscription = manager.subscribeChannel("default", "webpush", webpushData, null, System.currentTimeMillis()); assertSubscribed(subscription); // No exception is success. @@ -224,7 +225,7 @@ public class TestPushManager { registration = manager.registerUserAgent("default", System.currentTimeMillis()); assertRegistered(registration, "http://localhost:8080", true); - PushSubscription subscription = manager.subscribeChannel("default", "webpush", null, System.currentTimeMillis()); + PushSubscription subscription = manager.subscribeChannel("default", "webpush", null, null, System.currentTimeMillis()); assertSubscribed(subscription); manager.startup(System.currentTimeMillis()); diff --git a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/push/autopush/test/TestLiveAutopushClient.java b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/push/autopush/test/TestLiveAutopushClient.java index 10507e513774..102ea34e4891 100644 --- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/push/autopush/test/TestLiveAutopushClient.java +++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/push/autopush/test/TestLiveAutopushClient.java @@ -3,12 +3,17 @@ package org.mozilla.gecko.push.autopush.test; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; + import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mozilla.apache.commons.codec.binary.Base64; import org.mozilla.gecko.background.fxa.FxAccountUtils; import org.mozilla.gecko.background.testhelpers.TestRunner; import org.mozilla.gecko.background.testhelpers.WaitHelper; @@ -20,6 +25,7 @@ import org.mozilla.gecko.push.autopush.AutopushClientException; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.net.BaseResource; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.startsWith; import static org.mockito.Mockito.any; @@ -108,13 +114,14 @@ public class TestLiveAutopushClient { // We should be able to subscribe to a channel. final RequestDelegate subscribeDelegate = mock(RequestDelegate.class); - client.subscribeChannel(registerResponse.uaid, registerResponse.secret, subscribeDelegate); + client.subscribeChannel(registerResponse.uaid, registerResponse.secret, null, subscribeDelegate); final SubscribeChannelResponse subscribeResponse = assertSuccess(subscribeDelegate, SubscribeChannelResponse.class); Assert.assertNotNull(subscribeResponse); Assert.assertNotNull(subscribeResponse.channelID); Assert.assertNotNull(subscribeResponse.endpoint); Assert.assertThat(subscribeResponse.endpoint, startsWith(FxAccountUtils.getAudienceForURL(serverURL))); + Assert.assertThat(subscribeResponse.endpoint, containsString("/v1/")); // And we should be able to unsubscribe. final RequestDelegate unsubscribeDelegate = mock(RequestDelegate.class); @@ -122,6 +129,29 @@ public class TestLiveAutopushClient { Assert.assertNull(assertSuccess(unsubscribeDelegate, Void.class)); + // We should be able to create a restricted subscription by specifying + // an ECDSA public key using the P-256 curve. + final RequestDelegate subscribeWithKeyDelegate = mock(RequestDelegate.class); + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDSA"); + keyPairGenerator.initialize(256); + final KeyPair keyPair = keyPairGenerator.generateKeyPair(); + final PublicKey publicKey = keyPair.getPublic(); + String appServerKey = Base64.encodeBase64URLSafeString(publicKey.getEncoded()); + client.subscribeChannel(registerResponse.uaid, registerResponse.secret, appServerKey, subscribeWithKeyDelegate); + + final SubscribeChannelResponse subscribeWithKeyResponse = assertSuccess(subscribeWithKeyDelegate, SubscribeChannelResponse.class); + Assert.assertNotNull(subscribeWithKeyResponse); + Assert.assertNotNull(subscribeWithKeyResponse.channelID); + Assert.assertNotNull(subscribeWithKeyResponse.endpoint); + Assert.assertThat(subscribeWithKeyResponse.endpoint, startsWith(FxAccountUtils.getAudienceForURL(serverURL))); + Assert.assertThat(subscribeWithKeyResponse.endpoint, containsString("/v2/")); + + // And we should be able to drop the restricted subscription. + final RequestDelegate unsubscribeWithKeyDelegate = mock(RequestDelegate.class); + client.unsubscribeChannel(registerResponse.uaid, registerResponse.secret, subscribeWithKeyResponse.channelID, unsubscribeWithKeyDelegate); + + Assert.assertNull(assertSuccess(unsubscribeWithKeyDelegate, Void.class)); + // Trying to unsubscribe a second time should give a 410. final RequestDelegate reunsubscribeDelegate = mock(RequestDelegate.class); client.unsubscribeChannel(registerResponse.uaid, registerResponse.secret, subscribeResponse.channelID, reunsubscribeDelegate); From d9d287a14e344fe961f1a84ea25cf1725c04422e Mon Sep 17 00:00:00 2001 From: Kit Cambridge Date: Tue, 19 Apr 2016 22:04:09 -0700 Subject: [PATCH 187/199] Bug 1265841 - Implement the `notificationclose` service worker event. r=wchen,baku MozReview-Commit-ID: EQfCbQKqn9H --HG-- extra : rebase_source : 2dad51a3c148db794769fb7c64c28f81ea2ca6ff extra : histedit_source : 254e2e372ba605f74c1f5106f40b207e492e85e1 --- dom/base/nsGkAtomList.h | 1 + .../base/nsIServiceWorkerManager.idl | 13 ++++ dom/notification/Notification.cpp | 60 +++++++++------ dom/webidl/NotificationEvent.webidl | 1 + dom/workers/ServiceWorkerManager.cpp | 65 ++++++++++++---- dom/workers/ServiceWorkerManager.h | 14 ++++ dom/workers/ServiceWorkerPrivate.cpp | 76 +++++++++++-------- dom/workers/ServiceWorkerPrivate.h | 25 +++--- dom/workers/WorkerScope.h | 1 + dom/workers/test/serviceworkers/mochitest.ini | 3 + .../serviceworkers/notificationclose.html | 37 +++++++++ .../test/serviceworkers/notificationclose.js | 19 +++++ .../test_notificationclose.html | 62 +++++++++++++++ 13 files changed, 299 insertions(+), 78 deletions(-) create mode 100644 dom/workers/test/serviceworkers/notificationclose.html create mode 100644 dom/workers/test/serviceworkers/notificationclose.js create mode 100644 dom/workers/test/serviceworkers/test_notificationclose.html diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index c93adafed895..539629855d87 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -856,6 +856,7 @@ GK_ATOM(onmapsendmessagereq, "onmapsendmessagereq") GK_ATOM(onmapmessageupdatereq, "onmapmessageupdatereq") GK_ATOM(onnewrdsgroup, "onnewrdsgroup") GK_ATOM(onnotificationclick, "onnotificationclick") +GK_ATOM(onnotificationclose, "onnotificationclose") GK_ATOM(onnoupdate, "onnoupdate") GK_ATOM(onobexpasswordreq, "onobexpasswordreq") GK_ATOM(onobsolete, "onobsolete") diff --git a/dom/interfaces/base/nsIServiceWorkerManager.idl b/dom/interfaces/base/nsIServiceWorkerManager.idl index c3a149918278..38e220f9e658 100644 --- a/dom/interfaces/base/nsIServiceWorkerManager.idl +++ b/dom/interfaces/base/nsIServiceWorkerManager.idl @@ -185,6 +185,19 @@ interface nsIServiceWorkerManager : nsISupports in AString aIcon, in AString aData, in AString aBehavior); + + void sendNotificationCloseEvent(in ACString aOriginSuffix, + in ACString scope, + in AString aID, + in AString aTitle, + in AString aDir, + in AString aLang, + in AString aBody, + in AString aTag, + in AString aIcon, + in AString aData, + in AString aBehavior); + [optional_argc] void sendPushEvent(in ACString aOriginAttributes, in ACString aScope, [optional] in uint32_t aDataLength, diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 26d8bca8b267..938810081c0a 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -1590,29 +1590,31 @@ ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject, { AssertIsOnMainThread(); + nsAutoCString originSuffix; + nsresult rv = mPrincipal->GetOriginSuffix(originSuffix); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr swm = + mozilla::services::GetServiceWorkerManager(); + if (NS_WARN_IF(!swm)) { + return NS_ERROR_FAILURE; + } + if (!strcmp("alertclickcallback", aTopic)) { - nsAutoCString originSuffix; - nsresult rv = mPrincipal->GetOriginSuffix(originSuffix); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr swm = - mozilla::services::GetServiceWorkerManager(); - - if (swm) { - swm->SendNotificationClickEvent(originSuffix, - NS_ConvertUTF16toUTF8(mScope), - mID, - mTitle, - mDir, - mLang, - mBody, - mTag, - mIcon, - mData, - mBehavior); - } + rv = swm->SendNotificationClickEvent(originSuffix, + NS_ConvertUTF16toUTF8(mScope), + mID, + mTitle, + mDir, + mLang, + mBody, + mTag, + mIcon, + mData, + mBehavior); + Unused << NS_WARN_IF(NS_FAILED(rv)); return NS_OK; } @@ -1629,6 +1631,20 @@ ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject, if (notificationStorage) { notificationStorage->Delete(origin, mID); } + + rv = swm->SendNotificationCloseEvent(originSuffix, + NS_ConvertUTF16toUTF8(mScope), + mID, + mTitle, + mDir, + mLang, + mBody, + mTag, + mIcon, + mData, + mBehavior); + Unused << NS_WARN_IF(NS_FAILED(rv)); + return NS_OK; } return NS_OK; diff --git a/dom/webidl/NotificationEvent.webidl b/dom/webidl/NotificationEvent.webidl index b5099e6a214b..3f25cf1b8265 100644 --- a/dom/webidl/NotificationEvent.webidl +++ b/dom/webidl/NotificationEvent.webidl @@ -23,4 +23,5 @@ dictionary NotificationEventInit : ExtendableEventInit { partial interface ServiceWorkerGlobalScope { attribute EventHandler onnotificationclick; + attribute EventHandler onnotificationclose; }; diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 52749d85e6b6..08529bb7c5ba 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -959,6 +959,37 @@ ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aOriginA #endif } +nsresult +ServiceWorkerManager::SendNotificationEvent(const nsAString& aEventName, + const nsACString& aOriginSuffix, + const nsACString& aScope, + const nsAString& aID, + const nsAString& aTitle, + const nsAString& aDir, + const nsAString& aLang, + const nsAString& aBody, + const nsAString& aTag, + const nsAString& aIcon, + const nsAString& aData, + const nsAString& aBehavior) +{ + PrincipalOriginAttributes attrs; + if (!attrs.PopulateFromSuffix(aOriginSuffix)) { + return NS_ERROR_INVALID_ARG; + } + + ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope); + if (!info) { + return NS_ERROR_FAILURE; + } + + ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate(); + return workerPrivate->SendNotificationEvent(aEventName, aID, aTitle, aDir, + aLang, aBody, aTag, + aIcon, aData, aBehavior, + NS_ConvertUTF8toUTF16(aScope)); +} + NS_IMETHODIMP ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix, const nsACString& aScope, @@ -972,21 +1003,27 @@ ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix const nsAString& aData, const nsAString& aBehavior) { - PrincipalOriginAttributes attrs; - if (!attrs.PopulateFromSuffix(aOriginSuffix)) { - return NS_ERROR_INVALID_ARG; - } + return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLICK_EVENT_NAME), + aOriginSuffix, aScope, aID, aTitle, aDir, aLang, + aBody, aTag, aIcon, aData, aBehavior); +} - ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope); - if (!info) { - return NS_ERROR_FAILURE; - } - - ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate(); - return workerPrivate->SendNotificationClickEvent(aID, aTitle, aDir, - aLang, aBody, aTag, - aIcon, aData, aBehavior, - NS_ConvertUTF8toUTF16(aScope)); +NS_IMETHODIMP +ServiceWorkerManager::SendNotificationCloseEvent(const nsACString& aOriginSuffix, + const nsACString& aScope, + const nsAString& aID, + const nsAString& aTitle, + const nsAString& aDir, + const nsAString& aLang, + const nsAString& aBody, + const nsAString& aTag, + const nsAString& aIcon, + const nsAString& aData, + const nsAString& aBehavior) +{ + return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLOSE_EVENT_NAME), + aOriginSuffix, aScope, aID, aTitle, aDir, aLang, + aBody, aTag, aIcon, aData, aBehavior); } NS_IMETHODIMP diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h index 75c71e4e7412..3797060a0949 100644 --- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -431,6 +431,20 @@ private: void MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope); + + nsresult + SendNotificationEvent(const nsAString& aEventName, + const nsACString& aOriginSuffix, + const nsACString& aScope, + const nsAString& aID, + const nsAString& aTitle, + const nsAString& aDir, + const nsAString& aLang, + const nsAString& aBody, + const nsAString& aTag, + const nsAString& aIcon, + const nsAString& aData, + const nsAString& aBehavior); }; } // namespace workers diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp index 277fae49cb33..a6e21d271deb 100644 --- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -1079,8 +1079,9 @@ ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPriv return true; } -class SendNotificationClickEventRunnable final : public ExtendableEventWorkerRunnable +class SendNotificationEventRunnable final : public ExtendableEventWorkerRunnable { + const nsString mEventName; const nsString mID; const nsString mTitle; const nsString mDir; @@ -1093,19 +1094,21 @@ class SendNotificationClickEventRunnable final : public ExtendableEventWorkerRun const nsString mScope; public: - SendNotificationClickEventRunnable(WorkerPrivate* aWorkerPrivate, - KeepAliveToken* aKeepAliveToken, - const nsAString& aID, - const nsAString& aTitle, - const nsAString& aDir, - const nsAString& aLang, - const nsAString& aBody, - const nsAString& aTag, - const nsAString& aIcon, - const nsAString& aData, - const nsAString& aBehavior, - const nsAString& aScope) + SendNotificationEventRunnable(WorkerPrivate* aWorkerPrivate, + KeepAliveToken* aKeepAliveToken, + const nsAString& aEventName, + const nsAString& aID, + const nsAString& aTitle, + const nsAString& aDir, + const nsAString& aLang, + const nsAString& aBody, + const nsAString& aTag, + const nsAString& aIcon, + const nsAString& aData, + const nsAString& aBehavior, + const nsAString& aScope) : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken) + , mEventName(aEventName) , mID(aID) , mTitle(aTitle) , mDir(aDir) @@ -1144,8 +1147,7 @@ public: nei.mCancelable = false; RefPtr event = - NotificationEvent::Constructor(target, - NS_LITERAL_STRING("notificationclick"), + NotificationEvent::Constructor(target, mEventName, nei, result); if (NS_WARN_IF(result.Failed())) { return false; @@ -1170,27 +1172,37 @@ public: } // namespace anonymous nsresult -ServiceWorkerPrivate::SendNotificationClickEvent(const nsAString& aID, - const nsAString& aTitle, - const nsAString& aDir, - const nsAString& aLang, - const nsAString& aBody, - const nsAString& aTag, - const nsAString& aIcon, - const nsAString& aData, - const nsAString& aBehavior, - const nsAString& aScope) +ServiceWorkerPrivate::SendNotificationEvent(const nsAString& aEventName, + const nsAString& aID, + const nsAString& aTitle, + const nsAString& aDir, + const nsAString& aLang, + const nsAString& aBody, + const nsAString& aTag, + const nsAString& aIcon, + const nsAString& aData, + const nsAString& aBehavior, + const nsAString& aScope) { - nsresult rv = SpawnWorkerIfNeeded(NotificationClickEvent, nullptr); + WakeUpReason why; + if (aEventName.EqualsLiteral(NOTIFICATION_CLICK_EVENT_NAME)) { + why = NotificationClickEvent; + gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay"); + } else if (aEventName.EqualsLiteral(NOTIFICATION_CLOSE_EVENT_NAME)) { + why = NotificationCloseEvent; + } else { + MOZ_ASSERT_UNREACHABLE("Invalid notification event name"); + return NS_ERROR_FAILURE; + } + + nsresult rv = SpawnWorkerIfNeeded(why, nullptr); NS_ENSURE_SUCCESS(rv, rv); - gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay"); - RefPtr r = - new SendNotificationClickEventRunnable(mWorkerPrivate, mKeepAliveToken, - aID, aTitle, aDir, aLang, - aBody, aTag, aIcon, aData, - aBehavior, aScope); + new SendNotificationEventRunnable(mWorkerPrivate, mKeepAliveToken, + aEventName, aID, aTitle, aDir, aLang, + aBody, aTag, aIcon, aData, aBehavior, + aScope); if (NS_WARN_IF(!r->Dispatch())) { return NS_ERROR_FAILURE; } diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h index d854dfa8c22e..74faa4509b9e 100644 --- a/dom/workers/ServiceWorkerPrivate.h +++ b/dom/workers/ServiceWorkerPrivate.h @@ -11,6 +11,9 @@ #include "WorkerPrivate.h" +#define NOTIFICATION_CLICK_EVENT_NAME "notificationclick" +#define NOTIFICATION_CLOSE_EVENT_NAME "notificationclose" + class nsIInterceptedChannel; namespace mozilla { @@ -93,16 +96,17 @@ public: SendPushSubscriptionChangeEvent(); nsresult - SendNotificationClickEvent(const nsAString& aID, - const nsAString& aTitle, - const nsAString& aDir, - const nsAString& aLang, - const nsAString& aBody, - const nsAString& aTag, - const nsAString& aIcon, - const nsAString& aData, - const nsAString& aBehavior, - const nsAString& aScope); + SendNotificationEvent(const nsAString& aEventName, + const nsAString& aID, + const nsAString& aTitle, + const nsAString& aDir, + const nsAString& aLang, + const nsAString& aBody, + const nsAString& aTag, + const nsAString& aIcon, + const nsAString& aData, + const nsAString& aBehavior, + const nsAString& aScope); nsresult SendFetchEvent(nsIInterceptedChannel* aChannel, @@ -149,6 +153,7 @@ private: PushSubscriptionChangeEvent, MessageEvent, NotificationClickEvent, + NotificationCloseEvent, LifeCycleEvent, AttachEvent }; diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index 74d62bd3de61..541e4d6f44ba 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -243,6 +243,7 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope) IMPL_EVENT_HANDLER(notificationclick) + IMPL_EVENT_HANDLER(notificationclose) ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope); diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini index a7eb33ebfc6e..aafaa57217dc 100644 --- a/dom/workers/test/serviceworkers/mochitest.ini +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -133,6 +133,8 @@ support-files = notificationclick.js notificationclick_focus.html notificationclick_focus.js + notificationclose.html + notificationclose.js worker_updatefoundevent.js worker_updatefoundevent2.js updatefoundevent.html @@ -249,6 +251,7 @@ tags = mcb [test_notificationclick.html] [test_notificationclick_focus.html] [test_notificationclick-otherwindow.html] +[test_notificationclose.html] [test_opaque_intercept.html] [test_openWindow.html] [test_origin_after_redirect.html] diff --git a/dom/workers/test/serviceworkers/notificationclose.html b/dom/workers/test/serviceworkers/notificationclose.html new file mode 100644 index 000000000000..10c8da453976 --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclose.html @@ -0,0 +1,37 @@ + + + + + Bug 1265841 - controlled page + + + + + + diff --git a/dom/workers/test/serviceworkers/notificationclose.js b/dom/workers/test/serviceworkers/notificationclose.js new file mode 100644 index 000000000000..d482180752e5 --- /dev/null +++ b/dom/workers/test/serviceworkers/notificationclose.js @@ -0,0 +1,19 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// +onnotificationclose = function(e) { + self.clients.matchAll().then(function(clients) { + if (clients.length === 0) { + dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n"); + return; + } + + clients.forEach(function(client) { + client.postMessage({ result: e.notification.data && + e.notification.data['complex'] && + e.notification.data['complex'][0] == "jsval" && + e.notification.data['complex'][1] == 5 }); + + }); + }); +} diff --git a/dom/workers/test/serviceworkers/test_notificationclose.html b/dom/workers/test/serviceworkers/test_notificationclose.html new file mode 100644 index 000000000000..3b81132c4b6e --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notificationclose.html @@ -0,0 +1,62 @@ + + + + + Bug 1265841 - Test ServiceWorkerGlobalScope.notificationclose event. + + + + + + +Bug 1265841 +

+ +
+
+ + + From d53c07485bb24a3bb6a621f8b287a92d14b06633 Mon Sep 17 00:00:00 2001 From: "Dragana Damjanovic dd.mozilla@gmail.com" Date: Tue, 31 May 2016 22:13:23 -0700 Subject: [PATCH 188/199] Bug 1271987 - After on-***-request observers and loadGroup::AddRequest are called, on a failure AsyncOpen() must return async.r=mcmanus, r=mayhemer MozReview-Commit-ID: AAi6R0pb6It --HG-- extra : histedit_source : 8c7f0f46745958c7d3aa7e8a5521bb5a38e6e10e --- netwerk/protocol/http/nsHttpChannel.cpp | 44 ++++++++++++++----------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index b2a98e2f859b..634186464a77 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -5313,6 +5313,12 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) AddCookiesToRequest(); + // After we notify any observers (on-opening-request, loadGroup, etc) we + // must return NS_OK and return any errors asynchronously via + // OnStart/OnStopRequest. Observers may add a reference to the channel + // and expect to get OnStopRequest so they know when to drop the reference, + // etc. + // notify "http-on-opening-request" observers, but not if this is a redirect if (!(mLoadFlags & LOAD_REPLACE)) { gHttpHandler->OnOpeningRequest(this); @@ -5327,8 +5333,6 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) mListener = listener; mListenerContext = context; - // add ourselves to the load group. from this point forward, we'll report - // all failures asynchronously. if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr); @@ -5342,16 +5346,20 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) // just once and early, AsyncOpen is the best place. mCustomAuthHeader = mRequestHead.HasHeader(nsHttp::Authorization); - // the only time we would already know the proxy information at this - // point would be if we were proxying a non-http protocol like ftp - if (!mProxyInfo && NS_SUCCEEDED(ResolveProxy())) + // The common case for HTTP channels is to begin proxy resolution and return + // at this point. The only time we know mProxyInfo already is if we're + // proxying a non-http protocol like ftp. + if (!mProxyInfo && NS_SUCCEEDED(ResolveProxy())) { return NS_OK; + } rv = BeginConnect(); - if (NS_FAILED(rv)) - ReleaseListeners(); + if (NS_FAILED(rv)) { + CloseCacheEntry(false); + AsyncAbort(rv); + } - return rv; + return NS_OK; } NS_IMETHODIMP @@ -5363,11 +5371,9 @@ nsHttpChannel::AsyncOpen2(nsIStreamListener *aListener) return AsyncOpen(listener, nullptr); } -// BeginConnect() will not call AsyncAbort() on an error and if AsyncAbort needs -// to be called the function calling BeginConnect will need to call AsyncAbort. -// If BeginConnect is called from AsyncOpen, AsyncnAbort doesn't need to be -// called. If it is called form another function (e.g. the function is called -// from OnProxyAvailable) that function should call AsyncOpen. +// BeginConnect() SHOULD NOT call AsyncAbort(). AsyncAbort will be called by +// functions that called BeginConnect if needed. Only AsyncOpen and +// OnProxyAvailable ever call BeginConnect. nsresult nsHttpChannel::BeginConnect() { @@ -5535,13 +5541,12 @@ nsHttpChannel::BeginConnect() nsCOMPtr pas = do_GetService("@mozilla.org/network/packaged-app-service;1", &rv); if (NS_WARN_IF(NS_FAILED(rv))) { - AsyncAbort(rv); return rv; } rv = pas->GetResource(this, this); if (NS_FAILED(rv)) { - AsyncAbort(rv); + return rv; } // We need to alter the flags so the cache entry returned by the @@ -5621,8 +5626,7 @@ nsHttpChannel::BeginConnect() } if (!(mLoadFlags & LOAD_CLASSIFY_URI)) { - ContinueBeginConnect(); - return NS_OK; + return ContinueBeginConnectWithResult(); } // mLocalBlocklist is true only if tracking protection is enabled and the @@ -5648,7 +5652,7 @@ nsHttpChannel::BeginConnect() channelClassifier.get(), this)); channelClassifier->Start(this); if (callContinueBeginConnect) { - ContinueBeginConnect(); + return ContinueBeginConnectWithResult(); } return NS_OK; } @@ -5744,7 +5748,7 @@ nsHttpChannel::ContinueBeginConnect() { nsresult rv = ContinueBeginConnectWithResult(); if (NS_FAILED(rv)) { - CloseCacheEntry(true); + CloseCacheEntry(false); AsyncAbort(rv); } } @@ -5805,8 +5809,8 @@ nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIChannel *channel, } if (NS_FAILED(rv)) { + CloseCacheEntry(false); AsyncAbort(rv); - Cancel(rv); } return rv; } From 525c0861874c3564fb65268884fe1022bc9cf308 Mon Sep 17 00:00:00 2001 From: Jonathan Hao Date: Tue, 24 May 2016 18:01:34 +0800 Subject: [PATCH 189/199] Bug 1259871 - Replace getSimpleCodebasePrincipal with createCodebasePrincipal. r=sicking MozReview-Commit-ID: Frx0CjBzuve --HG-- extra : histedit_source : 036eb321d9ccb20e0e071ba588b0a1249eb34bdd --- .../content/test/general/browser_bug413915.js | 2 +- browser/components/feeds/FeedWriter.js | 2 +- caps/nsIScriptSecurityManager.idl | 7 ------- caps/nsScriptSecurityManager.cpp | 10 ---------- dom/base/DOMParser.cpp | 9 +++------ dom/security/test/TestCSPParser.cpp | 4 +++- dom/security/test/csp/test_bug949549.html | 2 +- dom/security/test/unit/test_csp_reports.js | 2 +- dom/xslt/xslt/txMozillaStylesheetCompiler.cpp | 17 +++++++---------- ipc/glue/BackgroundUtils.cpp | 10 +++++----- layout/tools/reftest/reftest.jsm | 2 +- toolkit/components/places/nsLivemarkService.js | 4 ++-- toolkit/content/nsDragAndDrop.js | 2 +- 13 files changed, 26 insertions(+), 47 deletions(-) diff --git a/browser/base/content/test/general/browser_bug413915.js b/browser/base/content/test/general/browser_bug413915.js index fa3f7b04d53e..86c94c42767a 100644 --- a/browser/base/content/test/general/browser_bug413915.js +++ b/browser/base/content/test/general/browser_bug413915.js @@ -4,7 +4,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Feeds", function test() { var exampleUri = makeURI("http://example.com/"); var secman = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager); - var principal = secman.getSimpleCodebasePrincipal(exampleUri); + var principal = secman.createCodebasePrincipal(exampleUri, {}); function testIsFeed(aTitle, aHref, aType, aKnown) { var link = { title: aTitle, href: aHref, type: aType }; diff --git a/browser/components/feeds/FeedWriter.js b/browser/components/feeds/FeedWriter.js index 60c475312572..0c99f58d7fab 100644 --- a/browser/components/feeds/FeedWriter.js +++ b/browser/components/feeds/FeedWriter.js @@ -924,7 +924,7 @@ FeedWriter.prototype = { let secman = Cc["@mozilla.org/scriptsecuritymanager;1"]. getService(Ci.nsIScriptSecurityManager); - this._feedPrincipal = secman.getSimpleCodebasePrincipal(this._feedURI); + this._feedPrincipal = secman.createCodebasePrincipal(this._feedURI, {}); LOG("Subscribe Preview: feed uri = " + this._window.location.href); diff --git a/caps/nsIScriptSecurityManager.idl b/caps/nsIScriptSecurityManager.idl index 624d7ab639e6..16cc9b787a15 100644 --- a/caps/nsIScriptSecurityManager.idl +++ b/caps/nsIScriptSecurityManager.idl @@ -133,13 +133,6 @@ interface nsIScriptSecurityManager : nsISupports */ nsIPrincipal getSystemPrincipal(); - /** - * Return a principal that has the same origin as aURI. - * This principals should not be used for any data/permission check, it will - * have appId = UNKNOWN_APP_ID. - */ - nsIPrincipal getSimpleCodebasePrincipal(in nsIURI aURI); - /** * Returns a principal that has the given information. * @param appId is the app id of the principal. It can't be UNKNOWN_APP_ID. diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index 8d2b5e3f2634..2a5c6b6ab80e 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -1108,16 +1108,6 @@ nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result) return NS_OK; } -NS_IMETHODIMP -nsScriptSecurityManager::GetSimpleCodebasePrincipal(nsIURI* aURI, - nsIPrincipal** aPrincipal) -{ - PrincipalOriginAttributes attrs(UNKNOWN_APP_ID, false); - nsCOMPtr prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs); - prin.forget(aPrincipal); - return *aPrincipal ? NS_OK : NS_ERROR_FAILURE; -} - NS_IMETHODIMP nsScriptSecurityManager::GetNoAppCodebasePrincipal(nsIURI* aURI, nsIPrincipal** aPrincipal) diff --git a/dom/base/DOMParser.cpp b/dom/base/DOMParser.cpp index c33b267dbc67..b27cfd7c810d 100644 --- a/dom/base/DOMParser.cpp +++ b/dom/base/DOMParser.cpp @@ -339,12 +339,9 @@ DOMParser::Init(nsIPrincipal* principal, nsIURI* documentURI, mPrincipal = principal; nsresult rv; if (!mPrincipal) { - nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); - NS_ENSURE_TRUE(secMan, NS_ERROR_NOT_AVAILABLE); - rv = - secMan->GetSimpleCodebasePrincipal(mDocumentURI, - getter_AddRefs(mPrincipal)); - NS_ENSURE_SUCCESS(rv, rv); + PrincipalOriginAttributes attrs; + mPrincipal = BasePrincipal::CreateCodebasePrincipal(mDocumentURI, attrs); + NS_ENSURE_TRUE(mPrincipal, NS_ERROR_FAILURE); mOriginalPrincipal = mPrincipal; } else { mOriginalPrincipal = mPrincipal; diff --git a/dom/security/test/TestCSPParser.cpp b/dom/security/test/TestCSPParser.cpp index a19861eb7b25..df833be82dfd 100644 --- a/dom/security/test/TestCSPParser.cpp +++ b/dom/security/test/TestCSPParser.cpp @@ -101,7 +101,9 @@ nsresult runTest(uint32_t aExpectedPolicyCount, // this should be 0 for policies NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr selfURIPrincipal; - rv = secman->GetSimpleCodebasePrincipal(selfURI, getter_AddRefs(selfURIPrincipal)); + // Can't use BasePrincipal::CreateCodebasePrincipal here + // because the symbol is not visible here + rv = secman->GetCodebasePrincipal(selfURI, getter_AddRefs(selfURIPrincipal)); NS_ENSURE_SUCCESS(rv, rv); // create a CSP object diff --git a/dom/security/test/csp/test_bug949549.html b/dom/security/test/csp/test_bug949549.html index 3d6528abbd70..858653c13c02 100644 --- a/dom/security/test/csp/test_bug949549.html +++ b/dom/security/test/csp/test_bug949549.html @@ -41,7 +41,7 @@ var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] .getService(SpecialPowers.Ci.nsIScriptSecurityManager); var manifestURI = SpecialPowers.Services.io.newURI(gManifestURL, null, null); - principal = secMan.getSimpleCodebasePrincipal(manifestURI); + principal = secMan.createCodebasePrincipal(manifestURI, {}); csp.setRequestContext(null, principal); ok(true, "setRequestContext hasn't thown"); } catch(e) { diff --git a/dom/security/test/unit/test_csp_reports.js b/dom/security/test/unit/test_csp_reports.js index 78be3d8640ec..5c832d203982 100644 --- a/dom/security/test/unit/test_csp_reports.js +++ b/dom/security/test/unit/test_csp_reports.js @@ -87,7 +87,7 @@ function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) { let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"] .getService(Ci.nsIScriptSecurityManager); - principal = ssm.getSimpleCodebasePrincipal(selfuri); + principal = ssm.createCodebasePrincipal(selfuri, {}); csp.setRequestContext(null, principal); // Load up the policy diff --git a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp index e00b354a4a44..daddf3036a1c 100644 --- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp +++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp @@ -417,11 +417,10 @@ txCompileObserver::loadURI(const nsAString& aUri, rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr referrerPrincipal; - rv = nsContentUtils::GetSecurityManager()-> - GetSimpleCodebasePrincipal(referrerUri, - getter_AddRefs(referrerPrincipal)); - NS_ENSURE_SUCCESS(rv, rv); + PrincipalOriginAttributes attrs; + nsCOMPtr referrerPrincipal = + BasePrincipal::CreateCodebasePrincipal(referrerUri, attrs); + NS_ENSURE_TRUE(referrerPrincipal, NS_ERROR_FAILURE); return startLoad(uri, aCompiler, referrerPrincipal, aReferrerPolicy); } @@ -622,11 +621,9 @@ txSyncCompileObserver::loadURI(const nsAString& aUri, rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr referrerPrincipal; - rv = nsContentUtils::GetSecurityManager()-> - GetSimpleCodebasePrincipal(referrerUri, - getter_AddRefs(referrerPrincipal)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr referrerPrincipal = + BasePrincipal::CreateCodebasePrincipal(referrerUri, PrincipalOriginAttributes()); + NS_ENSURE_TRUE(referrerPrincipal, NS_ERROR_FAILURE); // This is probably called by js, a loadGroup for the channel doesn't // make sense. diff --git a/ipc/glue/BackgroundUtils.cpp b/ipc/glue/BackgroundUtils.cpp index 9bb74959db08..0b690a5fb9ce 100644 --- a/ipc/glue/BackgroundUtils.cpp +++ b/ipc/glue/BackgroundUtils.cpp @@ -77,12 +77,12 @@ PrincipalInfoToPrincipal(const PrincipalInfo& aPrincipalInfo, return nullptr; } - if (info.attrs().mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) { - rv = secMan->GetSimpleCodebasePrincipal(uri, getter_AddRefs(principal)); - } else { - principal = BasePrincipal::CreateCodebasePrincipal(uri, info.attrs()); - rv = principal ? NS_OK : NS_ERROR_FAILURE; + PrincipalOriginAttributes attrs; + if (info.attrs().mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) { + attrs = info.attrs(); } + principal = BasePrincipal::CreateCodebasePrincipal(uri, attrs); + rv = principal ? NS_OK : NS_ERROR_FAILURE; if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } diff --git a/layout/tools/reftest/reftest.jsm b/layout/tools/reftest/reftest.jsm index c472ed6c25ab..398feb805e25 100644 --- a/layout/tools/reftest/reftest.jsm +++ b/layout/tools/reftest/reftest.jsm @@ -1030,7 +1030,7 @@ function ReadManifest(aURL, inherited_status, aFilter) } } - var principal = secMan.getSimpleCodebasePrincipal(aURL); + var principal = secMan.createCodebasePrincipal(aURL, {}); if (items[0] == "include") { if (items.length != 2) diff --git a/toolkit/components/places/nsLivemarkService.js b/toolkit/components/places/nsLivemarkService.js index 918bad614645..66ff1c0dff86 100644 --- a/toolkit/components/places/nsLivemarkService.js +++ b/toolkit/components/places/nsLivemarkService.js @@ -482,7 +482,7 @@ Livemark.prototype = { // Security check the site URI against the feed URI principal. let secMan = Services.scriptSecurityManager; - let feedPrincipal = secMan.getSimpleCodebasePrincipal(this.feedURI); + let feedPrincipal = secMan.createCodebasePrincipal(this.feedURI, {}); try { secMan.checkLoadURIWithPrincipal(feedPrincipal, aSiteURI, Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL); @@ -759,7 +759,7 @@ LivemarkLoadListener.prototype = { // We need this to make sure the item links are safe let feedPrincipal = Services.scriptSecurityManager - .getSimpleCodebasePrincipal(this._livemark.feedURI); + .createCodebasePrincipal(this._livemark.feedURI, {}); // Enforce well-formedness because the existing code does if (!aResult || !aResult.doc || aResult.bozo) { diff --git a/toolkit/content/nsDragAndDrop.js b/toolkit/content/nsDragAndDrop.js index d76a5a8a87ba..c1a42dfbc9de 100644 --- a/toolkit/content/nsDragAndDrop.js +++ b/toolkit/content/nsDragAndDrop.js @@ -588,7 +588,7 @@ var nsDragAndDrop = { // Use "file:///" as the default sourceURI so that drops of file:// URIs // are always allowed. var principal = sourceDoc ? sourceDoc.nodePrincipal - : secMan.getSimpleCodebasePrincipal(ioService.newURI("file:///", null, null)); + : secMan.createCodebasePrincipal(ioService.newURI("file:///", null, null), {}); try { secMan.checkLoadURIStrWithPrincipal(principal, aDraggedText, From 601acdd08744e44b4b453a8fb2cd42ad1fdbf820 Mon Sep 17 00:00:00 2001 From: Honza Bambas Date: Tue, 31 May 2016 22:20:17 -0700 Subject: [PATCH 190/199] Bug 1275898 - Proper about:cache asyncOpen implementation + kill the disk entries loop hard on shutdown, r=michal MozReview-Commit-ID: 3Tmvy45Iayu --HG-- extra : histedit_source : 736f2ff12fb097bf2776cc900750010a00c4cf2a --- netwerk/cache2/CacheStorageService.cpp | 2 +- netwerk/protocol/about/nsAboutCache.cpp | 44 ++++++++++++++++++++++++- netwerk/protocol/about/nsAboutCache.h | 31 +++++++++++++++-- 3 files changed, 73 insertions(+), 4 deletions(-) diff --git a/netwerk/cache2/CacheStorageService.cpp b/netwerk/cache2/CacheStorageService.cpp index 940c1b0e425b..fe9e731feb2b 100644 --- a/netwerk/cache2/CacheStorageService.cpp +++ b/netwerk/cache2/CacheStorageService.cpp @@ -455,7 +455,7 @@ private: } } - while (!mCancel) { + while (!mCancel && !CacheObserver::ShuttingDown()) { if (CacheIOThread::YieldAndRerun()) return NS_OK; diff --git a/netwerk/protocol/about/nsAboutCache.cpp b/netwerk/protocol/about/nsAboutCache.cpp index 30a126423fd1..8971c7323322 100644 --- a/netwerk/protocol/about/nsAboutCache.cpp +++ b/netwerk/protocol/about/nsAboutCache.cpp @@ -131,13 +131,55 @@ nsAboutCache::Channel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo) FlushBuffer(); - // Kick it, this goes async. + return NS_OK; +} + +NS_IMETHODIMP nsAboutCache::Channel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext) +{ + nsresult rv; + + if (!mChannel) { + return NS_ERROR_UNEXPECTED; + } + + // Kick the walk loop. rv = VisitNextStorage(); if (NS_FAILED(rv)) return rv; + rv = mChannel->AsyncOpen(aListener, aContext); + if (NS_FAILED(rv)) return rv; + return NS_OK; } +NS_IMETHODIMP nsAboutCache::Channel::AsyncOpen2(nsIStreamListener *aListener) +{ + return AsyncOpen(aListener, nullptr); +} + +NS_IMETHODIMP nsAboutCache::Channel::Open(nsIInputStream * *_retval) +{ + nsresult rv; + + if (!mChannel) { + return NS_ERROR_UNEXPECTED; + } + + // Kick the walk loop. + rv = VisitNextStorage(); + if (NS_FAILED(rv)) return rv; + + rv = mChannel->Open(_retval); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + +NS_IMETHODIMP nsAboutCache::Channel::Open2(nsIInputStream * *_retval) +{ + return Open(_retval); +} + nsresult nsAboutCache::Channel::ParseURI(nsIURI * uri, nsACString & storage) { diff --git a/netwerk/protocol/about/nsAboutCache.h b/netwerk/protocol/about/nsAboutCache.h index ed2fcff51fd7..1f404f357aaf 100644 --- a/netwerk/protocol/about/nsAboutCache.h +++ b/netwerk/protocol/about/nsAboutCache.h @@ -17,6 +17,29 @@ #include "nsCOMPtr.h" #include "nsTArray.h" +#define NS_FORWARD_SAFE_NSICHANNEL_SUBSET(_to) \ + NS_IMETHOD GetOriginalURI(nsIURI * *aOriginalURI) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetOriginalURI(aOriginalURI); } \ + NS_IMETHOD SetOriginalURI(nsIURI *aOriginalURI) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetOriginalURI(aOriginalURI); } \ + NS_IMETHOD GetURI(nsIURI * *aURI) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetURI(aURI); } \ + NS_IMETHOD GetOwner(nsISupports * *aOwner) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetOwner(aOwner); } \ + NS_IMETHOD SetOwner(nsISupports *aOwner) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetOwner(aOwner); } \ + NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor * *aNotificationCallbacks) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetNotificationCallbacks(aNotificationCallbacks); } \ + NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aNotificationCallbacks) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetNotificationCallbacks(aNotificationCallbacks); } \ + NS_IMETHOD GetSecurityInfo(nsISupports * *aSecurityInfo) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetSecurityInfo(aSecurityInfo); } \ + NS_IMETHOD GetContentType(nsACString & aContentType) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentType(aContentType); } \ + NS_IMETHOD SetContentType(const nsACString & aContentType) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentType(aContentType); } \ + NS_IMETHOD GetContentCharset(nsACString & aContentCharset) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentCharset(aContentCharset); } \ + NS_IMETHOD SetContentCharset(const nsACString & aContentCharset) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentCharset(aContentCharset); } \ + NS_IMETHOD GetContentLength(int64_t *aContentLength) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentLength(aContentLength); } \ + NS_IMETHOD SetContentLength(int64_t aContentLength) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentLength(aContentLength); } \ + NS_IMETHOD GetContentDisposition(uint32_t *aContentDisposition) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentDisposition(aContentDisposition); } \ + NS_IMETHOD SetContentDisposition(uint32_t aContentDisposition) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentDisposition(aContentDisposition); } \ + NS_IMETHOD GetContentDispositionFilename(nsAString & aContentDispositionFilename) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentDispositionFilename(aContentDispositionFilename); } \ + NS_IMETHOD SetContentDispositionFilename(const nsAString & aContentDispositionFilename) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetContentDispositionFilename(aContentDispositionFilename); } \ + NS_IMETHOD GetContentDispositionHeader(nsACString & aContentDispositionHeader) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetContentDispositionHeader(aContentDispositionHeader); } \ + NS_IMETHOD GetLoadInfo(nsILoadInfo * *aLoadInfo) override { return !_to ? NS_ERROR_NULL_POINTER : _to->GetLoadInfo(aLoadInfo); } \ + NS_IMETHOD SetLoadInfo(nsILoadInfo *aLoadInfo) override { return !_to ? NS_ERROR_NULL_POINTER : _to->SetLoadInfo(aLoadInfo); } \ + class nsAboutCache final : public nsIAboutModule { public: @@ -40,8 +63,12 @@ protected: { NS_DECL_ISUPPORTS NS_DECL_NSICACHESTORAGEVISITOR - NS_FORWARD_SAFE_NSICHANNEL(mChannel) NS_FORWARD_SAFE_NSIREQUEST(mChannel) + NS_FORWARD_SAFE_NSICHANNEL_SUBSET(mChannel) + NS_IMETHOD AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext) override; + NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override; + NS_IMETHOD Open(nsIInputStream * *_retval) override; + NS_IMETHOD Open2(nsIInputStream * *_retval) override; private: virtual ~Channel() {} @@ -55,7 +82,7 @@ protected: // one in the list then.) Posts FireVisitStorage() when found. nsresult VisitNextStorage(); // Helper method that calls VisitStorage() for the current storage. - // When it fails, OnCacheEntryVisitCompleted is simlated to close + // When it fails, OnCacheEntryVisitCompleted is simulated to close // the output stream and thus the about:cache channel. void FireVisitStorage(); // Kiks the visit cycle for the given storage, names can be: From 8ca38235c05db3190da25a8bdd37473ffcc6e566 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Wed, 11 May 2016 10:58:06 +0200 Subject: [PATCH 191/199] Bug 1267754 - Replace WARNING about missing triggeringPrincipal with LOG() r=mcmanus MozReview-Commit-ID: ESHG6geGqAD --HG-- extra : rebase_source : 0c2ead7341d796f9d2bb129752f2093038ea7972 --- netwerk/protocol/http/HttpBaseChannel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 31ddcf3e3912..df21c89bd689 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -1408,7 +1408,7 @@ HttpBaseChannel::SetReferrerWithPolicy(nsIURI *referrer, rv = ssm->CheckSameOriginURI(triggeringURI, mURI, false); isCrossOrigin = NS_FAILED(rv); } else { - NS_WARNING("no triggering principal available via loadInfo, assuming load is cross-origin"); + LOG(("no triggering principal available via loadInfo, assuming load is cross-origin")); } nsCOMPtr clone; From 8ddce9d86b9e8b62025f3bbb85900b78987c905f Mon Sep 17 00:00:00 2001 From: Eddy Bruel Date: Wed, 1 Jun 2016 08:36:08 +0200 Subject: [PATCH 192/199] Bug 1271653 - Use mozilla::range for eval code instead of HandleValue;r=jimb --- js/src/vm/Debugger.cpp | 74 +++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 642e4e363bfd..728645de443a 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -7552,10 +7552,27 @@ EvaluateInEnv(JSContext* cx, Handle env, AbstractFramePtr frame, return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address()); } +static bool +ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, + AutoStableStringChars& stableChars) +{ + if (!value.isString()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, + fnname, "string", "string"); + return false; + } + RootedLinearString linear(cx, value.toString()->ensureLinear(cx)); + if (!linear) + return false; + if (!stableChars.initTwoByte(cx, linear)) + return false; + return true; +} + enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false }; static bool -DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code, +DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, EvalBindings evalWithBindings, HandleValue bindings, const EvalOptions& options, MutableHandleValue vp, Debugger* dbg, HandleObject scope, ScriptFrameIter* iter) @@ -7564,16 +7581,6 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code MOZ_ASSERT_IF(iter, !scope); MOZ_ASSERT_IF(!iter, scope && IsGlobalLexicalScope(scope)); - /* Check the first argument, the eval code string. */ - if (!code.isString()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, - fullMethodName, "string", InformalValueTypeName(code)); - return false; - } - RootedLinearString linear(cx, code.toString()->ensureLinear(cx)); - if (!linear) - return false; - /* * Gather keys and values of bindings, if any. This must be done in the * debugger compartment, since that is where any exceptions must be @@ -7646,11 +7653,7 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code RootedValue rval(cx); AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr(); jsbytecode* pc = iter ? iter->pc() : nullptr; - AutoStableStringChars stableChars(cx); - if (!stableChars.initTwoByte(cx, linear)) - return false; - mozilla::Range chars = stableChars.twoByteRange(); bool ok = EvaluateInEnv(cx, env, frame, pc, chars, options.filename() ? options.filename() : "debugger eval code", options.lineno(), &rval); @@ -7666,12 +7669,16 @@ DebuggerFrame_eval(JSContext* cx, unsigned argc, Value* vp) Debugger* dbg = Debugger::fromChildJSObject(thisobj); UpdateFrameIterPc(iter); + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Frame.prototype.eval", args[0], stableChars)) + return false; + mozilla::Range chars = stableChars.twoByteRange(); + EvalOptions options; if (!ParseEvalOptions(cx, args.get(1), options)) return false; - return DebuggerGenericEval(cx, "Debugger.Frame.prototype.eval", - args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue, + return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, options, args.rval(), dbg, nullptr, &iter); } @@ -7684,12 +7691,19 @@ DebuggerFrame_evalWithBindings(JSContext* cx, unsigned argc, Value* vp) Debugger* dbg = Debugger::fromChildJSObject(thisobj); UpdateFrameIterPc(iter); + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Frame.prototype.evalWithBindings", args[0], + stableChars)) + { + return false; + } + mozilla::Range chars = stableChars.twoByteRange(); + EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; - return DebuggerGenericEval(cx, "Debugger.Frame.prototype.evalWithBindings", - args[0], EvalHasExtraBindings, args[1], options, + return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, args.rval(), dbg, nullptr, &iter); } @@ -8700,13 +8714,20 @@ DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) if (!RequireGlobalObject(cx, args.thisv(), referent)) return false; + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobal", args[0], + stableChars)) + { + return false; + } + mozilla::Range chars = stableChars.twoByteRange(); + EvalOptions options; if (!ParseEvalOptions(cx, args.get(1), options)) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, "Debugger.Object.prototype.executeInGlobal", - args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue, + return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, options, args.rval(), dbg, globalLexical, nullptr); } @@ -8720,13 +8741,20 @@ DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* if (!RequireGlobalObject(cx, args.thisv(), referent)) return false; + AutoStableStringChars stableChars(cx); + if (!ValueToStableChars(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", args[0], + stableChars)) + { + return false; + } + mozilla::Range chars = stableChars.twoByteRange(); + EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, "Debugger.Object.prototype.executeInGlobalWithBindings", - args[0], EvalHasExtraBindings, args[1], options, + return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, args.rval(), dbg, globalLexical, nullptr); } From ceec41fb99a39ece8cba3d8ea0d6b930da3746fa Mon Sep 17 00:00:00 2001 From: Eddy Bruel Date: Wed, 1 Jun 2016 08:37:16 +0200 Subject: [PATCH 193/199] Bug 1271653 - Use HandleObject for eval bindings instead of HandleValue;r=jimb --- js/src/vm/Debugger.cpp | 73 +++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 728645de443a..6ed3fa80a894 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -406,6 +406,23 @@ NukeDebuggerWrapper(NativeObject *wrapper) wrapper->setPrivate(nullptr); } +static bool +ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, + AutoStableStringChars& stableChars) +{ + if (!value.isString()) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, + fnname, "string", "string"); + return false; + } + RootedLinearString linear(cx, value.toString()->ensureLinear(cx)); + if (!linear) + return false; + if (!stableChars.initTwoByte(cx, linear)) + return false; + return true; +} + class MOZ_RAII EvalOptions { const char* filename_; unsigned lineno_; @@ -7552,30 +7569,10 @@ EvaluateInEnv(JSContext* cx, Handle env, AbstractFramePtr frame, return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address()); } -static bool -ValueToStableChars(JSContext* cx, const char *fnname, HandleValue value, - AutoStableStringChars& stableChars) -{ - if (!value.isString()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, - fnname, "string", "string"); - return false; - } - RootedLinearString linear(cx, value.toString()->ensureLinear(cx)); - if (!linear) - return false; - if (!stableChars.initTwoByte(cx, linear)) - return false; - return true; -} - -enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false }; - static bool DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, - EvalBindings evalWithBindings, HandleValue bindings, - const EvalOptions& options, MutableHandleValue vp, Debugger* dbg, - HandleObject scope, ScriptFrameIter* iter) + HandleObject bindings, const EvalOptions& options, MutableHandleValue vp, + Debugger* dbg, HandleObject scope, ScriptFrameIter* iter) { /* Either we're specifying the frame, or a global. */ MOZ_ASSERT_IF(iter, !scope); @@ -7588,17 +7585,15 @@ DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, */ AutoIdVector keys(cx); AutoValueVector values(cx); - if (evalWithBindings) { - RootedObject bindingsobj(cx, NonNullObject(cx, bindings)); - if (!bindingsobj || - !GetPropertyKeys(cx, bindingsobj, JSITER_OWNONLY, &keys) || + if (bindings) { + if (!GetPropertyKeys(cx, bindings, JSITER_OWNONLY, &keys) || !values.growBy(keys.length())) { return false; } for (size_t i = 0; i < keys.length(); i++) { MutableHandleValue valp = values[i]; - if (!GetProperty(cx, bindingsobj, bindingsobj, keys[i], valp) || + if (!GetProperty(cx, bindings, bindings, keys[i], valp) || !dbg->unwrapDebuggeeValue(cx, valp)) { return false; @@ -7622,7 +7617,7 @@ DebuggerGenericEval(JSContext* cx, const mozilla::Range chars, } /* If evalWithBindings, create the inner environment. */ - if (evalWithBindings) { + if (bindings) { RootedPlainObject nenv(cx, NewObjectWithGivenProto(cx, nullptr)); if (!nenv) return false; @@ -7678,8 +7673,7 @@ DebuggerFrame_eval(JSContext* cx, unsigned argc, Value* vp) if (!ParseEvalOptions(cx, args.get(1), options)) return false; - return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, - options, args.rval(), dbg, nullptr, &iter); + return DebuggerGenericEval(cx, chars, nullptr, options, args.rval(), dbg, nullptr, &iter); } static bool @@ -7699,12 +7693,15 @@ DebuggerFrame_evalWithBindings(JSContext* cx, unsigned argc, Value* vp) } mozilla::Range chars = stableChars.twoByteRange(); + RootedObject bindings(cx, NonNullObject(cx, args[1])); + if (!bindings) + return false; + EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; - return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, - args.rval(), dbg, nullptr, &iter); + return DebuggerGenericEval(cx, chars, bindings, options, args.rval(), dbg, nullptr, &iter); } static bool @@ -8727,8 +8724,8 @@ DebuggerObject_executeInGlobal(JSContext* cx, unsigned argc, Value* vp) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, chars, EvalWithDefaultBindings, JS::UndefinedHandleValue, - options, args.rval(), dbg, globalLexical, nullptr); + return DebuggerGenericEval(cx, chars, nullptr, options, args.rval(), dbg, globalLexical, + nullptr); } static bool @@ -8749,13 +8746,17 @@ DebuggerObject_executeInGlobalWithBindings(JSContext* cx, unsigned argc, Value* } mozilla::Range chars = stableChars.twoByteRange(); + RootedObject bindings(cx, NonNullObject(cx, args[1])); + if (!bindings) + return false; + EvalOptions options; if (!ParseEvalOptions(cx, args.get(2), options)) return false; RootedObject globalLexical(cx, &referent->as().lexicalScope()); - return DebuggerGenericEval(cx, chars, EvalHasExtraBindings, args[1], options, - args.rval(), dbg, globalLexical, nullptr); + return DebuggerGenericEval(cx, chars, bindings, options, args.rval(), dbg, globalLexical, + nullptr); } static bool From 85f95c9e861782e4272a4a7f58e7acfe17bcba35 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Wed, 1 Jun 2016 08:46:47 +0200 Subject: [PATCH 194/199] Backed out changeset d5832177887d (bug 1258286) --- .../submitted/masking/mask-repeat-1-ref.html | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html b/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html index fe36924f932b..fa386cdac1ef 100644 --- a/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html +++ b/layout/reftests/w3c-css/submitted/masking/mask-repeat-1-ref.html @@ -13,39 +13,33 @@ border: 1px solid black; } - .color { + div.inner { + width: 50px; + height: 50px; + position: absolute; background-color: purple; } - #default { - position: absolute; - width: 50px; height: 50px; - } - - #repeat-x { - position: absolute; - width: 100%; height: 50px; - } - - #repeat-y { - position: absolute; - width: 50px; height: 100%; - } + #pos-top-left { left: 0; top: 0; } + #pos-top-right { right: 0; top: 0; } + #pos-bottom-left { left: 0; bottom: 0; } + #pos-bottom-right { right: 0; bottom: 0; }
-
+
+
+
+
-
-
-
-
-
+
+
-
+
+
From a415d8009612a85c3b3e25b59580383f5f87278c Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Wed, 1 Jun 2016 08:47:06 +0200 Subject: [PATCH 195/199] Backed out changeset a9db7e5634d7 (bug 1258286) for test_smilCSSFromTo.xhtml test failures --- layout/style/nsComputedDOMStyle.cpp | 4 +- layout/style/nsRuleNode.cpp | 6 +- layout/style/nsStyleStruct.cpp | 228 +++++++++---------------- layout/style/nsStyleStruct.h | 32 ++-- layout/style/test/property_database.js | 4 +- 5 files changed, 104 insertions(+), 170 deletions(-) diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 1df47dee6cad..cec13022c7e1 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -6104,10 +6104,10 @@ nsComputedDOMStyle::DoGetMask() firstLayer.mComposite != NS_STYLE_MASK_COMPOSITE_ADD || firstLayer.mMaskMode != NS_STYLE_MASK_MODE_MATCH_SOURCE || !firstLayer.mPosition.IsInitialValue() || - !firstLayer.mRepeat.IsInitialValue(nsStyleImageLayers::LayerType::Mask) || + !firstLayer.mRepeat.IsInitialValue() || !firstLayer.mSize.IsInitialValue() || !(firstLayer.mImage.GetType() == eStyleImageType_Null || - firstLayer.mImage.GetType() == eStyleImageType_Image)) { + firstLayer.mImage.GetType() == eStyleImageType_Image)){ return nullptr; } diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 09a89dfd7315..ffd5bb4099c9 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -7088,7 +7088,7 @@ nsRuleNode::ComputeBackgroundData(void* aStartStruct, // background-repeat: enum, inherit, initial [pair list] nsStyleImageLayers::Repeat initialRepeat; - initialRepeat.SetInitialValues(nsStyleImageLayers::LayerType::Background); + initialRepeat.SetInitialValues(); SetImageLayerPairList(aContext, *aRuleData->ValueForBackgroundRepeat(), bg->mImage.mLayers, parentBG->mImage.mLayers, @@ -9897,7 +9897,7 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, // mask-repeat: enum, inherit, initial [pair list] nsStyleImageLayers::Repeat initialRepeat; - initialRepeat.SetInitialValues(nsStyleImageLayers::LayerType::Mask); + initialRepeat.SetInitialValues(); SetImageLayerPairList(aContext, *aRuleData->ValueForMaskRepeat(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, @@ -9921,7 +9921,7 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &nsStyleImageLayers::Layer::mOrigin, - uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_BORDER), + uint8_t(NS_STYLE_IMAGELAYER_ORIGIN_PADDING), parentSVGReset->mMask.mOriginCount, svgReset->mMask.mOriginCount, maxItemCount, rebuild, conditions); diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 16c5e37bd46a..a27e4b7294e3 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -47,8 +47,7 @@ static_assert((((1 << nsStyleStructID_Length) - 1) & const int32_t nsStyleGridLine::kMinLine = -10000; const int32_t nsStyleGridLine::kMaxLine = 10000; -static bool -EqualURIs(nsIURI *aURI1, nsIURI *aURI2) +static bool EqualURIs(nsIURI *aURI1, nsIURI *aURI2) { bool eq; return aURI1 == aURI2 || // handle null==null, and optimize @@ -57,15 +56,13 @@ EqualURIs(nsIURI *aURI1, nsIURI *aURI2) eq); } -static bool -EqualURIs(mozilla::css::URLValue *aURI1, mozilla::css::URLValue *aURI2) +static bool EqualURIs(mozilla::css::URLValue *aURI1, mozilla::css::URLValue *aURI2) { return aURI1 == aURI2 || // handle null==null, and optimize (aURI1 && aURI2 && aURI1->URIEquals(*aURI2)); } -static bool -EqualImages(imgIRequest *aImage1, imgIRequest* aImage2) +static bool EqualImages(imgIRequest *aImage1, imgIRequest* aImage2) { if (aImage1 == aImage2) { return true; @@ -82,8 +79,7 @@ EqualImages(imgIRequest *aImage1, imgIRequest* aImage2) } // A nullsafe wrapper for strcmp. We depend on null-safety. -static int -safe_strcmp(const char16_t* a, const char16_t* b) +static int safe_strcmp(const char16_t* a, const char16_t* b) { if (!a || !b) { return (int)(a - b); @@ -107,7 +103,8 @@ StyleStructContext::HackilyFindSomeDeviceContext() return nsLayoutUtils::GetDeviceContextForScreenInfo(static_cast(win.get())); } -static bool AreShadowArraysEqual(nsCSSShadowArray* lhs, nsCSSShadowArray* rhs); +static bool AreShadowArraysEqual(nsCSSShadowArray* lhs, + nsCSSShadowArray* rhs); // -------------------- // nsStyleFont @@ -183,8 +180,7 @@ nsStyleFont::EnableZoom(nsPresContext* aContext, bool aEnable) } } -nsChangeHint -nsStyleFont::CalcDifference(const nsStyleFont& aOther) const +nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aOther) const { MOZ_ASSERT(mAllowZoom == aOther.mAllowZoom, "expected mAllowZoom to be the same on both nsStyleFonts"); @@ -241,8 +237,9 @@ nsStyleFont::GetLanguage(StyleStructContext aContext) return language.forget(); } -static nscoord -CalcCoord(const nsStyleCoord& aCoord, const nscoord* aEnumTable, int32_t aNumEnums) +static nscoord CalcCoord(const nsStyleCoord& aCoord, + const nscoord* aEnumTable, + int32_t aNumEnums) { if (aCoord.GetUnit() == eStyleUnit_Enumerated) { MOZ_ASSERT(aEnumTable, "must have enum table"); @@ -279,8 +276,7 @@ nsStyleMargin::Destroy(nsPresContext* aContext) { FreeByObjectID(eArenaObjectID_nsStyleMargin, this); } -nsChangeHint -nsStyleMargin::CalcDifference(const nsStyleMargin& aOther) const +nsChangeHint nsStyleMargin::CalcDifference(const nsStyleMargin& aOther) const { if (mMargin == aOther.mMargin) { return NS_STYLE_HINT_NONE; @@ -314,8 +310,7 @@ nsStylePadding::Destroy(nsPresContext* aContext) { FreeByObjectID(eArenaObjectID_nsStylePadding, this); } -nsChangeHint -nsStylePadding::CalcDifference(const nsStylePadding& aOther) const +nsChangeHint nsStylePadding::CalcDifference(const nsStylePadding& aOther) const { if (mPadding == aOther.mPadding) { return NS_STYLE_HINT_NONE; @@ -328,13 +323,13 @@ nsStylePadding::CalcDifference(const nsStylePadding& aOther) const } nsStyleBorder::nsStyleBorder(StyleStructContext aContext) - : mBorderColors(nullptr) - , mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL) - , mBorderImageRepeatH(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) - , mBorderImageRepeatV(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) - , mFloatEdge(NS_STYLE_FLOAT_EDGE_CONTENT_BOX) - , mBoxDecorationBreak(NS_STYLE_BOX_DECORATION_BREAK_SLICE) - , mComputedBorder(0, 0, 0, 0) + : mBorderColors(nullptr), + mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL), + mBorderImageRepeatH(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH), + mBorderImageRepeatV(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH), + mFloatEdge(NS_STYLE_FLOAT_EDGE_CONTENT_BOX), + mBoxDecorationBreak(NS_STYLE_BOX_DECORATION_BREAK_SLICE), + mComputedBorder(0, 0, 0, 0) { MOZ_COUNT_CTOR(nsStyleBorder); @@ -374,20 +369,20 @@ nsBorderColors::Clone(bool aDeep) const } nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) - : mBorderColors(nullptr) - , mBorderRadius(aSrc.mBorderRadius) - , mBorderImageSource(aSrc.mBorderImageSource) - , mBorderImageSlice(aSrc.mBorderImageSlice) - , mBorderImageWidth(aSrc.mBorderImageWidth) - , mBorderImageOutset(aSrc.mBorderImageOutset) - , mBorderImageFill(aSrc.mBorderImageFill) - , mBorderImageRepeatH(aSrc.mBorderImageRepeatH) - , mBorderImageRepeatV(aSrc.mBorderImageRepeatV) - , mFloatEdge(aSrc.mFloatEdge) - , mBoxDecorationBreak(aSrc.mBoxDecorationBreak) - , mComputedBorder(aSrc.mComputedBorder) - , mBorder(aSrc.mBorder) - , mTwipsPerPixel(aSrc.mTwipsPerPixel) + : mBorderColors(nullptr), + mBorderRadius(aSrc.mBorderRadius), + mBorderImageSource(aSrc.mBorderImageSource), + mBorderImageSlice(aSrc.mBorderImageSlice), + mBorderImageWidth(aSrc.mBorderImageWidth), + mBorderImageOutset(aSrc.mBorderImageOutset), + mBorderImageFill(aSrc.mBorderImageFill), + mBorderImageRepeatH(aSrc.mBorderImageRepeatH), + mBorderImageRepeatV(aSrc.mBorderImageRepeatV), + mFloatEdge(aSrc.mFloatEdge), + mBoxDecorationBreak(aSrc.mBoxDecorationBreak), + mComputedBorder(aSrc.mComputedBorder), + mBorder(aSrc.mBorder), + mTwipsPerPixel(aSrc.mTwipsPerPixel) { MOZ_COUNT_CTOR(nsStyleBorder); if (aSrc.mBorderColors) { @@ -450,8 +445,7 @@ nsStyleBorder::Destroy(nsPresContext* aContext) { FreeByObjectID(eArenaObjectID_nsStyleBorder, this); } -nsChangeHint -nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const +nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const { // XXXbz we should be able to return a more specific change hint for // at least GetComputedBorder() differences... @@ -567,8 +561,7 @@ nsStyleOutline::RecalcData() } } -nsChangeHint -nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const +nsChangeHint nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const { if (mActualOutlineWidth != aOther.mActualOutlineWidth || (mActualOutlineWidth > 0 && @@ -599,8 +592,8 @@ nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const // nsStyleList // nsStyleList::nsStyleList(StyleStructContext aContext) - : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE) - , mCounterStyle(aContext.BuildCounterStyle(NS_LITERAL_STRING("disc"))) + : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE), + mCounterStyle(aContext.BuildCounterStyle(NS_LITERAL_STRING("disc"))) { MOZ_COUNT_CTOR(nsStyleList); SetQuotesInitial(); @@ -612,10 +605,10 @@ nsStyleList::~nsStyleList() } nsStyleList::nsStyleList(const nsStyleList& aSource) - : mListStylePosition(aSource.mListStylePosition) - , mCounterStyle(aSource.mCounterStyle) - , mQuotes(aSource.mQuotes) - , mImageRegion(aSource.mImageRegion) + : mListStylePosition(aSource.mListStylePosition), + mCounterStyle(aSource.mCounterStyle), + mQuotes(aSource.mQuotes), + mImageRegion(aSource.mImageRegion) { SetListStyleImage(aSource.GetListStyleImage()); MOZ_COUNT_CTOR(nsStyleList); @@ -735,8 +728,7 @@ nsStyleXUL::nsStyleXUL(const nsStyleXUL& aSource) MOZ_COUNT_CTOR(nsStyleXUL); } -nsChangeHint -nsStyleXUL::CalcDifference(const nsStyleXUL& aOther) const +nsChangeHint nsStyleXUL::CalcDifference(const nsStyleXUL& aOther) const { if (mBoxAlign == aOther.mBoxAlign && mBoxDirection == aOther.mBoxDirection && @@ -790,8 +782,7 @@ nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource) MOZ_COUNT_CTOR(nsStyleColumn); } -nsChangeHint -nsStyleColumn::CalcDifference(const nsStyleColumn& aOther) const +nsChangeHint nsStyleColumn::CalcDifference(const nsStyleColumn& aOther) const { if ((mColumnWidth.GetUnit() == eStyleUnit_Auto) != (aOther.mColumnWidth.GetUnit() == eStyleUnit_Auto) || @@ -829,7 +820,8 @@ nsStyleSVG::nsStyleSVG(StyleStructContext aContext) : mFill(eStyleSVGPaintType_Color) // Will be initialized to NS_RGB(0, 0, 0) , mStroke(eStyleSVGPaintType_None) , mStrokeDashoffset(0, nsStyleCoord::CoordConstructor) - , mStrokeWidth(nsPresContext::CSSPixelsToAppUnits(1), nsStyleCoord::CoordConstructor) + , mStrokeWidth(nsPresContext::CSSPixelsToAppUnits(1), + nsStyleCoord::CoordConstructor) , mFillOpacity(1.0f) , mStrokeMiterlimit(4.0f) , mStrokeOpacity(1.0f) @@ -886,8 +878,8 @@ nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource) MOZ_COUNT_CTOR(nsStyleSVG); } -static bool -PaintURIChanged(const nsStyleSVGPaint& aPaint1, const nsStyleSVGPaint& aPaint2) +static bool PaintURIChanged(const nsStyleSVGPaint& aPaint1, + const nsStyleSVGPaint& aPaint2) { if (aPaint1.mType != aPaint2.mType) { return aPaint1.mType == eStyleSVGPaintType_Server || @@ -897,8 +889,7 @@ PaintURIChanged(const nsStyleSVGPaint& aPaint1, const nsStyleSVGPaint& aPaint2) !EqualURIs(aPaint1.mPaint.mPaintServer, aPaint2.mPaint.mPaintServer); } -nsChangeHint -nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const +nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const { nsChangeHint hint = nsChangeHint(0); @@ -1045,6 +1036,7 @@ nsStyleClipPath::operator=(const nsStyleClipPath& aOther) return *this; } + bool nsStyleClipPath::operator==(const nsStyleClipPath& aOther) const { @@ -1158,6 +1150,7 @@ nsStyleFilter::operator=(const nsStyleFilter& aOther) return *this; } + bool nsStyleFilter::operator==(const nsStyleFilter& aOther) const { @@ -1222,8 +1215,7 @@ nsStyleFilter::SetDropShadow(nsCSSShadowArray* aDropShadow) // nsStyleSVGReset // nsStyleSVGReset::nsStyleSVGReset(StyleStructContext aContext) - : mMask(nsStyleImageLayers::LayerType::Mask) - , mStopColor(NS_RGB(0, 0, 0)) + : mStopColor(NS_RGB(0, 0, 0)) , mFloodColor(NS_RGB(0, 0, 0)) , mLightingColor(NS_RGB(255, 255, 255)) , mStopOpacity(1.0f) @@ -1255,9 +1247,7 @@ nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource) MOZ_COUNT_CTOR(nsStyleSVGReset); } -void -nsStyleSVGReset::Destroy(nsPresContext* aContext) -{ +void nsStyleSVGReset::Destroy(nsPresContext* aContext) { mMask.UntrackImages(aContext); this->~nsStyleSVGReset(); @@ -1265,8 +1255,7 @@ nsStyleSVGReset::Destroy(nsPresContext* aContext) FreeByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, this); } -nsChangeHint -nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) const +nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) const { nsChangeHint hint = nsChangeHint(0); @@ -1350,8 +1339,7 @@ nsStyleSVGPaint::SetType(nsStyleSVGPaintType aType) mType = aType; } -nsStyleSVGPaint& -nsStyleSVGPaint::operator=(const nsStyleSVGPaint& aOther) +nsStyleSVGPaint& nsStyleSVGPaint::operator=(const nsStyleSVGPaint& aOther) { if (this == &aOther) return *this; @@ -1774,8 +1762,7 @@ nsStyleTableBorder::nsStyleTableBorder(const nsStyleTableBorder& aSource) MOZ_COUNT_CTOR(nsStyleTableBorder); } -nsChangeHint -nsStyleTableBorder::CalcDifference(const nsStyleTableBorder& aOther) const +nsChangeHint nsStyleTableBorder::CalcDifference(const nsStyleTableBorder& aOther) const { // Border-collapse changes need a reframe, because we use a different frame // class for table cells in the collapsed border model. This is used to @@ -1812,8 +1799,7 @@ nsStyleColor::nsStyleColor(const nsStyleColor& aSource) MOZ_COUNT_CTOR(nsStyleColor); } -nsChangeHint -nsStyleColor::CalcDifference(const nsStyleColor& aOther) const +nsChangeHint nsStyleColor::CalcDifference(const nsStyleColor& aOther) const { if (mColor == aOther.mColor) return NS_STYLE_HINT_NONE; @@ -2260,7 +2246,7 @@ const nsCSSProperty nsStyleImageLayers::kMaskLayerTable[] = { }; #endif -nsStyleImageLayers::nsStyleImageLayers(nsStyleImageLayers::LayerType aType) +nsStyleImageLayers::nsStyleImageLayers() : mAttachmentCount(1) , mClipCount(1) , mOriginCount(1) @@ -2275,9 +2261,6 @@ nsStyleImageLayers::nsStyleImageLayers(nsStyleImageLayers::LayerType aType) , mLayers(nsStyleAutoArray::WITH_SINGLE_INITIAL_ELEMENT) { MOZ_COUNT_CTOR(nsStyleImageLayers); - - // Ensure first layer is initialized as specified layer type - mLayers[0].Initialize(aType); } nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers &aSource) @@ -2517,42 +2500,24 @@ nsStyleImageLayers::Size::operator==(const Size& aOther) const (mHeightType != eLengthPercentage || mHeight == aOther.mHeight); } -bool -nsStyleImageLayers::Repeat::IsInitialValue(LayerType aType) const -{ - if (aType == LayerType::Background || - aType == LayerType::Mask) { - // bug 1258623 - mask-repeat initial value should be no-repeat - return mXRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT && - mYRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT; - } - - MOZ_ASSERT_UNREACHABLE("unsupported layer type."); - return false; -} - void -nsStyleImageLayers::Repeat::SetInitialValues(LayerType aType) +nsStyleImageLayers::Repeat::SetInitialValues() { - if (aType == LayerType::Background || - aType == LayerType::Mask) { - // bug 1258623 - mask-repeat initial value should be no-repeat - mXRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT; - mYRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT; - } else { - MOZ_ASSERT_UNREACHABLE("unsupported layer type."); - } + mXRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT; + mYRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT; } nsStyleImageLayers::Layer::Layer() - : mClip(NS_STYLE_IMAGELAYER_CLIP_BORDER) - , mAttachment(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL) - , mBlendMode(NS_STYLE_BLEND_NORMAL) - , mComposite(NS_STYLE_MASK_COMPOSITE_ADD) - , mMaskMode(NS_STYLE_MASK_MODE_MATCH_SOURCE) +: mClip(NS_STYLE_IMAGELAYER_CLIP_BORDER), + mOrigin(NS_STYLE_IMAGELAYER_ORIGIN_PADDING), + mAttachment(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL), + mBlendMode(NS_STYLE_BLEND_NORMAL), + mComposite(NS_STYLE_MASK_COMPOSITE_ADD), + mMaskMode(NS_STYLE_MASK_MODE_MATCH_SOURCE) { mPosition.SetInitialPercentValues(0.0f); // Initial value is "0% 0%" mImage.SetNull(); + mRepeat.SetInitialValues(); mSize.SetInitialValues(); } @@ -2560,19 +2525,6 @@ nsStyleImageLayers::Layer::~Layer() { } -void -nsStyleImageLayers::Layer::Initialize(nsStyleImageLayers::LayerType aType) -{ - mRepeat.SetInitialValues(aType); - - if (aType == LayerType::Background) { - mOrigin = NS_STYLE_IMAGELAYER_ORIGIN_PADDING; - } else { - MOZ_ASSERT(aType == LayerType::Mask, "unsupported layer type."); - mOrigin = NS_STYLE_IMAGELAYER_ORIGIN_BORDER; - } -} - bool nsStyleImageLayers::Layer::RenderingMightDependOnPositioningAreaSizeChange() const { @@ -2635,8 +2587,7 @@ nsStyleImageLayers::Layer::CalcDifference(const nsStyleImageLayers::Layer& aOthe // nsStyleBackground::nsStyleBackground(StyleStructContext aContext) - : mImage(nsStyleImageLayers::LayerType::Background) - , mBackgroundColor(NS_RGBA(0, 0, 0, 0)) + : mBackgroundColor(NS_RGBA(0, 0, 0, 0)) { MOZ_COUNT_CTOR(nsStyleBackground); } @@ -2646,6 +2597,7 @@ nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource) , mBackgroundColor(aSource.mBackgroundColor) { MOZ_COUNT_CTOR(nsStyleBackground); + } nsStyleBackground::~nsStyleBackground() @@ -2664,8 +2616,7 @@ nsStyleBackground::Destroy(nsPresContext* aContext) FreeByObjectID(eArenaObjectID_nsStyleBackground, this); } -nsChangeHint -nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const +nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const { nsChangeHint hint = nsChangeHint(0); if (mBackgroundColor != aOther.mBackgroundColor) { @@ -2677,8 +2628,7 @@ nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const return hint; } -bool -nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const +bool nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const { NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) { const nsStyleImageLayers::Layer &layer = mImage.mLayers[i]; @@ -2691,16 +2641,14 @@ nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const return false; } -bool -nsStyleBackground::IsTransparent() const +bool nsStyleBackground::IsTransparent() const { return BottomLayer().mImage.IsEmpty() && mImage.mImageCount == 1 && NS_GET_A(mBackgroundColor) == 0; } -void -nsTimingFunction::AssignFromKeyword(int32_t aTimingFunctionType) +void nsTimingFunction::AssignFromKeyword(int32_t aTimingFunctionType) { switch (aTimingFunctionType) { case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START: @@ -2954,8 +2902,7 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) mPerspectiveOrigin[1] = aSource.mPerspectiveOrigin[1]; } -nsChangeHint -nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const +nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const { nsChangeHint hint = nsChangeHint(0); @@ -3191,8 +3138,7 @@ nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource) MOZ_COUNT_CTOR(nsStyleVisibility); } -nsChangeHint -nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) const +nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) const { nsChangeHint hint = nsChangeHint(0); @@ -3242,8 +3188,7 @@ nsStyleContentData::~nsStyleContentData() } } -nsStyleContentData& -nsStyleContentData::operator=(const nsStyleContentData& aOther) +nsStyleContentData& nsStyleContentData::operator=(const nsStyleContentData& aOther) { if (this == &aOther) return *this; @@ -3266,8 +3211,7 @@ nsStyleContentData::operator=(const nsStyleContentData& aOther) return *this; } -bool -nsStyleContentData::operator==(const nsStyleContentData& aOther) const +bool nsStyleContentData::operator==(const nsStyleContentData& aOther) const { if (mType != aOther.mType) return false; @@ -3408,8 +3352,7 @@ nsStyleContent::nsStyleContent(const nsStyleContent& aSource) } } -nsChangeHint -nsStyleContent::CalcDifference(const nsStyleContent& aOther) const +nsChangeHint nsStyleContent::CalcDifference(const nsStyleContent& aOther) const { // In ReResolveStyleContext we assume that if there's no existing // ::before or ::after and we don't have to restyle children of the @@ -3458,8 +3401,7 @@ nsStyleContent::CalcDifference(const nsStyleContent& aOther) const return NS_STYLE_HINT_NONE; } -nsresult -nsStyleContent::AllocateContents(uint32_t aCount) +nsresult nsStyleContent::AllocateContents(uint32_t aCount) { // We need to run the destructors of the elements of mContents, so we // delete and reallocate even if aCount == mContentCount. (If @@ -3486,7 +3428,8 @@ nsStyleContent::AllocateContents(uint32_t aCount) nsStyleTextReset::nsStyleTextReset(StyleStructContext aContext) : mTextDecorationLine(NS_STYLE_TEXT_DECORATION_LINE_NONE) , mUnicodeBidi(NS_STYLE_UNICODE_BIDI_NORMAL) - , mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID | BORDER_COLOR_FOREGROUND) + , mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID | + BORDER_COLOR_FOREGROUND) , mTextDecorationColor(NS_RGB(0, 0, 0)) { MOZ_COUNT_CTOR(nsStyleTextReset); @@ -3640,8 +3583,7 @@ nsStyleText::~nsStyleText() MOZ_COUNT_DTOR(nsStyleText); } -nsChangeHint -nsStyleText::CalcDifference(const nsStyleText& aOther) const +nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aOther) const { if (WhiteSpaceOrNewlineIsSignificant() != aOther.WhiteSpaceOrNewlineIsSignificant()) { @@ -3813,8 +3755,7 @@ nsStyleUserInterface::~nsStyleUserInterface() delete [] mCursorArray; } -nsChangeHint -nsStyleUserInterface::CalcDifference(const nsStyleUserInterface& aOther) const +nsChangeHint nsStyleUserInterface::CalcDifference(const nsStyleUserInterface& aOther) const { nsChangeHint hint = nsChangeHint(0); if (mCursor != aOther.mCursor) @@ -3896,8 +3837,7 @@ nsStyleUIReset::~nsStyleUIReset() MOZ_COUNT_DTOR(nsStyleUIReset); } -nsChangeHint -nsStyleUIReset::CalcDifference(const nsStyleUIReset& aOther) const +nsChangeHint nsStyleUIReset::CalcDifference(const nsStyleUIReset& aOther) const { // ignore mIMEMode if (mForceBrokenImageIcon != aOther.mForceBrokenImageIcon) diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index b492be0fb7ba..c50c27596160 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -472,6 +472,12 @@ private: }; struct nsStyleImageLayers { + nsStyleImageLayers(); + nsStyleImageLayers(const nsStyleImageLayers &aSource); + ~nsStyleImageLayers() { + MOZ_COUNT_DTOR(nsStyleImageLayers); + } + // Indices into kBackgroundLayerTable and kMaskLayerTable enum { shorthand = 0, @@ -488,17 +494,6 @@ struct nsStyleImageLayers { composite }; - enum class LayerType : uint8_t { - Background = 0, - Mask - }; - - explicit nsStyleImageLayers(LayerType aType); - nsStyleImageLayers(const nsStyleImageLayers &aSource); - ~nsStyleImageLayers() { - MOZ_COUNT_DTOR(nsStyleImageLayers); - } - struct Position; friend struct Position; struct Position { @@ -603,10 +598,13 @@ struct nsStyleImageLayers { // Initialize nothing Repeat() {} - bool IsInitialValue(LayerType aType) const; + bool IsInitialValue() const { + return mXRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT && + mYRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT; + } // Initialize to initial values - void SetInitialValues(LayerType aType); + void SetInitialValues(); bool operator==(const Repeat& aOther) const { return mXRepeat == aOther.mXRepeat && @@ -658,14 +656,10 @@ struct nsStyleImageLayers { // NS_STYLE_MASK_MODE_MATCH_SOURCE. Repeat mRepeat; // [reset] See nsStyleConsts.h - // This constructor does not initialize mRepeat or mOrigin and Initialize() - // must be called to do that. + // Initializes only mImage Layer(); ~Layer(); - // Initialize mRepeat and mOrigin by specified layer type - void Initialize(LayerType aType); - // Register/unregister images with the document. We do this only // after the dust has settled in ComputeBackgroundData. void TrackImages(nsPresContext* aContext) { @@ -3547,7 +3541,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVGReset } nsStyleImageLayers mMask; - nsStyleClipPath mClipPath; // [reset] + nsStyleClipPath mClipPath; // [reset] nscolor mStopColor; // [reset] nscolor mFloodColor; // [reset] nscolor mLightingColor; // [reset] diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 1f9215ac2053..f0c16a390ae5 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -6989,8 +6989,8 @@ if (SupportsMaskShorthand()) { domProp: "maskOrigin", inherited: false, type: CSS_TYPE_LONGHAND, - initial_values: [ "border-box" ], - other_values: [ "padding-box", "content-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ], + initial_values: [ "padding-box" ], + other_values: [ "border-box", "content-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ], invalid_values: [ "margin-box", "padding-box padding-box" ] }; gCSSProperties["mask-position"] = { From 8d11127d3c1a4187d36ddcc8294d6396875f05ed Mon Sep 17 00:00:00 2001 From: JW Wang Date: Tue, 31 May 2016 13:55:27 +0800 Subject: [PATCH 196/199] Bug 1276849 - some AudioStream::OpenCubeb code cleanup. r=kinetik. MozReview-Commit-ID: 1X1DXa8fEZN --HG-- extra : rebase_source : 79ab44ea8cbfd5710006731789925daaad80dc3a --- dom/media/AudioStream.cpp | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/dom/media/AudioStream.cpp b/dom/media/AudioStream.cpp index 7db8f457c878..b29a504007aa 100644 --- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -329,10 +329,6 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aRate, auto startTime = TimeStamp::Now(); mIsFirst = CubebUtils::GetFirstStream(); - if (!CubebUtils::GetCubebContext()) { - return NS_ERROR_FAILURE; - } - LOG("%s channels: %d, rate: %d", __FUNCTION__, aNumChannels, aRate); mInRate = mOutRate = aRate; mChannels = aNumChannels; @@ -361,42 +357,26 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aRate, return OpenCubeb(params, startTime); } -// This code used to live inside AudioStream::Init(), but on Mac (others?) -// it has been known to take 300-800 (or even 8500) ms to execute(!) nsresult AudioStream::OpenCubeb(cubeb_stream_params &aParams, TimeStamp aStartTime) { cubeb* cubebContext = CubebUtils::GetCubebContext(); if (!cubebContext) { NS_WARNING("Can't get cubeb context!"); - MonitorAutoLock mon(mMonitor); - mState = AudioStream::ERRORED; return NS_ERROR_FAILURE; } - // If the latency pref is set, use it. Otherwise, if this stream is intended - // for low latency playback, try to get the lowest latency possible. - // Otherwise, for normal streams, use 100ms. - uint32_t latency = CubebUtils::GetCubebLatency(); - - { - cubeb_stream* stream; - if (cubeb_stream_init(cubebContext, &stream, "AudioStream", - nullptr, nullptr, nullptr, &aParams, - latency, DataCallback_S, StateCallback_S, this) == CUBEB_OK) { - MonitorAutoLock mon(mMonitor); - MOZ_ASSERT(mState != SHUTDOWN); - mCubebStream.reset(stream); - } else { - MonitorAutoLock mon(mMonitor); - mState = ERRORED; - NS_WARNING(nsPrintfCString("AudioStream::OpenCubeb() %p failed to init cubeb", this).get()); - return NS_ERROR_FAILURE; - } + cubeb_stream* stream = nullptr; + if (cubeb_stream_init(cubebContext, &stream, "AudioStream", + nullptr, nullptr, nullptr, &aParams, + CubebUtils::GetCubebLatency(), + DataCallback_S, StateCallback_S, this) == CUBEB_OK) { + mCubebStream.reset(stream); + } else { + NS_WARNING(nsPrintfCString("AudioStream::OpenCubeb() %p failed to init cubeb", this).get()); + return NS_ERROR_FAILURE; } - mState = INITIALIZED; - TimeDuration timeDelta = TimeStamp::Now() - aStartTime; LOG("creation time %sfirst: %u ms", mIsFirst ? "" : "not ", (uint32_t) timeDelta.ToMilliseconds()); From 17e4b1d7d86de06106f31ddc91fd42d299528e2b Mon Sep 17 00:00:00 2001 From: JW Wang Date: Tue, 31 May 2016 14:18:58 +0800 Subject: [PATCH 197/199] Bug 1276851 - Remove AudioStream::mIsFirst. r=kinetik. MozReview-Commit-ID: H9KCM2zZulA --HG-- extra : rebase_source : 51387a1bd37f1b22bdb5100abe83af17c0caa481 --- dom/media/AudioStream.cpp | 11 ++++++----- dom/media/AudioStream.h | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/dom/media/AudioStream.cpp b/dom/media/AudioStream.cpp index b29a504007aa..51bea8a2898a 100644 --- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -327,7 +327,7 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aRate, const dom::AudioChannel aAudioChannel) { auto startTime = TimeStamp::Now(); - mIsFirst = CubebUtils::GetFirstStream(); + auto isFirst = CubebUtils::GetFirstStream(); LOG("%s channels: %d, rate: %d", __FUNCTION__, aNumChannels, aRate); mInRate = mOutRate = aRate; @@ -354,11 +354,12 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aRate, params.format = ToCubebFormat::value; mAudioClock.Init(); - return OpenCubeb(params, startTime); + return OpenCubeb(params, startTime, isFirst); } nsresult -AudioStream::OpenCubeb(cubeb_stream_params &aParams, TimeStamp aStartTime) +AudioStream::OpenCubeb(cubeb_stream_params& aParams, + TimeStamp aStartTime, bool aIsFirst) { cubeb* cubebContext = CubebUtils::GetCubebContext(); if (!cubebContext) { @@ -378,9 +379,9 @@ AudioStream::OpenCubeb(cubeb_stream_params &aParams, TimeStamp aStartTime) } TimeDuration timeDelta = TimeStamp::Now() - aStartTime; - LOG("creation time %sfirst: %u ms", mIsFirst ? "" : "not ", + LOG("creation time %sfirst: %u ms", aIsFirst ? "" : "not ", (uint32_t) timeDelta.ToMilliseconds()); - Telemetry::Accumulate(mIsFirst ? Telemetry::AUDIOSTREAM_FIRST_OPEN_MS : + Telemetry::Accumulate(aIsFirst ? Telemetry::AUDIOSTREAM_FIRST_OPEN_MS : Telemetry::AUDIOSTREAM_LATER_OPEN_MS, timeDelta.ToMilliseconds()); return NS_OK; diff --git a/dom/media/AudioStream.h b/dom/media/AudioStream.h index 7942ecc57b60..f41ef862a501 100644 --- a/dom/media/AudioStream.h +++ b/dom/media/AudioStream.h @@ -313,7 +313,8 @@ protected: int64_t GetPositionInFramesUnlocked(); private: - nsresult OpenCubeb(cubeb_stream_params &aParams, TimeStamp aStartTime); + nsresult OpenCubeb(cubeb_stream_params& aParams, + TimeStamp aStartTime, bool aIsFirst); static long DataCallback_S(cubeb_stream*, void* aThis, const void* /* aInputBuffer */, void* aOutputBuffer, @@ -371,7 +372,6 @@ private: }; StreamState mState; - bool mIsFirst; DataSource& mDataSource; }; From 925bd5c7c664eb7acb395148b20ebfc4f7b3e772 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Tue, 31 May 2016 14:33:03 +0800 Subject: [PATCH 198/199] Bug 1276852 - Assert AudioStream::Start() is called when mState is INITIALIZED. r=kinetik. MozReview-Commit-ID: DeGSEpSWYSM --HG-- extra : rebase_source : d2ab8b8abb7e9893934783b99939bbfe359e93d0 --- dom/media/AudioStream.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/dom/media/AudioStream.cpp b/dom/media/AudioStream.cpp index 51bea8a2898a..63572ad9078a 100644 --- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -401,16 +401,10 @@ void AudioStream::Start() { MonitorAutoLock mon(mMonitor); - if (mState == INITIALIZED) { - // DataCallback might be called before InvokeCubeb returns - // if cubeb_stream_start() succeeds. mState must be set to STARTED - // beforehand. - mState = STARTED; - if (InvokeCubeb(cubeb_stream_start) != CUBEB_OK) { - mState = ERRORED; - } - LOG("started, state %s", mState == STARTED ? "STARTED" : "ERRORED"); - } + MOZ_ASSERT(mState == INITIALIZED); + auto r = InvokeCubeb(cubeb_stream_start); + mState = r == CUBEB_OK ? STARTED : ERRORED; + LOG("started, state %s", mState == STARTED ? "STARTED" : "ERRORED"); } void From ad436d89e51ad8895064086798926e8403732637 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Wed, 1 Jun 2016 09:19:06 +0200 Subject: [PATCH 199/199] Backed out changeset b541ce3cbff5 (bug 1277087) for ab build failures --HG-- extra : rebase_source : bad752021d110e22a4942fd23a9ec04c780a677c --- build/mach_bootstrap.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index fe769addb847..ce73bbc55aaf 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -233,8 +233,11 @@ def bootstrap(topsrcdir, mozilla_dir=None): # case. For default behavior, we educate users and give them an opportunity # to react. We always exit after creating the directory because users don't # like surprises. - sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS] - import mach.main + try: + import mach.main + except ImportError: + sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS] + import mach.main def telemetry_handler(context, data): # We have not opted-in to telemetry