WebKit Bugzilla
Attachment 347253 Details for
Bug 188589
: [JSC] Add GPRReg::InvalidGPRReg and FPRReg::InvalidFPRReg
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-188589-20180816171225.patch (text/plain), 19.01 KB, created by
Fujii Hironori
on 2018-08-16 01:12:26 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Fujii Hironori
Created:
2018-08-16 01:12:26 PDT
Size:
19.01 KB
patch
obsolete
>Subversion Revision: 234913 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 4cd46816db50337e1e93f3101169be64d595fe61..40ef3905fbb8be0758c4ecbba820e42c7d196448 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,40 @@ >+2018-08-16 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ [JSC] Add GPRReg::InvalidGPRReg and FPRReg::InvalidFPRReg >+ https://bugs.webkit.org/show_bug.cgi?id=188589 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Since GPRReg(RegisterID) and FPRReg(FPRegisterID) do not include -1 in their enum values, >+ UBSan dumps bunch of warnings "runtime error: load of value 4294967295, which is not a valid value for type 'RegisterID'". >+ >+ - We add InvalidGPRReg and InvalidFPRReg to enum values of GPRReg and FPRReg to suppress the above warnings. >+ - We make GPRReg and FPRReg int8_t enums. >+ - We replace `#define InvalidGPRReg ((JSC::GPRReg)-1)` to `static constexpr GPRReg InvalidGPRReg { GPRReg::InvalidGPRReg };`. >+ - We add operator+/- definition for RegisterIDs as a MSVC workaround. MSVC fails to resolve operator+ and operator- >+ if `enum : int8_t` is used instead of `enum`. >+ >+ * assembler/ARM64Assembler.h: >+ * assembler/ARMAssembler.h: >+ * assembler/ARMv7Assembler.h: >+ * assembler/MIPSAssembler.h: >+ * assembler/MacroAssembler.h: >+ * assembler/X86Assembler.h: >+ * jit/CCallHelpers.h: >+ (JSC::CCallHelpers::clampArrayToSize): >+ * jit/FPRInfo.h: >+ * jit/GPRInfo.h: >+ (JSC::JSValueRegs::JSValueRegs): >+ (JSC::JSValueRegs::tagGPR const): >+ (JSC::JSValueRegs::payloadGPR const): >+ (JSC::JSValueSource::JSValueSource): >+ (JSC::JSValueSource::unboxedCell): >+ (JSC::JSValueSource::operator bool const): >+ (JSC::JSValueSource::base const): >+ (JSC::JSValueSource::tagGPR const): >+ (JSC::JSValueSource::payloadGPR const): >+ (JSC::JSValueSource::hasKnownTag const): >+ > 2018-08-15 Keith Miller <keith_miller@apple.com> > > Remove evernote hacks >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 6033253ffbd476951c9c06a6ec13e193405e770e..35d041ebbbddd506bc04bd30cf74c3072be599a7 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,15 @@ >+2018-08-16 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ [JSC] Add GPRReg::InvalidGPRReg and FPRReg::InvalidFPRReg >+ https://bugs.webkit.org/show_bug.cgi?id=188589 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * cssjit/FunctionCall.h: >+ (WebCore::FunctionCall::FunctionCall): >+ * cssjit/SelectorCompiler.cpp: >+ (WebCore::SelectorCompiler::SelectorCodeGenerator::modulo): >+ > 2018-08-15 Ansh Shukla <ansh_shukla@apple.com> > > NSURLAuthenticationMethodOAuth challenges are surfaced to clients in -didReceiveAuthenticationChallenge as NSURLAuthenticationMethodDefault >diff --git a/Source/JavaScriptCore/assembler/ARM64Assembler.h b/Source/JavaScriptCore/assembler/ARM64Assembler.h >index 209e53a8501e12df53f745b7834e2971da3c8573..3c9478e5142b0fa475f3ee61f896644852396575 100644 >--- a/Source/JavaScriptCore/assembler/ARM64Assembler.h >+++ b/Source/JavaScriptCore/assembler/ARM64Assembler.h >@@ -162,7 +162,7 @@ inline uint16_t getHalfword(uint64_t value, int which) > > namespace ARM64Registers { > >-typedef enum { >+typedef enum : int8_t { > // Parameter/result registers. > x0, > x1, >@@ -208,9 +208,10 @@ typedef enum { > x29 = fp, > x30 = lr, > zr = 0x3f, >+ InvalidGPRReg = -1, > } RegisterID; > >-typedef enum { >+typedef enum : int8_t { > pc, > nzcv, > fpsr >@@ -219,7 +220,7 @@ typedef enum { > // ARM64 always has 32 FPU registers 128-bits each. See http://llvm.org/devmtg/2012-11/Northover-AArch64.pdf > // and Section 5.1.2 in http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf. > // However, we only use them for 64-bit doubles. >-typedef enum { >+typedef enum : int8_t { > // Parameter/result registers. > q0, > q1, >@@ -255,6 +256,7 @@ typedef enum { > q29, > q30, > q31, >+ InvalidFPRReg = -1, > } FPRegisterID; > > static constexpr bool isSp(RegisterID reg) { return reg == sp; } >diff --git a/Source/JavaScriptCore/assembler/ARMAssembler.h b/Source/JavaScriptCore/assembler/ARMAssembler.h >index 362ad372ad04df95990b7af08b0fd5473be268db..3554821b60fc0e744d7f33d1cbafd102465f1f25 100644 >--- a/Source/JavaScriptCore/assembler/ARMAssembler.h >+++ b/Source/JavaScriptCore/assembler/ARMAssembler.h >@@ -38,7 +38,7 @@ namespace JSC { > > namespace ARMRegisters { > >- typedef enum { >+ typedef enum : int8_t { > r0, > r1, > r2, >@@ -62,15 +62,16 @@ namespace JSC { > r12 = ip, S1 = ip, > r13 = sp, > r14 = lr, >- r15 = pc >+ r15 = pc, >+ InvalidGPRReg = -1, > } RegisterID; > >- typedef enum { >+ typedef enum : int8_t { > apsr, > fpscr > } SPRegisterID; > >- typedef enum { >+ typedef enum : int8_t { > d0, > d1, > d2, >@@ -105,6 +106,7 @@ namespace JSC { > d30, > d31, > #endif // CPU(ARM_NEON) || CPU(ARM_VFP_V3_D32) >+ InvalidFPRReg = -1, > } FPRegisterID; > > } // namespace ARMRegisters >diff --git a/Source/JavaScriptCore/assembler/ARMv7Assembler.h b/Source/JavaScriptCore/assembler/ARMv7Assembler.h >index 3bade3f7d498cf2cf39baf743a86a5965b38964b..40d785efa787bee31bea7abcac53b20e08001db7 100644 >--- a/Source/JavaScriptCore/assembler/ARMv7Assembler.h >+++ b/Source/JavaScriptCore/assembler/ARMv7Assembler.h >@@ -39,7 +39,7 @@ namespace JSC { > > namespace ARMRegisters { > >- typedef enum { >+ typedef enum : int8_t { > r0, > r1, > r2, >@@ -63,15 +63,16 @@ namespace ARMRegisters { > ip = r12, > sp = r13, > lr = r14, >- pc = r15 >+ pc = r15, >+ InvalidGPRReg = -1, > } RegisterID; > >- typedef enum { >+ typedef enum : int8_t { > apsr, > fpscr > } SPRegisterID; > >- typedef enum { >+ typedef enum : int8_t { > s0, > s1, > s2, >@@ -106,7 +107,7 @@ namespace ARMRegisters { > s31, > } FPSingleRegisterID; > >- typedef enum { >+ typedef enum : int8_t { > d0, > d1, > d2, >@@ -141,10 +142,11 @@ namespace ARMRegisters { > d30, > d31, > #endif // CPU(ARM_NEON) || CPU(ARM_VFP_V3_D32) >+ InvalidFPRReg = -1, > } FPDoubleRegisterID; > > #if CPU(ARM_NEON) >- typedef enum { >+ typedef enum : int8_t { > q0, > q1, > q2, >diff --git a/Source/JavaScriptCore/assembler/MIPSAssembler.h b/Source/JavaScriptCore/assembler/MIPSAssembler.h >index e3e549004fc6e7ad2920760107f994b957da3f97..edb22446208cd17333ff66ec8148aa0b537799ea 100644 >--- a/Source/JavaScriptCore/assembler/MIPSAssembler.h >+++ b/Source/JavaScriptCore/assembler/MIPSAssembler.h >@@ -41,7 +41,7 @@ namespace JSC { > typedef uint32_t MIPSWord; > > namespace MIPSRegisters { >-typedef enum { >+typedef enum : int8_t { > r0 = 0, > r1, > r2, >@@ -105,10 +105,11 @@ typedef enum { > gp = r28, > sp = r29, > fp = r30, >- ra = r31 >+ ra = r31, >+ InvalidGPRReg = -1, > } RegisterID; > >-typedef enum { >+typedef enum : int8_t { > fir = 0, > fccr = 25, > fexr = 26, >@@ -117,7 +118,7 @@ typedef enum { > pc > } SPRegisterID; > >-typedef enum { >+typedef enum : int8_t { > f0, > f1, > f2, >@@ -149,7 +150,8 @@ typedef enum { > f28, > f29, > f30, >- f31 >+ f31, >+ InvalidFPRReg = -1, > } FPRegisterID; > > } // namespace MIPSRegisters >diff --git a/Source/JavaScriptCore/assembler/MacroAssembler.h b/Source/JavaScriptCore/assembler/MacroAssembler.h >index d3c81765e9027f67ca05823b39880808e65b5ae8..625b1123f53fa168d4fe14c7c802e505ff0c2b1d 100644 >--- a/Source/JavaScriptCore/assembler/MacroAssembler.h >+++ b/Source/JavaScriptCore/assembler/MacroAssembler.h >@@ -1997,8 +1997,8 @@ private: > > public: > >- enum RegisterID { NoRegister }; >- enum FPRegisterID { NoFPRegister }; >+ enum RegisterID : int8_t { NoRegister, InvalidGPRReg = -1 }; >+ enum FPRegisterID : int8_t { NoFPRegister, InvalidFPRReg = -1 }; > }; > > } // namespace JSC >diff --git a/Source/JavaScriptCore/assembler/X86Assembler.h b/Source/JavaScriptCore/assembler/X86Assembler.h >index 1ea7a75ed8ff11f044d9fca524eae891786c7868..e6387a6cac557103951aa027c68b44449a7a0d1c 100644 >--- a/Source/JavaScriptCore/assembler/X86Assembler.h >+++ b/Source/JavaScriptCore/assembler/X86Assembler.h >@@ -41,7 +41,7 @@ inline bool CAN_SIGN_EXTEND_8_32(int32_t value) { return value == (int32_t)(sign > > namespace X86Registers { > >-typedef enum { >+typedef enum : int8_t { > eax, > ecx, > edx, >@@ -58,16 +58,17 @@ typedef enum { > r12, > r13, > r14, >- r15 >+ r15, > #endif >+ InvalidGPRReg = -1, > } RegisterID; > >-typedef enum { >+typedef enum : int8_t { > eip, > eflags > } SPRegisterID; > >-typedef enum { >+typedef enum : int8_t { > xmm0, > xmm1, > xmm2, >@@ -84,10 +85,31 @@ typedef enum { > xmm12, > xmm13, > xmm14, >- xmm15 >+ xmm15, > #endif >+ InvalidFPRReg = -1, > } XMMRegisterID; > >+// MSVC fails to handle RegisterID + Type if we use `enum : int8_t` instead of `enum`. >+// We introduces a workaround for this: defining operator+ and operator-. >+#define JSC_DEFINE_OPERATORS_FOR_REGISTERS(RegisterType) \ >+ inline constexpr RegisterType operator+ (RegisterType a, uint8_t b) { return static_cast<RegisterType>(static_cast<int32_t>(a) + b); } \ >+ inline constexpr RegisterType operator+ (RegisterType a, int32_t b) { return static_cast<RegisterType>(static_cast<int32_t>(a) + b); } \ >+ inline constexpr RegisterType operator+ (int32_t a, RegisterType b) { return static_cast<RegisterType>(a + static_cast<int32_t>(b)); } \ >+ inline constexpr RegisterType operator+ (RegisterType a, uint32_t b) { return static_cast<RegisterType>(static_cast<int32_t>(a) + static_cast<int32_t>(b)); } \ >+ inline constexpr RegisterType operator+ (uint32_t a, RegisterType b) { return static_cast<RegisterType>(static_cast<int32_t>(a) + static_cast<int32_t>(b)); } \ >+ inline constexpr RegisterType operator+ (RegisterType a, RegisterType b) { return static_cast<RegisterType>(static_cast<int32_t>(a) + static_cast<int32_t>(b)); } \ >+ inline constexpr RegisterType operator- (RegisterType a, uint8_t b) { return static_cast<RegisterType>(static_cast<int32_t>(a) - b); } \ >+ inline constexpr RegisterType operator- (RegisterType a, int32_t b) { return static_cast<RegisterType>(static_cast<int32_t>(a) - b); } \ >+ inline constexpr RegisterType operator- (int32_t a, RegisterType b) { return static_cast<RegisterType>(a - static_cast<int32_t>(b)); } \ >+ inline constexpr RegisterType operator- (RegisterType a, uint32_t b) { return static_cast<RegisterType>(static_cast<int32_t>(a) - static_cast<int32_t>(b)); } \ >+ inline constexpr RegisterType operator- (uint32_t a, RegisterType b) { return static_cast<RegisterType>(static_cast<int32_t>(a) - static_cast<int32_t>(b)); } \ >+ inline constexpr RegisterType operator- (RegisterType a, RegisterType b) { return static_cast<RegisterType>(static_cast<int32_t>(a) - static_cast<int32_t>(b)); } >+ >+JSC_DEFINE_OPERATORS_FOR_REGISTERS(RegisterID) >+JSC_DEFINE_OPERATORS_FOR_REGISTERS(SPRegisterID) >+JSC_DEFINE_OPERATORS_FOR_REGISTERS(XMMRegisterID) >+ > } // namespace X86Register > > class X86Assembler { >diff --git a/Source/JavaScriptCore/jit/CCallHelpers.h b/Source/JavaScriptCore/jit/CCallHelpers.h >index e20ced4ec96cb7f99b32fc93a5167826c7c47e02..1d8e3bee9b6bd67e7093475536d2bd57d1f3183f 100644 >--- a/Source/JavaScriptCore/jit/CCallHelpers.h >+++ b/Source/JavaScriptCore/jit/CCallHelpers.h >@@ -314,7 +314,7 @@ private: > std::array<RegType, TargetSize> result { }; > > for (unsigned i = 0; i < TargetSize; i++) { >- ASSERT(sourceArray[i] != InfoTypeForReg<RegType>::InvalidIndex); >+ ASSERT(sourceArray[i] != static_cast<int32_t>(InfoTypeForReg<RegType>::InvalidIndex)); > result[i] = sourceArray[i]; > } > >diff --git a/Source/JavaScriptCore/jit/FPRInfo.h b/Source/JavaScriptCore/jit/FPRInfo.h >index ea47197963ef740c450a529092d73e5b593e4b80..a24d1cb75a3ff99b63895ebbd179ae6142da56ec 100644 >--- a/Source/JavaScriptCore/jit/FPRInfo.h >+++ b/Source/JavaScriptCore/jit/FPRInfo.h >@@ -31,7 +31,7 @@ > namespace JSC { > > typedef MacroAssembler::FPRegisterID FPRReg; >-#define InvalidFPRReg ((::JSC::FPRReg)-1) >+static constexpr FPRReg InvalidFPRReg { FPRReg::InvalidFPRReg }; > > #if ENABLE(JIT) > >diff --git a/Source/JavaScriptCore/jit/GPRInfo.h b/Source/JavaScriptCore/jit/GPRInfo.h >index 8a086ddb50d0495cbfd293e4f7b7d56a9466ed18..2d374e3b859d3dac76fb8f4dfc08a10f1300e874 100644 >--- a/Source/JavaScriptCore/jit/GPRInfo.h >+++ b/Source/JavaScriptCore/jit/GPRInfo.h >@@ -39,7 +39,7 @@ enum NoResultTag { NoResult }; > // top of the LowLevelInterpreter.asm file. > > typedef MacroAssembler::RegisterID GPRReg; >-#define InvalidGPRReg ((::JSC::GPRReg)-1) >+static constexpr GPRReg InvalidGPRReg { GPRReg::InvalidGPRReg }; > > #if ENABLE(JIT) > >@@ -161,8 +161,8 @@ private: > class JSValueRegs { > public: > JSValueRegs() >- : m_tagGPR(static_cast<int8_t>(InvalidGPRReg)) >- , m_payloadGPR(static_cast<int8_t>(InvalidGPRReg)) >+ : m_tagGPR(InvalidGPRReg) >+ , m_payloadGPR(InvalidGPRReg) > { > } > >@@ -196,8 +196,8 @@ public: > } > bool operator!=(JSValueRegs other) const { return !(*this == other); } > >- GPRReg tagGPR() const { return static_cast<GPRReg>(m_tagGPR); } >- GPRReg payloadGPR() const { return static_cast<GPRReg>(m_payloadGPR); } >+ GPRReg tagGPR() const { return m_tagGPR; } >+ GPRReg payloadGPR() const { return m_payloadGPR; } > GPRReg gpr(WhichValueWord which) const > { > switch (which) { >@@ -215,16 +215,16 @@ public: > void dump(PrintStream&) const; > > private: >- int8_t m_tagGPR; >- int8_t m_payloadGPR; >+ GPRReg m_tagGPR; >+ GPRReg m_payloadGPR; > }; > > class JSValueSource { > public: > JSValueSource() > : m_offset(notAddress()) >- , m_baseOrTag(static_cast<int8_t>(InvalidGPRReg)) >- , m_payload(static_cast<int8_t>(InvalidGPRReg)) >+ , m_baseOrTag(InvalidGPRReg) >+ , m_payload(InvalidGPRReg) > , m_tagType(0) > { > } >@@ -239,28 +239,28 @@ public: > > JSValueSource(GPRReg tagGPR, GPRReg payloadGPR) > : m_offset(notAddress()) >- , m_baseOrTag(static_cast<int8_t>(tagGPR)) >- , m_payload(static_cast<int8_t>(payloadGPR)) >+ , m_baseOrTag(tagGPR) >+ , m_payload(payloadGPR) > , m_tagType(0) > { > } > > JSValueSource(MacroAssembler::Address address) > : m_offset(address.offset) >- , m_baseOrTag(static_cast<int8_t>(address.base)) >- , m_payload(static_cast<int8_t>(InvalidGPRReg)) >+ , m_baseOrTag(address.base) >+ , m_payload(InvalidGPRReg) > , m_tagType(0) > { > ASSERT(m_offset != notAddress()); >- ASSERT(static_cast<GPRReg>(m_baseOrTag) != InvalidGPRReg); >+ ASSERT(m_baseOrTag != InvalidGPRReg); > } > > static JSValueSource unboxedCell(GPRReg payloadGPR) > { > JSValueSource result; > result.m_offset = notAddress(); >- result.m_baseOrTag = static_cast<int8_t>(InvalidGPRReg); >- result.m_payload = static_cast<int8_t>(payloadGPR); >+ result.m_baseOrTag = InvalidGPRReg; >+ result.m_payload = payloadGPR; > result.m_tagType = static_cast<int8_t>(JSValue::CellTag); > return result; > } >@@ -268,8 +268,7 @@ public: > bool operator!() const { return !static_cast<bool>(*this); } > explicit operator bool() const > { >- return static_cast<GPRReg>(m_baseOrTag) != InvalidGPRReg >- || static_cast<GPRReg>(m_payload) != InvalidGPRReg; >+ return m_baseOrTag != InvalidGPRReg || m_payload != InvalidGPRReg; > } > > bool isAddress() const >@@ -287,26 +286,26 @@ public: > GPRReg base() const > { > ASSERT(isAddress()); >- return static_cast<GPRReg>(m_baseOrTag); >+ return m_baseOrTag; > } > > GPRReg tagGPR() const > { >- ASSERT(!isAddress() && static_cast<GPRReg>(m_baseOrTag) != InvalidGPRReg); >- return static_cast<GPRReg>(m_baseOrTag); >+ ASSERT(!isAddress() && m_baseOrTag != InvalidGPRReg); >+ return m_baseOrTag; > } > > GPRReg payloadGPR() const > { > ASSERT(!isAddress()); >- return static_cast<GPRReg>(m_payload); >+ return m_payload; > } > > bool hasKnownTag() const > { > ASSERT(!!*this); > ASSERT(!isAddress()); >- return static_cast<GPRReg>(m_baseOrTag) == InvalidGPRReg; >+ return m_baseOrTag == InvalidGPRReg; > } > > uint32_t tag() const >@@ -325,8 +324,8 @@ private: > static inline int32_t notAddress() { return 0x80000000; } > > int32_t m_offset; >- int8_t m_baseOrTag; >- int8_t m_payload; >+ GPRReg m_baseOrTag; >+ GPRReg m_payload; > int8_t m_tagType; // Contains the low bits of the tag. > }; > #endif // USE(JSVALUE32_64) >diff --git a/Source/WebCore/cssjit/FunctionCall.h b/Source/WebCore/cssjit/FunctionCall.h >index 346cf283b899d52b088b0e46ec0e21702d717e8b..85ec74a8a060c09aaf8848bf395b43e48cd9c350 100644 >--- a/Source/WebCore/cssjit/FunctionCall.h >+++ b/Source/WebCore/cssjit/FunctionCall.h >@@ -43,8 +43,8 @@ public: > , m_stackAllocator(stackAllocator) > , m_callRegistry(callRegistry) > , m_argumentCount(0) >- , m_firstArgument(InvalidGPRReg) >- , m_secondArgument(InvalidGPRReg) >+ , m_firstArgument(JSC::InvalidGPRReg) >+ , m_secondArgument(JSC::InvalidGPRReg) > { > } > >diff --git a/Source/WebCore/cssjit/SelectorCompiler.cpp b/Source/WebCore/cssjit/SelectorCompiler.cpp >index 708a4013e5c3a54f1e77386a194988e42de94b2c..46ada4fe1b697a2c288289fd20fff08ab25517a0 100644 >--- a/Source/WebCore/cssjit/SelectorCompiler.cpp >+++ b/Source/WebCore/cssjit/SelectorCompiler.cpp >@@ -2378,7 +2378,7 @@ Assembler::Jump SelectorCodeGenerator::modulo(Assembler::ResultCondition conditi > Assembler::RegisterID dividend = JSC::X86Registers::eax; > RegisterAllocationType dividendAllocation = RegisterAllocationType::External; > StackAllocator::StackReference temporaryDividendStackReference; >- Assembler::RegisterID temporaryDividendCopy = InvalidGPRReg; >+ Assembler::RegisterID temporaryDividendCopy = JSC::InvalidGPRReg; > if (inputDividend != dividend) { > bool registerIsInUse = m_registerAllocator.allocatedRegisters().contains(dividend); > if (registerIsInUse) { >@@ -2400,7 +2400,7 @@ Assembler::Jump SelectorCodeGenerator::modulo(Assembler::ResultCondition conditi > Assembler::RegisterID remainder = JSC::X86Registers::edx; > RegisterAllocationType remainderAllocation = RegisterAllocationType::External; > StackAllocator::StackReference temporaryRemainderStackReference; >- Assembler::RegisterID temporaryRemainderCopy = InvalidGPRReg; >+ Assembler::RegisterID temporaryRemainderCopy = JSC::InvalidGPRReg; > if (inputDividend != remainder) { > bool registerIsInUse = m_registerAllocator.allocatedRegisters().contains(remainder); > if (registerIsInUse) {
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 188589
:
347131
|
347154
|
347160
|
347161
|
347250
|
347253
|
347349
|
347360