Bug 1502797 - Reimplement BigInt using V8/JSC code instead of GMP r=jwalden

Differential Revision: https://phabricator.services.mozilla.com/D10046

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andy Wingo 2019-01-23 11:13:51 +00:00
parent 9e9978c28d
commit 887bf9200b
14 changed files with 2729 additions and 559 deletions

View File

@ -45,25 +45,6 @@ extern JS_PUBLIC_API bool JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,
JS_ICUReallocFn reallocFn,
JS_ICUFreeFn freeFn);
#ifdef ENABLE_BIGINT
namespace JS {
// These types are documented as allocate_function, reallocate_function,
// and free_function in the Info node `(gmp)Custom Allocation`.
using GMPAllocFn = void* (*)(size_t allocSize);
using GMPReallocFn = void* (*)(void* p, size_t oldSize, size_t newSize);
using GMPFreeFn = void (*)(void* p, size_t size);
// This function can be used to track memory used by GMP. If it is
// called, it *must* be called before JS_Init so that same functions are
// used for all allocations.
extern JS_PUBLIC_API void SetGMPMemoryFunctions(GMPAllocFn allocFn,
GMPReallocFn reallocFn,
GMPFreeFn freeFn);
}; // namespace JS
#endif
/**
* Initialize SpiderMonkey, returning true only if initialization succeeded.
* Once this method has succeeded, it is safe to call JS_NewContext and other

View File

@ -164,7 +164,7 @@ JS_PUBLIC_API bool js::ToBooleanSlow(HandleValue v) {
}
#ifdef ENABLE_BIGINT
if (v.isBigInt()) {
return v.toBigInt()->toBoolean();
return !v.toBigInt()->isZero();
}
#endif

View File

@ -487,8 +487,8 @@ static Truthiness Boolish(ParseNode* pn) {
#ifdef ENABLE_BIGINT
case ParseNodeKind::BigIntExpr:
return (pn->as<BigIntLiteral>().box()->value()->toBoolean()) ? Truthy
: Falsy;
return (pn->as<BigIntLiteral>().box()->value()->isZero()) ? Falsy
: Truthy;
#endif
case ParseNodeKind::StringExpr:

View File

@ -9022,7 +9022,7 @@ BigIntLiteral* Parser<FullParseHandler, Unit>::newBigInt() {
const auto& chars = tokenStream.getCharBuffer();
mozilla::Range<const char16_t> source(chars.begin(), chars.length());
BigInt* b = js::StringToBigInt(context, source);
BigInt* b = js::ParseBigIntLiteral(context, source);
if (!b) {
return null();
}

View File

@ -24,7 +24,7 @@ FRAGMENT(jsval, simple) {
RootedValue friendly_string(cx, StringValue(hello));
RootedValue symbol(cx, SymbolValue(GetSymbolFor(cx, hello)));
#ifdef ENABLE_BIGINT
RootedValue bi(cx, BigIntValue(BigInt::create(cx)));
RootedValue bi(cx, BigIntValue(BigInt::zero(cx)));
#endif
RootedValue global(cx);

View File

@ -169,7 +169,8 @@ bool js::ValueToStringBufferSlow(JSContext* cx, const Value& arg,
}
#ifdef ENABLE_BIGINT
if (v.isBigInt()) {
JSLinearString* str = BigInt::toString(cx, v.toBigInt(), 10);
RootedBigInt i(cx, v.toBigInt());
JSLinearString* str = BigInt::toString(cx, i, 10);
if (!str) {
return false;
}

File diff suppressed because it is too large Load Diff

View File

@ -8,9 +8,7 @@
#define vm_BigIntType_h
#include "mozilla/Range.h"
#include "mozilla/RangedPtr.h"
#include <gmp.h>
#include "mozilla/Span.h"
#include "gc/Barrier.h"
#include "gc/GC.h"
@ -32,10 +30,6 @@ class BigInt;
namespace js {
template <typename CharT>
static bool StringToBigIntImpl(const mozilla::Range<const CharT>& chars,
uint8_t radix, Handle<JS::BigInt*> res);
template <XDRMode mode>
XDRResult XDRBigInt(XDRState<mode>* xdr, MutableHandle<JS::BigInt*> bi);
@ -44,54 +38,61 @@ XDRResult XDRBigInt(XDRState<mode>* xdr, MutableHandle<JS::BigInt*> bi);
namespace JS {
class BigInt final : public js::gc::TenuredCell {
// StringToBigIntImpl modifies the num_ field of the res argument.
template <typename CharT>
friend bool js::StringToBigIntImpl(const mozilla::Range<const CharT>& chars,
uint8_t radix, Handle<BigInt*> res);
template <js::XDRMode mode>
friend js::XDRResult js::XDRBigInt(js::XDRState<mode>* xdr,
MutableHandle<BigInt*> bi);
protected:
// Reserved word for Cell GC invariants. This also ensures minimum
// structure size.
uintptr_t reserved_;
public:
using Digit = uintptr_t;
private:
mpz_t num_;
// The low js::gc::Cell::ReservedBits are reserved.
static constexpr uintptr_t SignBit = JS_BIT(js::gc::Cell::ReservedBits);
static constexpr uintptr_t LengthShift = js::gc::Cell::ReservedBits + 1;
static constexpr size_t InlineDigitsLength =
(js::gc::MinCellSize - sizeof(uintptr_t)) / sizeof(Digit);
protected:
BigInt() : reserved_(0) {}
uintptr_t lengthSignAndReservedBits_;
// The digit storage starts with the least significant digit (little-endian
// digit order). Byte order within a digit is of course native endian.
union {
Digit* heapDigits_;
Digit inlineDigits_[InlineDigitsLength];
};
public:
// Allocate and initialize a BigInt value
static BigInt* create(JSContext* cx);
static BigInt* createFromDouble(JSContext* cx, double d);
static BigInt* createFromBoolean(JSContext* cx, bool b);
// Read a BigInt value from a little-endian byte array.
static BigInt* createFromBytes(JSContext* cx, int sign, void* bytes,
size_t nbytes);
static BigInt* createFromInt64(JSContext* cx, int64_t n);
static BigInt* createFromUint64(JSContext* cx, uint64_t n);
static const JS::TraceKind TraceKind = JS::TraceKind::BigInt;
size_t digitLength() const {
return lengthSignAndReservedBits_ >> LengthShift;
}
bool hasInlineDigits() const { return digitLength() <= InlineDigitsLength; }
bool hasHeapDigits() const { return !hasInlineDigits(); }
using Digits = mozilla::Span<Digit>;
Digits digits() {
return Digits(hasInlineDigits() ? inlineDigits_ : heapDigits_,
digitLength());
}
Digit digit(size_t idx) { return digits()[idx]; }
void setDigit(size_t idx, Digit digit) { digits()[idx] = digit; }
bool isZero() const { return digitLength() == 0; }
bool isNegative() const { return lengthSignAndReservedBits_ & SignBit; }
void initializeDigitsToZero();
void traceChildren(JSTracer* trc);
void finalize(js::FreeOp* fop);
js::HashNumber hash();
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
bool toBoolean();
int8_t sign();
static void init();
static BigInt* createUninitialized(JSContext* cx, size_t length,
bool isNegative);
static BigInt* createFromDouble(JSContext* cx, double d);
static BigInt* createFromUint64(JSContext* cx, uint64_t n);
static BigInt* createFromInt64(JSContext* cx, int64_t n);
// FIXME: Cache these values.
static BigInt* zero(JSContext* cx);
static BigInt* one(JSContext* cx);
static BigInt* copy(JSContext* cx, Handle<BigInt*> x);
static BigInt* add(JSContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
@ -116,7 +117,7 @@ class BigInt final : public js::gc::TenuredCell {
// Type-checking versions of arithmetic operations. These methods
// must be called with at least one BigInt operand. Binary
// operations with throw a TypeError if one of the operands is not a
// operations will throw a TypeError if one of the operands is not a
// BigInt value.
static bool add(JSContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
@ -146,15 +147,25 @@ class BigInt final : public js::gc::TenuredCell {
MutableHandle<Value> res);
static double numberValue(BigInt* x);
static JSLinearString* toString(JSContext* cx, BigInt* x, uint8_t radix);
static JSLinearString* toString(JSContext* cx, Handle<BigInt*> x,
uint8_t radix);
template <typename CharT>
static BigInt* parseLiteral(JSContext* cx,
const mozilla::Range<const CharT> chars,
bool* haveParseError);
template <typename CharT>
static BigInt* parseLiteralDigits(JSContext* cx,
const mozilla::Range<const CharT> chars,
unsigned radix, bool isNegative,
bool* haveParseError);
static int8_t compare(BigInt* lhs, BigInt* rhs);
static bool equal(BigInt* lhs, BigInt* rhs);
static bool equal(BigInt* lhs, double rhs);
static JS::Result<bool> looselyEqual(JSContext* cx, Handle<BigInt*> lhs,
HandleValue rhs);
static bool lessThan(BigInt* x, BigInt* y);
// These methods return Nothing when the non-BigInt operand is NaN
// or a string that can't be interpreted as a BigInt.
static mozilla::Maybe<bool> lessThan(BigInt* lhs, double rhs);
@ -166,34 +177,191 @@ class BigInt final : public js::gc::TenuredCell {
static bool lessThan(JSContext* cx, HandleValue lhs, HandleValue rhs,
mozilla::Maybe<bool>& res);
// Return the length in bytes of the representation used by
// writeBytes.
static size_t byteLength(BigInt* x);
private:
static constexpr size_t DigitBits = sizeof(Digit) * CHAR_BIT;
static constexpr size_t HalfDigitBits = DigitBits / 2;
static constexpr Digit HalfDigitMask = (1ull << HalfDigitBits) - 1;
// Write a little-endian representation of a BigInt's absolute value
// to a byte array.
static void writeBytes(BigInt* x, mozilla::RangedPtr<uint8_t> buffer);
static_assert(DigitBits == 32 || DigitBits == 64,
"Unexpected BigInt Digit size");
// The maximum number of digits that the current implementation supports
// would be 0x7fffffff / DigitBits. However, we use a lower limit for now,
// because raising it later is easier than lowering it. Support up to 1
// million bits.
static constexpr size_t MaxBitLength = 1024 * 1024;
static constexpr size_t MaxDigitLength = MaxBitLength / DigitBits;
// BigInts can be serialized to strings of radix between 2 and 36. For a
// given bigint, radix 2 will take the most characters (one per bit).
// Ensure that the max bigint size is small enough so that we can fit the
// corresponding character count into a size_t, with space for a possible
// sign prefix.
static_assert(MaxBitLength <= std::numeric_limits<size_t>::max() - 1,
"BigInt max length must be small enough to be serialized as a "
"binary string");
static size_t calculateMaximumCharactersRequired(HandleBigInt x,
unsigned radix);
static MOZ_MUST_USE bool calculateMaximumDigitsRequired(JSContext* cx,
uint8_t radix,
size_t charCount,
size_t* result);
static bool absoluteDivWithDigitDivisor(
JSContext* cx, Handle<BigInt*> x, Digit divisor,
const mozilla::Maybe<MutableHandle<BigInt*>>& quotient, Digit* remainder,
bool quotientNegative);
static void internalMultiplyAdd(BigInt* source, Digit factor, Digit summand,
unsigned, BigInt* result);
static void multiplyAccumulate(BigInt* multiplicand, Digit multiplier,
BigInt* accumulator,
unsigned accumulatorIndex);
static bool absoluteDivWithBigIntDivisor(
JSContext* cx, Handle<BigInt*> dividend, Handle<BigInt*> divisor,
const mozilla::Maybe<MutableHandle<BigInt*>>& quotient,
const mozilla::Maybe<MutableHandle<BigInt*>>& remainder,
bool quotientNegative);
enum class LeftShiftMode { SameSizeResult, AlwaysAddOneDigit };
static BigInt* absoluteLeftShiftAlwaysCopy(JSContext* cx, Handle<BigInt*> x,
unsigned shift, LeftShiftMode);
static bool productGreaterThan(Digit factor1, Digit factor2, Digit high,
Digit low);
static BigInt* lshByAbsolute(JSContext* cx, HandleBigInt x, HandleBigInt y);
static BigInt* rshByAbsolute(JSContext* cx, HandleBigInt x, HandleBigInt y);
static BigInt* rshByMaximum(JSContext* cx, bool isNegative);
static BigInt* truncateAndSubFromPowerOfTwo(JSContext* cx, HandleBigInt x,
uint64_t bits,
bool resultNegative);
Digit absoluteInplaceAdd(BigInt* summand, unsigned startIndex);
Digit absoluteInplaceSub(BigInt* subtrahend, unsigned startIndex);
void inplaceRightShiftLowZeroBits(unsigned shift);
void inplaceMultiplyAdd(Digit multiplier, Digit part);
// The result of an SymmetricTrim bitwise op has as many digits as the
// smaller operand. A SymmetricFill bitwise op result has as many digits as
// the larger operand, with high digits (if any) copied from the larger
// operand. AsymmetricFill is like SymmetricFill, except the result has as
// many digits as the first operand; this kind is used for the and-not
// operation.
enum class BitwiseOpKind { SymmetricTrim, SymmetricFill, AsymmetricFill };
template <BitwiseOpKind kind, typename BitwiseOp>
static BigInt* absoluteBitwiseOp(JSContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y, BitwiseOp&& op);
// Return `|x| & |y|`.
static BigInt* absoluteAnd(JSContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y);
// Return `|x| | |y|`.
static BigInt* absoluteOr(JSContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y);
// Return `|x| & ~|y|`.
static BigInt* absoluteAndNot(JSContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y);
// Return `|x| ^ |y|`.
static BigInt* absoluteXor(JSContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y);
// Return `(|x| + 1) * (resultNegative ? -1 : +1)`.
static BigInt* absoluteAddOne(JSContext* cx, Handle<BigInt*> x,
bool resultNegative);
// Return `(|x| - 1) * (resultNegative ? -1 : +1)`, with the precondition that
// |x| != 0.
static BigInt* absoluteSubOne(JSContext* cx, Handle<BigInt*> x,
unsigned resultLength);
// Return `a + b`, incrementing `*carry` if the addition overflows.
static inline Digit digitAdd(Digit a, Digit b, Digit* carry) {
Digit result = a + b;
*carry += static_cast<Digit>(result < a);
return result;
}
// Return `left - right`, incrementing `*borrow` if the addition overflows.
static inline Digit digitSub(Digit left, Digit right, Digit* borrow) {
Digit result = left - right;
*borrow += static_cast<Digit>(result > left);
return result;
}
// Compute `a * b`, returning the low half of the result and putting the
// high half in `*high`.
static Digit digitMul(Digit a, Digit b, Digit* high);
// Divide `(high << DigitBits) + low` by `divisor`, returning the quotient
// and storing the remainder in `*remainder`, with the precondition that
// `high < divisor` so that the result fits in a Digit.
static Digit digitDiv(Digit high, Digit low, Digit divisor, Digit* remainder);
// Return `(|x| + |y|) * (resultNegative ? -1 : +1)`.
static BigInt* absoluteAdd(JSContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y, bool resultNegative);
// Return `(|x| - |y|) * (resultNegative ? -1 : +1)`, with the precondition
// that |x| >= |y|.
static BigInt* absoluteSub(JSContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y, bool resultNegative);
// If `|x| < |y|` return -1; if `|x| == |y|` return 0; otherwise return 1.
static int8_t absoluteCompare(BigInt* lhs, BigInt* rhs);
static int8_t compare(BigInt* lhs, double rhs);
static bool equal(BigInt* lhs, double rhs);
static JSLinearString* toStringBasePowerOfTwo(JSContext* cx, Handle<BigInt*>,
unsigned radix);
static JSLinearString* toStringGeneric(JSContext* cx, Handle<BigInt*>,
unsigned radix);
static BigInt* trimHighZeroDigits(JSContext* cx, Handle<BigInt*> x);
static BigInt* destructivelyTrimHighZeroDigits(JSContext* cx,
Handle<BigInt*> x);
friend struct JSStructuredCloneReader;
friend struct JSStructuredCloneWriter;
template <js::XDRMode mode>
friend js::XDRResult js::XDRBigInt(js::XDRState<mode>* xdr,
MutableHandle<BigInt*> bi);
BigInt() = delete;
BigInt(const BigInt& other) = delete;
void operator=(const BigInt& other) = delete;
};
static_assert(
sizeof(BigInt) >= js::gc::MinCellSize,
"sizeof(BigInt) must be greater than the minimum allocation size");
static_assert(
sizeof(BigInt) == js::gc::MinCellSize,
"sizeof(BigInt) intended to be the same as the minimum allocation size");
} // namespace JS
namespace js {
extern JSAtom* BigIntToAtom(JSContext* cx, JS::BigInt* bi);
extern JSAtom* BigIntToAtom(JSContext* cx, JS::HandleBigInt bi);
extern JS::BigInt* NumberToBigInt(JSContext* cx, double d);
// Convert a string to a BigInt, returning nullptr if parsing fails.
// Parse a BigInt from a string, using the method specified for StringToBigInt.
// Used by the BigInt constructor among other places.
extern JS::Result<JS::BigInt*, JS::OOM&> StringToBigInt(
JSContext* cx, JS::Handle<JSString*> str, uint8_t radix);
JSContext* cx, JS::Handle<JSString*> str);
// Same.
extern JS::BigInt* StringToBigInt(JSContext* cx,
const mozilla::Range<const char16_t>& chars);
// Parse a BigInt from an already-validated numeric literal. Used by the
// parser. Can only fail in out-of-memory situations.
extern JS::BigInt* ParseBigIntLiteral(
JSContext* cx, const mozilla::Range<const char16_t>& chars);
extern JS::BigInt* ToBigInt(JSContext* cx, JS::Handle<JS::Value> v);

View File

@ -80,7 +80,7 @@ JS_PUBLIC_API const char* JS::detail::InitWithFailureDiagnostic(
MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
"must call JS_Init once before any JSAPI operation except "
"JS_SetICUMemoryFunctions or JS::SetGMPMemoryFunctions");
"JS_SetICUMemoryFunctions");
MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
"how do we have live runtimes before JS_Init?");
@ -146,10 +146,6 @@ JS_PUBLIC_API const char* JS::detail::InitWithFailureDiagnostic(
RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
#endif
#ifdef ENABLE_BIGINT
JS::BigInt::init();
#endif
libraryInitState = InitState::Running;
return nullptr;
}

View File

@ -1081,7 +1081,8 @@ static JSAtom* ToAtomSlow(
}
#ifdef ENABLE_BIGINT
if (v.isBigInt()) {
JSAtom* atom = BigIntToAtom(cx, v.toBigInt());
RootedBigInt i(cx, v.toBigInt());
JSAtom* atom = BigIntToAtom(cx, i);
if (!allowGC && !atom) {
cx->recoverFromOutOfMemory();
}

View File

@ -2169,7 +2169,8 @@ JSString* js::ToStringSlow(
if (!allowGC) {
return nullptr;
}
str = BigInt::toString(cx, v.toBigInt(), 10);
RootedBigInt i(cx, v.toBigInt());
str = BigInt::toString(cx, i, 10);
}
#endif
else {

View File

@ -310,7 +310,7 @@ struct SCOutput {
MOZ_MUST_USE bool writeChars(const char16_t* p, size_t nchars);
template <class T>
MOZ_MUST_USE bool writeArray(const T* p, size_t nbytes);
MOZ_MUST_USE bool writeArray(const T* p, size_t nelems);
void setCallbacks(const JSStructuredCloneCallbacks* callbacks, void* closure,
OwnTransferablePolicy policy) {
@ -1143,24 +1143,18 @@ bool JSStructuredCloneWriter::writeString(uint32_t tag, JSString* str) {
#ifdef ENABLE_BIGINT
bool JSStructuredCloneWriter::writeBigInt(uint32_t tag, BigInt* bi) {
bool signBit = bi->sign() < 1;
size_t length = BigInt::byteLength(bi);
bool signBit = bi->isNegative();
size_t length = bi->digitLength();
// The length must fit in 31 bits to leave room for a sign bit.
if (length > size_t(INT32_MAX)) {
return false;
}
uint32_t lengthAndSign = length | (static_cast<uint32_t>(signBit) << 31);
js::UniquePtr<uint8_t> buf(js_pod_malloc<uint8_t>(length));
if (!buf) {
return false;
}
BigInt::writeBytes(bi, RangedPtr<uint8_t>(buf.get(), length));
if (!out.writePair(tag, lengthAndSign)) {
return false;
}
return out.writeBytes(buf.get(), length);
return out.writeArray(bi->digits().data(), length);
}
#endif
@ -2070,22 +2064,19 @@ JSString* JSStructuredCloneReader::readString(uint32_t data) {
#ifdef ENABLE_BIGINT
BigInt* JSStructuredCloneReader::readBigInt(uint32_t data) {
size_t nbytes = data & JS_BITMASK(31);
size_t length = data & JS_BITMASK(31);
bool isNegative = data & (1 << 31);
if (nbytes == 0) {
return BigInt::create(context());
if (length == 0) {
return BigInt::zero(context());
}
UniquePtr<uint8_t> buf(js_pod_malloc<uint8_t>(nbytes));
if (!buf) {
BigInt* result = BigInt::createUninitialized(context(), length, isNegative);
if (!result) {
return nullptr;
}
if (!in.readBytes(buf.get(), nbytes)) {
if (!in.readArray(result->digits().data(), length)) {
return nullptr;
}
return BigInt::createFromBytes(context(), isNegative ? -1 : 1, buf.get(),
nbytes);
return result;
}
#endif

View File

@ -385,40 +385,6 @@ NS_IMPL_ISUPPORTS(OggReporter, nsIMemoryReporter)
CountingAllocatorBase<OggReporter>::AmountType
CountingAllocatorBase<OggReporter>::sAmount(0);
#ifdef ENABLE_BIGINT
class GMPReporter final : public nsIMemoryReporter,
public CountingAllocatorBase<GMPReporter> {
public:
NS_DECL_ISUPPORTS
static void* Alloc(size_t size) { return CountingMalloc(size); }
static void* Realloc(void* ptr, size_t oldSize, size_t newSize) {
return CountingRealloc(ptr, newSize);
}
static void Free(void* ptr, size_t size) { return CountingFree(ptr); }
private:
NS_IMETHOD
CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
bool aAnonymize) override {
MOZ_COLLECT_REPORT(
"explicit/gmp", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
"Memory allocated through libgmp for BigInt arithmetic.");
return NS_OK;
}
~GMPReporter() {}
};
NS_IMPL_ISUPPORTS(GMPReporter, nsIMemoryReporter)
/* static */ template <>
Atomic<size_t> CountingAllocatorBase<GMPReporter>::sAmount(0);
#endif // ENABLE_BIGINT
static bool sInitializedJS = false;
// Note that on OSX, aBinDirectory will point to .app/Contents/Resources/browser
@ -611,11 +577,6 @@ NS_InitXPCOM2(nsIServiceManager** aResult, nsIFile* aBinDirectory,
OggReporter::CountingMalloc, OggReporter::CountingCalloc,
OggReporter::CountingRealloc, OggReporter::CountingFree);
#ifdef ENABLE_BIGINT
// And for libgmp.
mozilla::SetGMPMemoryFunctions();
#endif
// Initialize the JS engine.
const char* jsInitFailureReason = JS_InitWithFailureDiagnostic();
if (jsInitFailureReason) {
@ -758,17 +719,6 @@ void SetICUMemoryFunctions() {
}
}
#ifdef ENABLE_BIGINT
void SetGMPMemoryFunctions() {
static bool sGMPReporterInitialized = false;
if (!sGMPReporterInitialized) {
JS::SetGMPMemoryFunctions(GMPReporter::Alloc, GMPReporter::Realloc,
GMPReporter::Free);
sGMPReporterInitialized = true;
}
}
#endif
nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
// Make sure the hang monitor is enabled for shutdown.
BackgroundHangMonitor().NotifyActivity();

View File

@ -40,10 +40,6 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr);
void SetICUMemoryFunctions();
#ifdef ENABLE_BIGINT
void SetGMPMemoryFunctions();
#endif
/**
* C++ namespaced version of NS_LogTerm.
*/