diff options
author | janekptacijarabaci <janekptacijarabaci@seznam.cz> | 2018-07-06 15:53:52 +0200 |
---|---|---|
committer | janekptacijarabaci <janekptacijarabaci@seznam.cz> | 2018-07-06 15:53:52 +0200 |
commit | 941e54654eabed0a3568f7fefe424a45aa02eddb (patch) | |
tree | 49aa02b174c428962d99142d8061267bfcd79e69 /js | |
parent | ad9ee72dcd7981bc47b3844a224d69fadfdfd8ef (diff) | |
parent | 0daa12376295d5d796256a116eb2a348a3a9273f (diff) | |
download | UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar.gz UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar.lz UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar.xz UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.zip |
Merge branch 'master' of https://github.com/MoonchildProductions/UXP into _testBranch_test_1
Diffstat (limited to 'js')
87 files changed, 1670 insertions, 1849 deletions
diff --git a/js/ipc/JavaScriptParent.cpp b/js/ipc/JavaScriptParent.cpp index 7fe92d662..ca0a0bd21 100644 --- a/js/ipc/JavaScriptParent.cpp +++ b/js/ipc/JavaScriptParent.cpp @@ -16,6 +16,7 @@ #include "xpcprivate.h" #include "mozilla/Casting.h" #include "mozilla/Telemetry.h" +#include "mozilla/Unused.h" #include "nsAutoPtr.h" using namespace js; diff --git a/js/public/Date.h b/js/public/Date.h index cba0ea875..cab36a3de 100644 --- a/js/public/Date.h +++ b/js/public/Date.h @@ -134,6 +134,14 @@ NewDateObject(JSContext* cx, ClippedTime time); JS_PUBLIC_API(double) MakeDate(double year, unsigned month, unsigned day); +// Year is a year, month is 0-11, day is 1-based, and time is in milliseconds. +// The return value is a number of milliseconds since the epoch. +// +// Consistent with the MakeDate algorithm defined in ECMAScript, this value is +// *not* clipped! Use JS::TimeClip if you need a clipped date. +JS_PUBLIC_API(double) +MakeDate(double year, unsigned month, unsigned day, double time); + // Takes an integer number of milliseconds since the epoch and returns the // year. Can return NaN, and will do so if NaN is passed in. JS_PUBLIC_API(double) diff --git a/js/public/StructuredClone.h b/js/public/StructuredClone.h index c48975cb9..ebff84387 100644 --- a/js/public/StructuredClone.h +++ b/js/public/StructuredClone.h @@ -9,6 +9,7 @@ #include "mozilla/Attributes.h" #include "mozilla/BufferList.h" +#include "mozilla/Move.h" #include <stdint.h> @@ -29,7 +30,35 @@ namespace JS { enum class StructuredCloneScope : uint32_t { SameProcessSameThread, SameProcessDifferentThread, - DifferentProcess + + /** + * When writing, this means we're writing for an audience in a different + * process. Produce serialized data that can be sent to other processes, + * bitwise copied, or even stored as bytes in a database and read by later + * versions of Firefox years from now. The HTML5 spec refers to this as + * "ForStorage" as in StructuredSerializeForStorage, though we use + * DifferentProcess for IPC as well as storage. + * + * Transferable objects are limited to ArrayBuffers, whose contents are + * copied into the serialized data (rather than just writing a pointer). + */ + DifferentProcess, + + /** + * Handle a backwards-compatibility case with IndexedDB (bug 1434308): when + * reading, this means to treat legacy SameProcessSameThread data as if it + * were DifferentProcess. + * + * Do not use this for writing; use DifferentProcess instead. + */ + DifferentProcessForIndexedDB, + + /** + * Existing code wants to be able to create an uninitialized + * JSStructuredCloneData without knowing the scope, then populate it with + * data (at which point the scope *is* known.) + */ + Unassigned }; enum TransferableOwnership { @@ -89,6 +118,10 @@ class CloneDataPolicy } /* namespace JS */ +namespace js { +template <typename T, typename AllocPolicy> struct BufferIterator; +} + /** * Read structured data from the reader r. This hook is used to read a value * previously serialized by a call to the WriteStructuredCloneOp hook. @@ -188,49 +221,152 @@ enum OwnTransferablePolicy { NoTransferables }; -class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) : - public mozilla::BufferList<js::SystemAllocPolicy> -{ - typedef js::SystemAllocPolicy AllocPolicy; - typedef mozilla::BufferList<js::SystemAllocPolicy> BufferList; +/** + * JSStructuredCloneData represents structured clone data together with the + * information needed to read/write/transfer/free the records within it, in the + * form of a set of callbacks. + */ +class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) { + public: + using BufferList = mozilla::BufferList<js::SystemAllocPolicy>; + using Iterator = BufferList::IterImpl; - static const size_t kInitialSize = 0; - static const size_t kInitialCapacity = 4096; + private: static const size_t kStandardCapacity = 4096; + BufferList bufList_; + + // The (address space, thread) scope within which this clone is valid. Note + // that this must be either set during construction, or start out as + // Unassigned and transition once to something else. + JS::StructuredCloneScope scope_; + const JSStructuredCloneCallbacks* callbacks_; void* closure_; OwnTransferablePolicy ownTransferables_; - void setOptionalCallbacks(const JSStructuredCloneCallbacks* callbacks, - void* closure, - OwnTransferablePolicy policy) { - callbacks_ = callbacks; - closure_ = closure; - ownTransferables_ = policy; - } - friend struct JSStructuredCloneWriter; friend class JS_PUBLIC_API(JSAutoStructuredCloneBuffer); + template <typename T, typename AllocPolicy> friend struct js::BufferIterator; -public: - explicit JSStructuredCloneData(AllocPolicy aAP = AllocPolicy()) - : BufferList(kInitialSize, kInitialCapacity, kStandardCapacity, aAP) + public: + // The constructor must be infallible but SystemAllocPolicy is not, so both + // the initial size and initial capacity of the BufferList must be zero. + explicit JSStructuredCloneData(JS::StructuredCloneScope aScope) + : bufList_(0, 0, kStandardCapacity, js::SystemAllocPolicy()) + , scope_(aScope) , callbacks_(nullptr) , closure_(nullptr) , ownTransferables_(OwnTransferablePolicy::NoTransferables) {} - MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers) - : BufferList(Move(buffers)) + + // Steal the raw data from a BufferList. In this case, we don't know the + // scope and none of the callback info is assigned yet. + JSStructuredCloneData(BufferList&& buffers, JS::StructuredCloneScope aScope) + : bufList_(mozilla::Move(buffers)) + , scope_(aScope) , callbacks_(nullptr) , closure_(nullptr) , ownTransferables_(OwnTransferablePolicy::NoTransferables) {} + MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers) + : JSStructuredCloneData(mozilla::Move(buffers), JS::StructuredCloneScope::Unassigned) + {} JSStructuredCloneData(JSStructuredCloneData&& other) = default; JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default; - ~JSStructuredCloneData(); + ~JSStructuredCloneData() { discardTransferables(); } + + void setCallbacks(const JSStructuredCloneCallbacks* callbacks, + void* closure, + OwnTransferablePolicy policy) + { + callbacks_ = callbacks; + closure_ = closure; + ownTransferables_ = policy; + } + + JS::StructuredCloneScope scope() const { return scope_; } + + void initScope(JS::StructuredCloneScope aScope) { + MOZ_ASSERT(Size() == 0, "initScope() of nonempty JSStructuredCloneData"); + if (scope_ != JS::StructuredCloneScope::Unassigned) + MOZ_ASSERT(scope_ == aScope, "Cannot change scope after it has been initialized"); + scope_ = aScope; + } + + size_t Size() const { return bufList_.Size(); } + + const Iterator Start() const { return bufList_.Iter(); } + + bool Advance(Iterator& iter, size_t distance) const { + return iter.AdvanceAcrossSegments(bufList_, distance); + } + + bool ReadBytes(Iterator& iter, char* buffer, size_t size) const { + return bufList_.ReadBytes(iter, buffer, size); + } + + // Append new data to the end of the buffer. + bool AppendBytes(const char* data, size_t size) { + MOZ_ASSERT(scope_ != JS::StructuredCloneScope::Unassigned); + return bufList_.WriteBytes(data, size); + } - using BufferList::BufferList; + // Update data stored within the existing buffer. There must be at least + // 'size' bytes between the position of 'iter' and the end of the buffer. + bool UpdateBytes(Iterator& iter, const char* data, size_t size) const { + MOZ_ASSERT(scope_ != JS::StructuredCloneScope::Unassigned); + while (size > 0) { + size_t remaining = iter.RemainingInSegment(); + size_t nbytes = std::min(remaining, size); + memcpy(iter.Data(), data, nbytes); + data += nbytes; + size -= nbytes; + iter.Advance(bufList_, nbytes); + } + return true; + } + + void Clear() { + discardTransferables(); + bufList_.Clear(); + } + + // Return a new read-only JSStructuredCloneData that "borrows" the contents + // of |this|. Its lifetime should not exceed the donor's. This is only + // allowed for DifferentProcess clones, so finalization of the borrowing + // clone will do nothing. + JSStructuredCloneData Borrow(Iterator& iter, size_t size, bool* success) const + { + MOZ_ASSERT(scope_ == JS::StructuredCloneScope::DifferentProcess); + return JSStructuredCloneData(bufList_.Borrow<js::SystemAllocPolicy>(iter, size, success), + scope_); + } + + // Iterate over all contained data, one BufferList segment's worth at a + // time, and invoke the given FunctionToApply with the data pointer and + // size. The function should return a bool value, and this loop will exit + // with false if the function ever returns false. + template <typename FunctionToApply> + bool ForEachDataChunk(FunctionToApply&& function) const { + Iterator iter = bufList_.Iter(); + while (!iter.Done()) { + if (!function(iter.Data(), iter.RemainingInSegment())) + return false; + iter.Advance(bufList_, iter.RemainingInSegment()); + } + return true; + } + + // Append the entire contents of other's bufList_ to our own. + bool Append(const JSStructuredCloneData& other) { + MOZ_ASSERT(scope_ == other.scope_); + return other.ForEachDataChunk([&](const char* data, size_t size) { + return AppendBytes(data, size); + }); + } + + void discardTransferables(); }; /** Note: if the *data contains transferable objects, it can be read only once. */ @@ -254,18 +390,29 @@ JS_PUBLIC_API(bool) JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp, const JSStructuredCloneCallbacks* optionalCallbacks, void* closure); -/** RAII sugar for JS_WriteStructuredClone. */ +/** + * The C-style API calls to read and write structured clones are fragile -- + * they rely on the caller to properly handle ownership of the clone data, and + * the handling of the input data as well as the interpretation of the contents + * of the clone buffer are dependent on the callbacks passed in. If you + * serialize and deserialize with different callbacks, the results are + * questionable. + * + * JSAutoStructuredCloneBuffer wraps things up in an RAII class for data + * management, and uses the same callbacks for both writing and reading + * (serializing and deserializing). + */ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { const JS::StructuredCloneScope scope_; JSStructuredCloneData data_; uint32_t version_; public: - JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope, + JSAutoStructuredCloneBuffer(JS::StructuredCloneScope aScope, const JSStructuredCloneCallbacks* callbacks, void* closure) - : scope_(scope), version_(JS_STRUCTURED_CLONE_VERSION) + : scope_(aScope), data_(aScope), version_(JS_STRUCTURED_CLONE_VERSION) { - data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables); + data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables); } JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other); @@ -276,11 +423,9 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { JSStructuredCloneData& data() { return data_; } bool empty() const { return !data_.Size(); } - void clear(const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr); + void clear(); - /** Copy some memory. It will be automatically freed by the destructor. */ - bool copy(const JSStructuredCloneData& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION, - const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr); + JS::StructuredCloneScope scope() const { return scope_; } /** * Adopt some memory. It will be automatically freed by the destructor. diff --git a/js/public/Value.h b/js/public/Value.h index a40e65c83..01666ed4e 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -140,12 +140,16 @@ static_assert(sizeof(JSValueShiftedTag) == sizeof(uint64_t), #define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_CLEAR | (type))) +#define JSVAL_RAW64_UNDEFINED (uint64_t(JSVAL_TAG_UNDEFINED) << 32) + #define JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET JSVAL_TAG_OBJECT #define JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET JSVAL_TAG_INT32 #define JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET JSVAL_TAG_STRING #elif defined(JS_PUNBOX64) +#define JSVAL_RAW64_UNDEFINED (uint64_t(JSVAL_TAG_UNDEFINED) << JSVAL_TAG_SHIFT) + #define JSVAL_PAYLOAD_MASK 0x00007FFFFFFFFFFFLL #define JSVAL_TAG_MASK 0xFFFF800000000000LL #define JSVAL_TYPE_TO_TAG(type) ((JSValueTag)(JSVAL_TAG_MAX_DOUBLE | (type))) @@ -817,7 +821,7 @@ class MOZ_NON_PARAM alignas(8) Value double asDouble; void* asPtr; - layout() = default; + layout() : asBits(JSVAL_RAW64_UNDEFINED) {} explicit constexpr layout(uint64_t bits) : asBits(bits) {} explicit constexpr layout(double d) : asDouble(d) {} } data; @@ -843,7 +847,7 @@ class MOZ_NON_PARAM alignas(8) Value size_t asWord; uintptr_t asUIntPtr; - layout() = default; + layout() : asBits(JSVAL_RAW64_UNDEFINED) {} explicit constexpr layout(uint64_t bits) : asBits(bits) {} explicit constexpr layout(double d) : asDouble(d) {} } data; @@ -871,7 +875,7 @@ class MOZ_NON_PARAM alignas(8) Value double asDouble; void* asPtr; - layout() = default; + layout() : asBits(JSVAL_RAW64_UNDEFINED) {} explicit constexpr layout(uint64_t bits) : asBits(bits) {} explicit constexpr layout(double d) : asDouble(d) {} } data; @@ -895,7 +899,7 @@ class MOZ_NON_PARAM alignas(8) Value size_t asWord; uintptr_t asUIntPtr; - layout() = default; + layout() : asBits(JSVAL_RAW64_UNDEFINED) {} explicit constexpr layout(uint64_t bits) : asBits(bits) {} explicit constexpr layout(double d) : asDouble(d) {} } data; @@ -948,8 +952,51 @@ class MOZ_NON_PARAM alignas(8) Value } } JS_HAZ_GC_POINTER; +/** + * This is a null-constructible structure that can convert to and from + * a Value, allowing UninitializedValue to be stored in unions. + */ +struct MOZ_NON_PARAM alignas(8) UninitializedValue +{ + private: + uint64_t bits; + + public: + UninitializedValue() = default; + UninitializedValue(const UninitializedValue&) = default; + MOZ_IMPLICIT UninitializedValue(const Value& val) : bits(val.asRawBits()) {} + + inline uint64_t asRawBits() const { + return bits; + } + + inline Value& asValueRef() { + return *reinterpret_cast<Value*>(this); + } + inline const Value& asValueRef() const { + return *reinterpret_cast<const Value*>(this); + } + + inline operator Value&() { + return asValueRef(); + } + inline operator Value const&() const { + return asValueRef(); + } + inline operator Value() const { + return asValueRef(); + } + + inline void operator=(Value const& other) { + asValueRef() = other; + } +}; + static_assert(sizeof(Value) == 8, "Value size must leave three tag bits, be a binary power, and is ubiquitously depended upon everywhere"); +static_assert(sizeof(UninitializedValue) == sizeof(Value), "Value and UninitializedValue must be the same size"); +static_assert(alignof(UninitializedValue) == alignof(Value), "Value and UninitializedValue must have same alignment"); + inline bool IsOptimizedPlaceholderMagicValue(const Value& v) { diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 3be6a6781..b007954b1 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -147,13 +147,6 @@ ifeq ($(OS_ARCH),AIX) CFLAGS += -qsuppress=1540-1281 -qsuppress=1540-1608 CXXFLAGS += -qsuppress=1540-1281 -qsuppress=1540-1608 endif -ifeq ($(OS_ARCH),HP-UX) -# Suppress warnings from aCC -# 3055: anonymous unions declaring types -# 4189: offsetof() on non-POD types -CFLAGS += +W3055,4189 -CXXFLAGS += +W3055,4189 -endif endif ifeq ($(OS_ARCH),SunOS) ifeq ($(TARGET_CPU),sparc) diff --git a/js/src/aclocal.m4 b/js/src/aclocal.m4 index 627837a41..abbbd4873 100644 --- a/js/src/aclocal.m4 +++ b/js/src/aclocal.m4 @@ -25,7 +25,6 @@ builtin(include, ../../build/autoconf/zlib.m4)dnl builtin(include, ../../build/autoconf/icu.m4)dnl builtin(include, ../../build/autoconf/clang-plugin.m4)dnl builtin(include, ../../build/autoconf/alloc.m4)dnl -builtin(include, ../../build/autoconf/jemalloc.m4)dnl builtin(include, ../../build/autoconf/sanitize.m4)dnl builtin(include, ../../build/autoconf/ios.m4)dnl diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 45f90a7b8..30e6fb35f 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -711,13 +711,14 @@ function CreateArrayIterator(obj, kind) { // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%.next function ArrayIteratorNext() { // Step 1-3. - if (!IsObject(this) || !IsArrayIterator(this)) { + var obj; + if (!IsObject(this) || (obj = GuardToArrayIterator(this)) === null) { return callFunction(CallArrayIteratorMethodIfWrapped, this, "ArrayIteratorNext"); } // Step 4. - var a = UnsafeGetReservedSlot(this, ITERATOR_SLOT_TARGET); + var a = UnsafeGetReservedSlot(obj, ITERATOR_SLOT_TARGET); var result = { value: undefined, done: false }; // Step 5. @@ -728,10 +729,10 @@ function ArrayIteratorNext() { // Step 6. // The index might not be an integer, so we have to do a generic get here. - var index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX); + var index = UnsafeGetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX); // Step 7. - var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); + var itemKind = UnsafeGetInt32FromReservedSlot(obj, ITERATOR_SLOT_ITEM_KIND); // Step 8-9. var len = IsPossiblyWrappedTypedArray(a) @@ -740,13 +741,13 @@ function ArrayIteratorNext() { // Step 10. if (index >= len) { - UnsafeSetReservedSlot(this, ITERATOR_SLOT_TARGET, null); + UnsafeSetReservedSlot(obj, ITERATOR_SLOT_TARGET, null); result.done = true; return result; } // Step 11. - UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1); + UnsafeSetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX, index + 1); // Step 16. if (itemKind === ITEM_KIND_VALUE) { @@ -900,11 +901,7 @@ function ArrayToLocaleString(locales, options) { if (firstElement === undefined || firstElement === null) { R = ""; } else { -#if EXPOSE_INTL_API R = ToString(callContentFunction(firstElement.toLocaleString, firstElement, locales, options)); -#else - R = ToString(callContentFunction(firstElement.toLocaleString, firstElement)); -#endif } // Step 3 (reordered). @@ -919,11 +916,7 @@ function ArrayToLocaleString(locales, options) { // Steps 9.a, 9.c-e. R += separator; if (!(nextElement === undefined || nextElement === null)) { -#if EXPOSE_INTL_API R += ToString(callContentFunction(nextElement.toLocaleString, nextElement, locales, options)); -#else - R += ToString(callContentFunction(nextElement.toLocaleString, nextElement)); -#endif } } diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 3a20c487b..71f7b58d4 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -23,7 +23,6 @@ #include "jsobj.h" #include "builtin/IntlTimeZoneData.h" -#if ENABLE_INTL_API #include "unicode/ucal.h" #include "unicode/ucol.h" #include "unicode/udat.h" @@ -32,7 +31,6 @@ #include "unicode/unum.h" #include "unicode/unumsys.h" #include "unicode/ustring.h" -#endif #include "vm/DateTime.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" @@ -60,512 +58,6 @@ using mozilla::PodCopy; * http://userguide.icu-project.org/design#TOC-Error-Handling */ - -/******************** ICU stubs ********************/ - -#if !ENABLE_INTL_API - -/* - * When the Internationalization API isn't enabled, we also shouldn't link - * against ICU. However, we still want to compile this code in order to prevent - * bit rot. The following stub implementations for ICU functions make this - * possible. The functions using them should never be called, so they assert - * and return error codes. Signatures adapted from ICU header files locid.h, - * numsys.h, ucal.h, ucol.h, udat.h, udatpg.h, uenum.h, unum.h; see the ICU - * directory for license. - */ - -typedef bool UBool; -typedef char16_t UChar; -typedef double UDate; - -enum UErrorCode { - U_ZERO_ERROR, - U_BUFFER_OVERFLOW_ERROR, -}; - -static inline UBool -U_FAILURE(UErrorCode code) -{ - MOZ_CRASH("U_FAILURE: Intl API disabled"); -} - -inline const UChar* -Char16ToUChar(const char16_t* chars) -{ - MOZ_CRASH("Char16ToUChar: Intl API disabled"); -} - -inline UChar* -Char16ToUChar(char16_t* chars) -{ - MOZ_CRASH("Char16ToUChar: Intl API disabled"); -} - -static int32_t -u_strlen(const UChar* s) -{ - MOZ_CRASH("u_strlen: Intl API disabled"); -} - -struct UEnumeration; - -static int32_t -uenum_count(UEnumeration* en, UErrorCode* status) -{ - MOZ_CRASH("uenum_count: Intl API disabled"); -} - -static const char* -uenum_next(UEnumeration* en, int32_t* resultLength, UErrorCode* status) -{ - MOZ_CRASH("uenum_next: Intl API disabled"); -} - -static void -uenum_close(UEnumeration* en) -{ - MOZ_CRASH("uenum_close: Intl API disabled"); -} - -struct UCollator; - -enum UColAttribute { - UCOL_ALTERNATE_HANDLING, - UCOL_CASE_FIRST, - UCOL_CASE_LEVEL, - UCOL_NORMALIZATION_MODE, - UCOL_STRENGTH, - UCOL_NUMERIC_COLLATION, -}; - -enum UColAttributeValue { - UCOL_DEFAULT = -1, - UCOL_PRIMARY = 0, - UCOL_SECONDARY = 1, - UCOL_TERTIARY = 2, - UCOL_OFF = 16, - UCOL_ON = 17, - UCOL_SHIFTED = 20, - UCOL_LOWER_FIRST = 24, - UCOL_UPPER_FIRST = 25, -}; - -enum UCollationResult { - UCOL_EQUAL = 0, - UCOL_GREATER = 1, - UCOL_LESS = -1 -}; - -static int32_t -ucol_countAvailable() -{ - MOZ_CRASH("ucol_countAvailable: Intl API disabled"); -} - -static const char* -ucol_getAvailable(int32_t localeIndex) -{ - MOZ_CRASH("ucol_getAvailable: Intl API disabled"); -} - -static UCollator* -ucol_open(const char* loc, UErrorCode* status) -{ - MOZ_CRASH("ucol_open: Intl API disabled"); -} - -static void -ucol_setAttribute(UCollator* coll, UColAttribute attr, UColAttributeValue value, UErrorCode* status) -{ - MOZ_CRASH("ucol_setAttribute: Intl API disabled"); -} - -static UCollationResult -ucol_strcoll(const UCollator* coll, const UChar* source, int32_t sourceLength, - const UChar* target, int32_t targetLength) -{ - MOZ_CRASH("ucol_strcoll: Intl API disabled"); -} - -static void -ucol_close(UCollator* coll) -{ - MOZ_CRASH("ucol_close: Intl API disabled"); -} - -static UEnumeration* -ucol_getKeywordValuesForLocale(const char* key, const char* locale, UBool commonlyUsed, - UErrorCode* status) -{ - MOZ_CRASH("ucol_getKeywordValuesForLocale: Intl API disabled"); -} - -struct UParseError; -struct UFieldPosition; -struct UFieldPositionIterator; -typedef void* UNumberFormat; - -enum UNumberFormatStyle { - UNUM_DECIMAL = 1, - UNUM_CURRENCY, - UNUM_PERCENT, - UNUM_CURRENCY_ISO, - UNUM_CURRENCY_PLURAL, -}; - -enum UNumberFormatRoundingMode { - UNUM_ROUND_HALFUP, -}; - -enum UNumberFormatAttribute { - UNUM_GROUPING_USED, - UNUM_MIN_INTEGER_DIGITS, - UNUM_MAX_FRACTION_DIGITS, - UNUM_MIN_FRACTION_DIGITS, - UNUM_ROUNDING_MODE, - UNUM_SIGNIFICANT_DIGITS_USED, - UNUM_MIN_SIGNIFICANT_DIGITS, - UNUM_MAX_SIGNIFICANT_DIGITS, -}; - -enum UNumberFormatTextAttribute { - UNUM_CURRENCY_CODE, -}; - -static int32_t -unum_countAvailable() -{ - MOZ_CRASH("unum_countAvailable: Intl API disabled"); -} - -static const char* -unum_getAvailable(int32_t localeIndex) -{ - MOZ_CRASH("unum_getAvailable: Intl API disabled"); -} - -static UNumberFormat* -unum_open(UNumberFormatStyle style, const UChar* pattern, int32_t patternLength, - const char* locale, UParseError* parseErr, UErrorCode* status) -{ - MOZ_CRASH("unum_open: Intl API disabled"); -} - -static void -unum_setAttribute(UNumberFormat* fmt, UNumberFormatAttribute attr, int32_t newValue) -{ - MOZ_CRASH("unum_setAttribute: Intl API disabled"); -} - -static int32_t -unum_formatDouble(const UNumberFormat* fmt, double number, UChar* result, - int32_t resultLength, UFieldPosition* pos, UErrorCode* status) -{ - MOZ_CRASH("unum_formatDouble: Intl API disabled"); -} - -static void -unum_close(UNumberFormat* fmt) -{ - MOZ_CRASH("unum_close: Intl API disabled"); -} - -static void -unum_setTextAttribute(UNumberFormat* fmt, UNumberFormatTextAttribute tag, const UChar* newValue, - int32_t newValueLength, UErrorCode* status) -{ - MOZ_CRASH("unum_setTextAttribute: Intl API disabled"); -} - -typedef void* UNumberingSystem; - -static UNumberingSystem* -unumsys_open(const char* locale, UErrorCode* status) -{ - MOZ_CRASH("unumsys_open: Intl API disabled"); -} - -static const char* -unumsys_getName(const UNumberingSystem* unumsys) -{ - MOZ_CRASH("unumsys_getName: Intl API disabled"); -} - -static void -unumsys_close(UNumberingSystem* unumsys) -{ - MOZ_CRASH("unumsys_close: Intl API disabled"); -} - -typedef void* UCalendar; - -enum UCalendarType { - UCAL_TRADITIONAL, - UCAL_DEFAULT = UCAL_TRADITIONAL, - UCAL_GREGORIAN -}; - -enum UCalendarAttribute { - UCAL_FIRST_DAY_OF_WEEK, - UCAL_MINIMAL_DAYS_IN_FIRST_WEEK -}; - -enum UCalendarDaysOfWeek { - UCAL_SUNDAY, - UCAL_MONDAY, - UCAL_TUESDAY, - UCAL_WEDNESDAY, - UCAL_THURSDAY, - UCAL_FRIDAY, - UCAL_SATURDAY -}; - -enum UCalendarWeekdayType { - UCAL_WEEKDAY, - UCAL_WEEKEND, - UCAL_WEEKEND_ONSET, - UCAL_WEEKEND_CEASE -}; - -enum UCalendarDateFields { - UCAL_ERA, - UCAL_YEAR, - UCAL_MONTH, - UCAL_WEEK_OF_YEAR, - UCAL_WEEK_OF_MONTH, - UCAL_DATE, - UCAL_DAY_OF_YEAR, - UCAL_DAY_OF_WEEK, - UCAL_DAY_OF_WEEK_IN_MONTH, - UCAL_AM_PM, - UCAL_HOUR, - UCAL_HOUR_OF_DAY, - UCAL_MINUTE, - UCAL_SECOND, - UCAL_MILLISECOND, - UCAL_ZONE_OFFSET, - UCAL_DST_OFFSET, - UCAL_YEAR_WOY, - UCAL_DOW_LOCAL, - UCAL_EXTENDED_YEAR, - UCAL_JULIAN_DAY, - UCAL_MILLISECONDS_IN_DAY, - UCAL_IS_LEAP_MONTH, - UCAL_FIELD_COUNT, - UCAL_DAY_OF_MONTH = UCAL_DATE -}; - -static UCalendar* -ucal_open(const UChar* zoneID, int32_t len, const char* locale, - UCalendarType type, UErrorCode* status) -{ - MOZ_CRASH("ucal_open: Intl API disabled"); -} - -static const char* -ucal_getType(const UCalendar* cal, UErrorCode* status) -{ - MOZ_CRASH("ucal_getType: Intl API disabled"); -} - -static UEnumeration* -ucal_getKeywordValuesForLocale(const char* key, const char* locale, - UBool commonlyUsed, UErrorCode* status) -{ - MOZ_CRASH("ucal_getKeywordValuesForLocale: Intl API disabled"); -} - -static void -ucal_close(UCalendar* cal) -{ - MOZ_CRASH("ucal_close: Intl API disabled"); -} - -static UCalendarWeekdayType -ucal_getDayOfWeekType(const UCalendar *cal, UCalendarDaysOfWeek dayOfWeek, UErrorCode* status) -{ - MOZ_CRASH("ucal_getDayOfWeekType: Intl API disabled"); -} - -static int32_t -ucal_getAttribute(const UCalendar* cal, - UCalendarAttribute attr) -{ - MOZ_CRASH("ucal_getAttribute: Intl API disabled"); -} - -static int32_t -ucal_get(const UCalendar *cal, UCalendarDateFields field, UErrorCode *status) -{ - MOZ_CRASH("ucal_get: Intl API disabled"); -} - -static UEnumeration* -ucal_openTimeZones(UErrorCode* status) -{ - MOZ_CRASH("ucal_openTimeZones: Intl API disabled"); -} - -static int32_t -ucal_getCanonicalTimeZoneID(const UChar* id, int32_t len, UChar* result, int32_t resultCapacity, - UBool* isSystemID, UErrorCode* status) -{ - MOZ_CRASH("ucal_getCanonicalTimeZoneID: Intl API disabled"); -} - -static int32_t -ucal_getDefaultTimeZone(UChar* result, int32_t resultCapacity, UErrorCode* status) -{ - MOZ_CRASH("ucal_getDefaultTimeZone: Intl API disabled"); -} - -typedef void* UDateTimePatternGenerator; - -static UDateTimePatternGenerator* -udatpg_open(const char* locale, UErrorCode* pErrorCode) -{ - MOZ_CRASH("udatpg_open: Intl API disabled"); -} - -static int32_t -udatpg_getBestPattern(UDateTimePatternGenerator* dtpg, const UChar* skeleton, - int32_t length, UChar* bestPattern, int32_t capacity, - UErrorCode* pErrorCode) -{ - MOZ_CRASH("udatpg_getBestPattern: Intl API disabled"); -} - -static void -udatpg_close(UDateTimePatternGenerator* dtpg) -{ - MOZ_CRASH("udatpg_close: Intl API disabled"); -} - -typedef void* UCalendar; -typedef void* UDateFormat; - -enum UDateFormatField { - UDAT_ERA_FIELD = 0, - UDAT_YEAR_FIELD = 1, - UDAT_MONTH_FIELD = 2, - UDAT_DATE_FIELD = 3, - UDAT_HOUR_OF_DAY1_FIELD = 4, - UDAT_HOUR_OF_DAY0_FIELD = 5, - UDAT_MINUTE_FIELD = 6, - UDAT_SECOND_FIELD = 7, - UDAT_FRACTIONAL_SECOND_FIELD = 8, - UDAT_DAY_OF_WEEK_FIELD = 9, - UDAT_DAY_OF_YEAR_FIELD = 10, - UDAT_DAY_OF_WEEK_IN_MONTH_FIELD = 11, - UDAT_WEEK_OF_YEAR_FIELD = 12, - UDAT_WEEK_OF_MONTH_FIELD = 13, - UDAT_AM_PM_FIELD = 14, - UDAT_HOUR1_FIELD = 15, - UDAT_HOUR0_FIELD = 16, - UDAT_TIMEZONE_FIELD = 17, - UDAT_YEAR_WOY_FIELD = 18, - UDAT_DOW_LOCAL_FIELD = 19, - UDAT_EXTENDED_YEAR_FIELD = 20, - UDAT_JULIAN_DAY_FIELD = 21, - UDAT_MILLISECONDS_IN_DAY_FIELD = 22, - UDAT_TIMEZONE_RFC_FIELD = 23, - UDAT_TIMEZONE_GENERIC_FIELD = 24, - UDAT_STANDALONE_DAY_FIELD = 25, - UDAT_STANDALONE_MONTH_FIELD = 26, - UDAT_QUARTER_FIELD = 27, - UDAT_STANDALONE_QUARTER_FIELD = 28, - UDAT_TIMEZONE_SPECIAL_FIELD = 29, - UDAT_YEAR_NAME_FIELD = 30, - UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD = 31, - UDAT_TIMEZONE_ISO_FIELD = 32, - UDAT_TIMEZONE_ISO_LOCAL_FIELD = 33, - UDAT_RELATED_YEAR_FIELD = 34, - UDAT_AM_PM_MIDNIGHT_NOON_FIELD = 35, - UDAT_FLEXIBLE_DAY_PERIOD_FIELD = 36, - UDAT_TIME_SEPARATOR_FIELD = 37, - UDAT_FIELD_COUNT = 38 -}; - -enum UDateFormatStyle { - UDAT_PATTERN = -2, - UDAT_IGNORE = UDAT_PATTERN -}; - -static int32_t -udat_countAvailable() -{ - MOZ_CRASH("udat_countAvailable: Intl API disabled"); -} - -static const char* -udat_getAvailable(int32_t localeIndex) -{ - MOZ_CRASH("udat_getAvailable: Intl API disabled"); -} - -static UDateFormat* -udat_open(UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const char* locale, - const UChar* tzID, int32_t tzIDLength, const UChar* pattern, - int32_t patternLength, UErrorCode* status) -{ - MOZ_CRASH("udat_open: Intl API disabled"); -} - -static const UCalendar* -udat_getCalendar(const UDateFormat* fmt) -{ - MOZ_CRASH("udat_getCalendar: Intl API disabled"); -} - -static void -ucal_setGregorianChange(UCalendar* cal, UDate date, UErrorCode* pErrorCode) -{ - MOZ_CRASH("ucal_setGregorianChange: Intl API disabled"); -} - -static int32_t -udat_format(const UDateFormat* format, UDate dateToFormat, UChar* result, - int32_t resultLength, UFieldPosition* position, UErrorCode* status) -{ - MOZ_CRASH("udat_format: Intl API disabled"); -} - -static int32_t -udat_formatForFields(const UDateFormat* format, UDate dateToFormat, - UChar* result, int32_t resultLength, UFieldPositionIterator* fpositer, - UErrorCode* status) -{ - MOZ_CRASH("udat_formatForFields: Intl API disabled"); -} - -static UFieldPositionIterator* -ufieldpositer_open(UErrorCode* status) -{ - MOZ_CRASH("ufieldpositer_open: Intl API disabled"); -} - -static void -ufieldpositer_close(UFieldPositionIterator* fpositer) -{ - MOZ_CRASH("ufieldpositer_close: Intl API disabled"); -} - -static int32_t -ufieldpositer_next(UFieldPositionIterator* fpositer, int32_t* beginIndex, int32_t* endIndex) -{ - MOZ_CRASH("ufieldpositer_next: Intl API disabled"); -} - -static void -udat_close(UDateFormat* format) -{ - MOZ_CRASH("udat_close: Intl API disabled"); -} - -#endif - - /******************** Common to Intl constructors ********************/ static bool @@ -615,7 +107,6 @@ intl_availableLocales(JSContext* cx, CountAvailable countAvailable, if (!locales) return false; -#if ENABLE_INTL_API uint32_t count = countAvailable(); RootedValue t(cx, BooleanValue(true)); for (uint32_t i = 0; i < count; i++) { @@ -635,7 +126,7 @@ intl_availableLocales(JSContext* cx, CountAvailable countAvailable, return false; } } -#endif + result.setObject(*locales); return true; } @@ -2921,6 +2412,296 @@ js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp) return true; } +template<size_t N> +inline bool +MatchPart(const char** pattern, const char (&part)[N]) +{ + if (strncmp(*pattern, part, N - 1)) + return false; + + *pattern += N - 1; + return true; +} + +bool +js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 3); + // 1. Assert: locale is a string. + MOZ_ASSERT(args[0].isString()); + // 2. Assert: style is a string. + MOZ_ASSERT(args[1].isString()); + // 3. Assert: keys is an Array. + MOZ_ASSERT(args[2].isObject()); + + JSAutoByteString locale(cx, args[0].toString()); + if (!locale) + return false; + + JSAutoByteString style(cx, args[1].toString()); + if (!style) + return false; + + RootedArrayObject keys(cx, &args[2].toObject().as<ArrayObject>()); + if (!keys) + return false; + + // 4. Let result be ArrayCreate(0). + RootedArrayObject result(cx, NewDenseUnallocatedArray(cx, keys->length())); + if (!result) + return false; + + UErrorCode status = U_ZERO_ERROR; + + UDateFormat* fmt = + udat_open(UDAT_DEFAULT, UDAT_DEFAULT, icuLocale(locale.ptr()), + nullptr, 0, nullptr, 0, &status); + if (U_FAILURE(status)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR); + return false; + } + ScopedICUObject<UDateFormat, udat_close> datToClose(fmt); + + // UDateTimePatternGenerator will be needed for translations of date and + // time fields like "month", "week", "day" etc. + UDateTimePatternGenerator* dtpg = udatpg_open(icuLocale(locale.ptr()), &status); + if (U_FAILURE(status)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR); + return false; + } + ScopedICUObject<UDateTimePatternGenerator, udatpg_close> datPgToClose(dtpg); + + RootedValue keyValue(cx); + RootedString keyValStr(cx); + RootedValue wordVal(cx); + Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx); + if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE)) + return false; + + // 5. For each element of keys, + for (uint32_t i = 0; i < keys->length(); i++) { + /** + * We iterate over keys array looking for paths that we have code + * branches for. + * + * For any unknown path branch, the wordVal will keep NullValue and + * we'll throw at the end. + */ + + if (!GetElement(cx, keys, keys, i, &keyValue)) + return false; + + JSAutoByteString pattern; + keyValStr = keyValue.toString(); + if (!pattern.encodeUtf8(cx, keyValStr)) + return false; + + wordVal.setNull(); + + // 5.a. Perform an implementation dependent algorithm to map a key to a + // corresponding display name. + const char* pat = pattern.ptr(); + + if (!MatchPart(&pat, "dates")) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + if (!MatchPart(&pat, "/")) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + if (MatchPart(&pat, "fields")) { + if (!MatchPart(&pat, "/")) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + UDateTimePatternField fieldType; + + if (MatchPart(&pat, "year")) { + fieldType = UDATPG_YEAR_FIELD; + } else if (MatchPart(&pat, "month")) { + fieldType = UDATPG_MONTH_FIELD; + } else if (MatchPart(&pat, "week")) { + fieldType = UDATPG_WEEK_OF_YEAR_FIELD; + } else if (MatchPart(&pat, "day")) { + fieldType = UDATPG_DAY_FIELD; + } else { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + // This part must be the final part with no trailing data. + if (*pat != '\0') { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + int32_t resultSize; + + const UChar* value = udatpg_getAppendItemName(dtpg, fieldType, &resultSize); + if (U_FAILURE(status)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR); + return false; + } + + JSString* word = NewStringCopyN<CanGC>(cx, UCharToChar16(value), resultSize); + if (!word) + return false; + + wordVal.setString(word); + } else if (MatchPart(&pat, "gregorian")) { + if (!MatchPart(&pat, "/")) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + UDateFormatSymbolType symbolType; + int32_t index; + + if (MatchPart(&pat, "months")) { + if (!MatchPart(&pat, "/")) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + if (equal(style, "narrow")) { + symbolType = UDAT_STANDALONE_NARROW_MONTHS; + } else if (equal(style, "short")) { + symbolType = UDAT_STANDALONE_SHORT_MONTHS; + } else { + MOZ_ASSERT(equal(style, "long")); + symbolType = UDAT_STANDALONE_MONTHS; + } + + if (MatchPart(&pat, "january")) { + index = UCAL_JANUARY; + } else if (MatchPart(&pat, "february")) { + index = UCAL_FEBRUARY; + } else if (MatchPart(&pat, "march")) { + index = UCAL_MARCH; + } else if (MatchPart(&pat, "april")) { + index = UCAL_APRIL; + } else if (MatchPart(&pat, "may")) { + index = UCAL_MAY; + } else if (MatchPart(&pat, "june")) { + index = UCAL_JUNE; + } else if (MatchPart(&pat, "july")) { + index = UCAL_JULY; + } else if (MatchPart(&pat, "august")) { + index = UCAL_AUGUST; + } else if (MatchPart(&pat, "september")) { + index = UCAL_SEPTEMBER; + } else if (MatchPart(&pat, "october")) { + index = UCAL_OCTOBER; + } else if (MatchPart(&pat, "november")) { + index = UCAL_NOVEMBER; + } else if (MatchPart(&pat, "december")) { + index = UCAL_DECEMBER; + } else { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + } else if (MatchPart(&pat, "weekdays")) { + if (!MatchPart(&pat, "/")) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + if (equal(style, "narrow")) { + symbolType = UDAT_STANDALONE_NARROW_WEEKDAYS; + } else if (equal(style, "short")) { + symbolType = UDAT_STANDALONE_SHORT_WEEKDAYS; + } else { + MOZ_ASSERT(equal(style, "long")); + symbolType = UDAT_STANDALONE_WEEKDAYS; + } + + if (MatchPart(&pat, "monday")) { + index = UCAL_MONDAY; + } else if (MatchPart(&pat, "tuesday")) { + index = UCAL_TUESDAY; + } else if (MatchPart(&pat, "wednesday")) { + index = UCAL_WEDNESDAY; + } else if (MatchPart(&pat, "thursday")) { + index = UCAL_THURSDAY; + } else if (MatchPart(&pat, "friday")) { + index = UCAL_FRIDAY; + } else if (MatchPart(&pat, "saturday")) { + index = UCAL_SATURDAY; + } else if (MatchPart(&pat, "sunday")) { + index = UCAL_SUNDAY; + } else { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + } else if (MatchPart(&pat, "dayperiods")) { + if (!MatchPart(&pat, "/")) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + symbolType = UDAT_AM_PMS; + + if (MatchPart(&pat, "am")) { + index = UCAL_AM; + } else if (MatchPart(&pat, "pm")) { + index = UCAL_PM; + } else { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + } else { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + // This part must be the final part with no trailing data. + if (*pat != '\0') { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + int32_t resultSize = + udat_getSymbols(fmt, symbolType, index, Char16ToUChar(chars.begin()), + INITIAL_CHAR_BUFFER_SIZE, &status); + if (status == U_BUFFER_OVERFLOW_ERROR) { + if (!chars.resize(resultSize)) + return false; + status = U_ZERO_ERROR; + udat_getSymbols(fmt, symbolType, index, Char16ToUChar(chars.begin()), + resultSize, &status); + } + if (U_FAILURE(status)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR); + return false; + } + + JSString* word = NewStringCopyN<CanGC>(cx, chars.begin(), resultSize); + if (!word) + return false; + + wordVal.setString(word); + } else { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr()); + return false; + } + + MOZ_ASSERT(wordVal.isString()); + + // 5.b. Append the result string to result. + if (!DefineElement(cx, result, i, wordVal)) + return false; + } + + // 6. Return result. + args.rval().setObject(*result); + return true; +} + /******************** Intl ********************/ const Class js::IntlClass = { diff --git a/js/src/builtin/Intl.h b/js/src/builtin/Intl.h index 54764605b..9a1e44913 100644 --- a/js/src/builtin/Intl.h +++ b/js/src/builtin/Intl.h @@ -16,9 +16,7 @@ #include "js/GCAPI.h" #include "js/GCHashTable.h" -#if ENABLE_INTL_API #include "unicode/utypes.h" -#endif /* * The Intl module specified by standard ECMA-402, @@ -387,7 +385,48 @@ intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp); extern MOZ_MUST_USE bool intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp); -#if ENABLE_INTL_API +/** + * Returns an Array with CLDR-based fields display names. + * The function takes three arguments: + * + * locale + * BCP47 compliant locale string + * style + * A string with values: long or short or narrow + * keys + * An array or path-like strings that identify keys to be returned + * At the moment the following types of keys are supported: + * + * 'dates/fields/{year|month|week|day}' + * 'dates/gregorian/months/{january|...|december}' + * 'dates/gregorian/weekdays/{sunday|...|saturday}' + * 'dates/gregorian/dayperiods/{am|pm}' + * + * Example: + * + * let info = intl_ComputeDisplayNames( + * 'en-US', + * 'long', + * [ + * 'dates/fields/year', + * 'dates/gregorian/months/january', + * 'dates/gregorian/weekdays/monday', + * 'dates/gregorian/dayperiods/am', + * ] + * ); + * + * Returned value: + * + * [ + * 'year', + * 'January', + * 'Monday', + * 'AM' + * ] + */ +extern MOZ_MUST_USE bool +intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp); + /** * Cast char16_t* strings to UChar* strings used by ICU. */ @@ -402,7 +441,18 @@ Char16ToUChar(char16_t* chars) { return reinterpret_cast<UChar*>(chars); } -#endif // ENABLE_INTL_API + +inline char16_t* +UCharToChar16(UChar* chars) +{ + return reinterpret_cast<char16_t*>(chars); +} + +inline const char16_t* +UCharToChar16(const UChar* chars) +{ + return reinterpret_cast<const char16_t*>(chars); +} } // namespace js diff --git a/js/src/builtin/Intl.js b/js/src/builtin/Intl.js index 37c87365b..6391c3e70 100644 --- a/js/src/builtin/Intl.js +++ b/js/src/builtin/Intl.js @@ -21,9 +21,6 @@ intl_availableCalendars: false, intl_patternForSkeleton: false, intl_FormatDateTime: false, - intl_SelectPluralRule: false, - intl_GetPluralCategories: false, - intl_GetCalendarInfo: false, */ /* @@ -860,7 +857,6 @@ function BestAvailableLocaleIgnoringDefault(availableLocales, locale) { return BestAvailableLocaleHelper(availableLocales, locale, false); } -var noRelevantExtensionKeys = []; /** * Compares a BCP 47 language priority list against the set of locales in @@ -1188,10 +1184,9 @@ function GetOption(options, property, type, values, fallback) { * Spec: ECMAScript Internationalization API Specification, 9.2.10. */ function GetNumberOption(options, property, minimum, maximum, fallback) { - assert(typeof minimum === "number" && (minimum | 0) === minimum, "GetNumberOption"); - assert(typeof maximum === "number" && (maximum | 0) === maximum, "GetNumberOption"); - assert(typeof fallback === "number" && (fallback | 0) === fallback, "GetNumberOption"); - assert(minimum <= fallback && fallback <= maximum, "GetNumberOption"); + assert(typeof minimum === "number", "GetNumberOption"); + assert(typeof maximum === "number", "GetNumberOption"); + assert(fallback === undefined || (fallback >= minimum && fallback <= maximum), "GetNumberOption"); // Step 1. var value = options[property]; @@ -1201,10 +1196,7 @@ function GetNumberOption(options, property, minimum, maximum, fallback) { value = ToNumber(value); if (Number_isNaN(value) || value < minimum || value > maximum) ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, value); - - // Apply bitwise-or to convert -0 to +0 per ES2017, 5.2 and to ensure - // the result is an int32 value. - return std_Math_floor(value) | 0; + return std_Math_floor(value); } // Step 3. @@ -1278,9 +1270,7 @@ function initializeIntlObject(obj) { function setLazyData(internals, type, lazyData) { assert(internals.type === "partial", "can't set lazy data for anything but a newborn"); - assert(type === "Collator" || type === "DateTimeFormat" || - type == "NumberFormat" || type === "PluralRules", - "bad type"); + assert(type === "Collator" || type === "DateTimeFormat" || type == "NumberFormat", "bad type"); assert(IsObject(lazyData), "non-object lazy data"); // Set in reverse order so that the .type change is a barrier. @@ -1330,9 +1320,7 @@ function isInitializedIntlObject(obj) { if (IsObject(internals)) { assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type"); var type = internals.type; - assert(type === "partial" || type === "Collator" || - type === "DateTimeFormat" || type === "NumberFormat" || type === "PluralRules", - "unexpected type"); + assert(type === "partial" || type === "Collator" || type === "DateTimeFormat" || type === "NumberFormat", "unexpected type"); assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData"); assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps"); } else { @@ -1389,8 +1377,6 @@ function getInternals(obj) internalProps = resolveCollatorInternals(lazyData) else if (type === "DateTimeFormat") internalProps = resolveDateTimeFormatInternals(lazyData) - else if (type === "PluralRules") - internalProps = resolvePluralRulesInternals(lazyData) else internalProps = resolveNumberFormatInternals(lazyData); setInternalProperties(internals, internalProps); @@ -1722,7 +1708,6 @@ function Intl_Collator_compare_get() { // Step 2. return internals.boundCompare; } -_SetCanonicalName(Intl_Collator_compare_get, "get compare"); /** @@ -1791,37 +1776,45 @@ function resolveNumberFormatInternals(lazyNumberFormatData) { // Step 6. var opt = lazyNumberFormatData.opt; + // Compute effective locale. + // Step 9. var NumberFormat = numberFormatInternalProperties; - // Step 9. + // Step 10. var localeData = NumberFormat.localeData; - // Step 10. + // Step 11. var r = ResolveLocale(callFunction(NumberFormat.availableLocales, NumberFormat), lazyNumberFormatData.requestedLocales, lazyNumberFormatData.opt, NumberFormat.relevantExtensionKeys, localeData); - // Steps 11-12. (Step 13 is not relevant to our implementation.) + // Steps 12-13. (Step 14 is not relevant to our implementation.) internalProps.locale = r.locale; internalProps.numberingSystem = r.nu; // Compute formatting options. - // Step 15. - var style = lazyNumberFormatData.style; - internalProps.style = style; + // Step 16. + var s = lazyNumberFormatData.style; + internalProps.style = s; - // Steps 19, 21. - if (style === "currency") { + // Steps 20, 22. + if (s === "currency") { internalProps.currency = lazyNumberFormatData.currency; internalProps.currencyDisplay = lazyNumberFormatData.currencyDisplay; } + // Step 24. internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits; + + // Steps 27. internalProps.minimumFractionDigits = lazyNumberFormatData.minimumFractionDigits; + + // Step 30. internalProps.maximumFractionDigits = lazyNumberFormatData.maximumFractionDigits; + // Step 33. if ("minimumSignificantDigits" in lazyNumberFormatData) { // Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the // actual presence (versus undefined-ness) of these properties. @@ -1830,10 +1823,10 @@ function resolveNumberFormatInternals(lazyNumberFormatData) { internalProps.maximumSignificantDigits = lazyNumberFormatData.maximumSignificantDigits; } - // Step 27. + // Step 35. internalProps.useGrouping = lazyNumberFormatData.useGrouping; - // Step 34. + // Step 42. internalProps.boundFormat = undefined; // The caller is responsible for associating |internalProps| with the right @@ -1861,44 +1854,6 @@ function getNumberFormatInternals(obj, methodName) { return internalProps; } -/** - * Applies digit options used for number formatting onto the intl object. - * - * Spec: ECMAScript Internationalization API Specification, 11.1.1. - */ -function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault, mxfdDefault) { - // We skip Step 1 because we set the properties on a lazyData object. - - // Step 2-3. - assert(IsObject(options), "SetNumberFormatDigitOptions"); - assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions"); - assert(typeof mxfdDefault === "number", "SetNumberFormatDigitOptions"); - assert(mnfdDefault <= mxfdDefault, "SetNumberFormatDigitOptions"); - - // Steps 4-6. - const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1); - const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault); - const mxfdActualDefault = std_Math_max(mnfd, mxfdDefault); - const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdActualDefault); - - // Steps 7-8. - let mnsd = options.minimumSignificantDigits; - let mxsd = options.maximumSignificantDigits; - - // Steps 9-11. - lazyData.minimumIntegerDigits = mnid; - lazyData.minimumFractionDigits = mnfd; - lazyData.maximumFractionDigits = mxfd; - - // Step 12. - if (mnsd !== undefined || mxsd !== undefined) { - mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1); - mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21); - lazyData.minimumSignificantDigits = mnsd; - lazyData.maximumSignificantDigits = mxsd; - } -} - /** * Initializes an object as a NumberFormat. @@ -1948,7 +1903,7 @@ function InitializeNumberFormat(numberFormat, locales, options) { // } // // Note that lazy data is only installed as a final step of initialization, - // so every NumberFormat lazy data object has *all* these properties, never a + // so every Collator lazy data object has *all* these properties, never a // subset of them. var lazyNumberFormatData = std_Object_create(null); @@ -1978,46 +1933,67 @@ function InitializeNumberFormat(numberFormat, locales, options) { opt.localeMatcher = matcher; // Compute formatting options. - // Step 14. - var style = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal"); - lazyNumberFormatData.style = style; + // Step 15. + var s = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal"); + lazyNumberFormatData.style = s; - // Steps 16-19. + // Steps 17-20. var c = GetOption(options, "currency", "string", undefined, undefined); if (c !== undefined && !IsWellFormedCurrencyCode(c)) ThrowRangeError(JSMSG_INVALID_CURRENCY_CODE, c); var cDigits; - if (style === "currency") { + if (s === "currency") { if (c === undefined) ThrowTypeError(JSMSG_UNDEFINED_CURRENCY); - // Steps 19.a-c. + // Steps 20.a-c. c = toASCIIUpperCase(c); lazyNumberFormatData.currency = c; cDigits = CurrencyDigits(c); } - // Step 20. + // Step 21. var cd = GetOption(options, "currencyDisplay", "string", ["code", "symbol", "name"], "symbol"); - if (style === "currency") + if (s === "currency") lazyNumberFormatData.currencyDisplay = cd; - // Steps 22-25. - var mnfdDefault, mxfdDefault; - if (style === "currency") { - mnfdDefault = cDigits; - mxfdDefault = cDigits; - } else { - mnfdDefault = 0; - mxfdDefault = style === "percent" ? 0 : 3; + // Step 23. + var mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1); + lazyNumberFormatData.minimumIntegerDigits = mnid; + + // Steps 25-26. + var mnfdDefault = (s === "currency") ? cDigits : 0; + var mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault); + lazyNumberFormatData.minimumFractionDigits = mnfd; + + // Steps 28-29. + var mxfdDefault; + if (s === "currency") + mxfdDefault = std_Math_max(mnfd, cDigits); + else if (s === "percent") + mxfdDefault = std_Math_max(mnfd, 0); + else + mxfdDefault = std_Math_max(mnfd, 3); + var mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdDefault); + lazyNumberFormatData.maximumFractionDigits = mxfd; + + // Steps 31-32. + var mnsd = options.minimumSignificantDigits; + var mxsd = options.maximumSignificantDigits; + + // Step 33. + if (mnsd !== undefined || mxsd !== undefined) { + mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1); + mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21); + lazyNumberFormatData.minimumSignificantDigits = mnsd; + lazyNumberFormatData.maximumSignificantDigits = mxsd; } - SetNumberFormatDigitOptions(lazyNumberFormatData, options, mnfdDefault, mxfdDefault); - // Steps 26. + // Step 34. var g = GetOption(options, "useGrouping", "boolean", undefined, true); lazyNumberFormatData.useGrouping = g; - // Steps 35-36. + // Step 43. // // We've done everything that must be done now: mark the lazy data as fully // computed and install it. @@ -2026,6 +2002,43 @@ function InitializeNumberFormat(numberFormat, locales, options) { /** + * Mapping from currency codes to the number of decimal digits used for them. + * Default is 2 digits. + * + * Spec: ISO 4217 Currency and Funds Code List. + * http://www.currency-iso.org/en/home/tables/table-a1.html + */ +var currencyDigits = { + BHD: 3, + BIF: 0, + BYR: 0, + CLF: 4, + CLP: 0, + DJF: 0, + GNF: 0, + IQD: 3, + ISK: 0, + JOD: 3, + JPY: 0, + KMF: 0, + KRW: 0, + KWD: 3, + LYD: 3, + OMR: 3, + PYG: 0, + RWF: 0, + TND: 3, + UGX: 0, + UYI: 0, + VND: 0, + VUV: 0, + XAF: 0, + XOF: 0, + XPF: 0 +}; + + +/** * Returns the number of decimal digits to be used for the given currency. * * Spec: ECMAScript Internationalization API Specification, 11.1.1. @@ -2106,7 +2119,7 @@ function numberFormatFormatToBind(value) { // Step 1.a.ii-iii. var x = ToNumber(value); - return intl_FormatNumber(this, x, /* formatToParts = */ false); + return intl_FormatNumber(this, x); } @@ -2133,22 +2146,6 @@ function Intl_NumberFormat_format_get() { // Step 2. return internals.boundFormat; } -_SetCanonicalName(Intl_NumberFormat_format_get, "get format"); - - -function Intl_NumberFormat_formatToParts(value) { - // Step 1. - var nf = this; - - // Steps 2-3. - getNumberFormatInternals(nf, "formatToParts"); - - // Step 4. - var x = ToNumber(value); - - // Step 5. - return intl_FormatNumber(nf, x, /* formatToParts = */ true); -} /** @@ -2290,6 +2287,26 @@ function getDateTimeFormatInternals(obj, methodName) { return internalProps; } +/** + * Components of date and time formats and their values. + * + * Spec: ECMAScript Internationalization API Specification, 12.1.1. + */ +var dateTimeComponentValues = { + weekday: ["narrow", "short", "long"], + era: ["narrow", "short", "long"], + year: ["2-digit", "numeric"], + month: ["2-digit", "numeric", "narrow", "short", "long"], + day: ["2-digit", "numeric"], + hour: ["2-digit", "numeric"], + minute: ["2-digit", "numeric"], + second: ["2-digit", "numeric"], + timeZoneName: ["short", "long"] +}; + + +var dateTimeComponents = std_Object_getOwnPropertyNames(dateTimeComponentValues); + /** * Initializes an object as a DateTimeFormat. @@ -2379,19 +2396,12 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) { lazyDateTimeFormatData.formatOpt = formatOpt; // Step 19. - // 12.1, Table 4: Components of date and time formats. - formatOpt.weekday = GetOption(options, "weekday", "string", ["narrow", "short", "long"], - undefined); - formatOpt.era = GetOption(options, "era", "string", ["narrow", "short", "long"], undefined); - formatOpt.year = GetOption(options, "year", "string", ["2-digit", "numeric"], undefined); - formatOpt.month = GetOption(options, "month", "string", - ["2-digit", "numeric", "narrow", "short", "long"], undefined); - formatOpt.day = GetOption(options, "day", "string", ["2-digit", "numeric"], undefined); - formatOpt.hour = GetOption(options, "hour", "string", ["2-digit", "numeric"], undefined); - formatOpt.minute = GetOption(options, "minute", "string", ["2-digit", "numeric"], undefined); - formatOpt.second = GetOption(options, "second", "string", ["2-digit", "numeric"], undefined); - formatOpt.timeZoneName = GetOption(options, "timeZoneName", "string", ["short", "long"], - undefined); + var i, prop; + for (i = 0; i < dateTimeComponents.length; i++) { + prop = dateTimeComponents[i]; + var value = GetOption(options, prop, "string", dateTimeComponentValues[prop], undefined); + formatOpt[prop] = value; + } // Steps 20-21 provided by ICU - see comment after this function. @@ -2659,6 +2669,7 @@ function ToDateTimeOptions(options, required, defaults) { return options; } + /** * Compares the date and time components requested by options with the available * date and time formats in formats, and selects the best match according @@ -2844,7 +2855,6 @@ function Intl_DateTimeFormat_format_get() { // Step 2. return internals.boundFormat; } -_SetCanonicalName(Intl_DateTimeFormat_format_get, "get format"); function Intl_DateTimeFormat_formatToParts() { @@ -2980,232 +2990,6 @@ function resolveICUPattern(pattern, result) { } } -/********** Intl.PluralRules **********/ - -/** - * PluralRules internal properties. - * - * Spec: ECMAScript 402 API, PluralRules, 1.3.3. - */ -var pluralRulesInternalProperties = { - _availableLocales: null, - availableLocales: function() - { - var locales = this._availableLocales; - if (locales) - return locales; - - locales = intl_PluralRules_availableLocales(); - addSpecialMissingLanguageTags(locales); - return (this._availableLocales = locales); - } -}; - -/** - * Compute an internal properties object from |lazyPluralRulesData|. - */ -function resolvePluralRulesInternals(lazyPluralRulesData) { - assert(IsObject(lazyPluralRulesData), "lazy data not an object?"); - - var internalProps = std_Object_create(null); - - var requestedLocales = lazyPluralRulesData.requestedLocales; - - var PluralRules = pluralRulesInternalProperties; - - // Step 13. - const r = ResolveLocale(callFunction(PluralRules.availableLocales, PluralRules), - lazyPluralRulesData.requestedLocales, - lazyPluralRulesData.opt, - noRelevantExtensionKeys, undefined); - - // Step 14. - internalProps.locale = r.locale; - internalProps.type = lazyPluralRulesData.type; - - internalProps.pluralCategories = intl_GetPluralCategories( - internalProps.locale, - internalProps.type); - - internalProps.minimumIntegerDigits = lazyPluralRulesData.minimumIntegerDigits; - internalProps.minimumFractionDigits = lazyPluralRulesData.minimumFractionDigits; - internalProps.maximumFractionDigits = lazyPluralRulesData.maximumFractionDigits; - - if ("minimumSignificantDigits" in lazyPluralRulesData) { - assert("maximumSignificantDigits" in lazyPluralRulesData, "min/max sig digits mismatch"); - internalProps.minimumSignificantDigits = lazyPluralRulesData.minimumSignificantDigits; - internalProps.maximumSignificantDigits = lazyPluralRulesData.maximumSignificantDigits; - } - - return internalProps; -} - -/** - * Returns an object containing the PluralRules internal properties of |obj|, - * or throws a TypeError if |obj| isn't PluralRules-initialized. - */ -function getPluralRulesInternals(obj, methodName) { - var internals = getIntlObjectInternals(obj, "PluralRules", methodName); - assert(internals.type === "PluralRules", "bad type escaped getIntlObjectInternals"); - - var internalProps = maybeInternalProperties(internals); - if (internalProps) - return internalProps; - - internalProps = resolvePluralRulesInternals(internals.lazyData); - setInternalProperties(internals, internalProps); - return internalProps; -} - -/** - * Initializes an object as a PluralRules. - * - * This method is complicated a moderate bit by its implementing initialization - * as a *lazy* concept. Everything that must happen now, does -- but we defer - * all the work we can until the object is actually used as a PluralRules. - * This later work occurs in |resolvePluralRulesInternals|; steps not noted - * here occur there. - * - * Spec: ECMAScript 402 API, PluralRules, 1.1.1. - */ -function InitializePluralRules(pluralRules, locales, options) { - assert(IsObject(pluralRules), "InitializePluralRules"); - - // Step 1. - if (isInitializedIntlObject(pluralRules)) - ThrowTypeError(JSMSG_INTL_OBJECT_REINITED); - - let internals = initializeIntlObject(pluralRules); - - // Lazy PluralRules data has the following structure: - // - // { - // requestedLocales: List of locales, - // type: "cardinal" / "ordinal", - // - // opt: // opt object computer in InitializePluralRules - // { - // localeMatcher: "lookup" / "best fit", - // } - // - // minimumIntegerDigits: integer ∈ [1, 21], - // minimumFractionDigits: integer ∈ [0, 20], - // maximumFractionDigits: integer ∈ [0, 20], - // - // // optional - // minimumSignificantDigits: integer ∈ [1, 21], - // maximumSignificantDigits: integer ∈ [1, 21], - // } - // - // Note that lazy data is only installed as a final step of initialization, - // so every PluralRules lazy data object has *all* these properties, never a - // subset of them. - const lazyPluralRulesData = std_Object_create(null); - - // Step 3. - let requestedLocales = CanonicalizeLocaleList(locales); - lazyPluralRulesData.requestedLocales = requestedLocales; - - // Steps 4-5. - if (options === undefined) - options = {}; - else - options = ToObject(options); - - // Step 6. - const type = GetOption(options, "type", "string", ["cardinal", "ordinal"], "cardinal"); - lazyPluralRulesData.type = type; - - // Step 8. - let opt = new Record(); - lazyPluralRulesData.opt = opt; - - // Steps 9-10. - let matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit"); - opt.localeMatcher = matcher; - - // Steps 11-12. - SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0, 3); - - setLazyData(internals, "PluralRules", lazyPluralRulesData) -} - -/** - * Returns the subset of the given locale list for which this locale list has a - * matching (possibly fallback) locale. Locales appear in the same order in the - * returned list as in the input list. - * - * Spec: ECMAScript 402 API, PluralRules, 1.3.2. - */ -function Intl_PluralRules_supportedLocalesOf(locales /*, options*/) { - var options = arguments.length > 1 ? arguments[1] : undefined; - - // Step 1. - var availableLocales = callFunction(pluralRulesInternalProperties.availableLocales, - pluralRulesInternalProperties); - // Step 2. - let requestedLocales = CanonicalizeLocaleList(locales); - - // Step 3. - return SupportedLocales(availableLocales, requestedLocales, options); -} - -/** - * Returns a String value representing the plural category matching - * the number passed as value according to the - * effective locale and the formatting options of this PluralRules. - * - * Spec: ECMAScript 402 API, PluralRules, 1.4.3. - */ -function Intl_PluralRules_select(value) { - // Step 1. - let pluralRules = this; - // Step 2. - let internals = getPluralRulesInternals(pluralRules, "select"); - - // Steps 3-4. - let n = ToNumber(value); - - // Step 5. - return intl_SelectPluralRule(pluralRules, n); -} - -/** - * Returns the resolved options for a PluralRules object. - * - * Spec: ECMAScript 402 API, PluralRules, 1.4.4. - */ -function Intl_PluralRules_resolvedOptions() { - var internals = getPluralRulesInternals(this, "resolvedOptions"); - - var internalsPluralCategories = internals.pluralCategories; - var pluralCategories = []; - for (var i = 0; i < internalsPluralCategories.length; i++) - _DefineDataProperty(pluralCategories, i, internalsPluralCategories[i]); - - var result = { - locale: internals.locale, - type: internals.type, - pluralCategories, - minimumIntegerDigits: internals.minimumIntegerDigits, - minimumFractionDigits: internals.minimumFractionDigits, - maximumFractionDigits: internals.maximumFractionDigits, - }; - - var optionalProperties = [ - "minimumSignificantDigits", - "maximumSignificantDigits" - ]; - - for (var i = 0; i < optionalProperties.length; i++) { - var p = optionalProperties[i]; - if (callFunction(std_Object_hasOwnProperty, internals, p)) - _DefineDataProperty(result, p, internals[p]); - } - return result; -} - - function Intl_getCanonicalLocales(locales) { let codes = CanonicalizeLocaleList(locales); let result = []; @@ -3362,5 +3146,5 @@ function Intl_getDisplayNames(locales, options) { // 24. Return result. return result; -} +} diff --git a/js/src/builtin/IntlTimeZoneData.h b/js/src/builtin/IntlTimeZoneData.h index f92c583df..fa808c0b9 100644 --- a/js/src/builtin/IntlTimeZoneData.h +++ b/js/src/builtin/IntlTimeZoneData.h @@ -1,5 +1,5 @@ // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2018c +// tzdata version = 2018e #ifndef builtin_IntlTimeZoneData_h #define builtin_IntlTimeZoneData_h diff --git a/js/src/builtin/Map.js b/js/src/builtin/Map.js index 580629a13..434cd6529 100644 --- a/js/src/builtin/Map.js +++ b/js/src/builtin/Map.js @@ -62,8 +62,8 @@ function MapIteratorNext() { var O = this; // Steps 2-3. - if (!IsObject(O) || !IsMapIterator(O)) - return callFunction(CallMapIteratorMethodIfWrapped, O, "MapIteratorNext"); + if (!IsObject(O) || (O = GuardToMapIterator(O)) === null) + return callFunction(CallMapIteratorMethodIfWrapped, this, "MapIteratorNext"); // Steps 4-5 (implemented in _GetNextMapEntryForIterator). // Steps 8-9 (omitted). @@ -82,7 +82,7 @@ function MapIteratorNext() { // Steps 10.b-c (omitted). // Step 6. - var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); + var itemKind = UnsafeGetInt32FromReservedSlot(O, ITERATOR_SLOT_ITEM_KIND); var result; if (itemKind === ITEM_KIND_KEY) { diff --git a/js/src/builtin/Set.js b/js/src/builtin/Set.js index 9af6cf8d1..e2571e66a 100644 --- a/js/src/builtin/Set.js +++ b/js/src/builtin/Set.js @@ -64,8 +64,8 @@ function SetIteratorNext() { var O = this; // Steps 2-3. - if (!IsObject(O) || !IsSetIterator(O)) - return callFunction(CallSetIteratorMethodIfWrapped, O, "SetIteratorNext"); + if (!IsObject(O) || (O = GuardToSetIterator(O)) === null) + return callFunction(CallSetIteratorMethodIfWrapped, this, "SetIteratorNext"); // Steps 4-5 (implemented in _GetNextSetEntryForIterator). // Steps 8-9 (omitted). @@ -83,7 +83,7 @@ function SetIteratorNext() { // Steps 10.b-c (omitted). // Step 6. - var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); + var itemKind = UnsafeGetInt32FromReservedSlot(O, ITERATOR_SLOT_ITEM_KIND); var result; if (itemKind === ITEM_KIND_VALUE) { diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js index 4202d0de6..e5b2ad552 100644 --- a/js/src/builtin/String.js +++ b/js/src/builtin/String.js @@ -529,16 +529,17 @@ function String_iterator() { } function StringIteratorNext() { - if (!IsObject(this) || !IsStringIterator(this)) { + var obj; + if (!IsObject(this) || (obj = GuardToStringIterator(this)) === null) { return callFunction(CallStringIteratorMethodIfWrapped, this, "StringIteratorNext"); } - var S = UnsafeGetStringFromReservedSlot(this, ITERATOR_SLOT_TARGET); + var S = UnsafeGetStringFromReservedSlot(obj, ITERATOR_SLOT_TARGET); // We know that JSString::MAX_LENGTH <= INT32_MAX (and assert this in // SelfHostring.cpp) so our current index can never be anything other than // an Int32Value. - var index = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX); + var index = UnsafeGetInt32FromReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX); var size = S.length; var result = { value: undefined, done: false }; @@ -556,7 +557,7 @@ function StringIteratorNext() { } } - UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + charCount); + UnsafeSetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX, index + charCount); result.value = callFunction(String_substring, S, index, index + charCount); return result; @@ -659,11 +660,7 @@ function String_static_localeCompare(str1, str2) { ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "String.localeCompare"); var locales = arguments.length > 2 ? arguments[2] : undefined; var options = arguments.length > 3 ? arguments[3] : undefined; -#if EXPOSE_INTL_API return callFunction(String_localeCompare, str1, str2, locales, options); -#else - return callFunction(std_String_localeCompare, str1, str2, locales, options); -#endif } // ES6 draft 2014-04-27 B.2.3.3 @@ -855,14 +852,12 @@ function String_static_toLocaleUpperCase(string) { return callFunction(std_String_toLocaleUpperCase, string); } -#if EXPOSE_INTL_API function String_static_normalize(string) { if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.normalize'); var form = arguments.length > 1 ? arguments[1] : undefined; return callFunction(std_String_normalize, string, form); } -#endif function String_static_concat(string, arg1) { if (arguments.length < 1) diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index a14f9ba69..373b6c9ed 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -236,11 +236,7 @@ GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) if (!JS_SetProperty(cx, info, "binary-data", value)) return false; -#ifdef EXPOSE_INTL_API value = BooleanValue(true); -#else - value = BooleanValue(false); -#endif if (!JS_SetProperty(cx, info, "intl-api", value)) return false; @@ -2092,7 +2088,7 @@ class CloneBufferObject : public NativeObject { Rooted<CloneBufferObject*> obj(cx, Create(cx)); if (!obj) return nullptr; - auto data = js::MakeUnique<JSStructuredCloneData>(); + auto data = js::MakeUnique<JSStructuredCloneData>(buffer->scope()); if (!data) { ReportOutOfMemory(cx); return nullptr; @@ -2145,8 +2141,11 @@ class CloneBufferObject : public NativeObject { return false; size_t nbytes = JS_GetStringLength(args[0].toString()); MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0); - auto buf = js::MakeUnique<JSStructuredCloneData>(nbytes, nbytes, nbytes); - js_memcpy(buf->Start(), str, nbytes); + auto buf = js::MakeUnique<JSStructuredCloneData>(JS::StructuredCloneScope::DifferentProcess); + if (!buf->AppendBytes(str, nbytes)) { + ReportOutOfMemory(cx); + return false; + } JS_free(cx, str); obj->setData(buf.release()); @@ -2190,7 +2189,7 @@ class CloneBufferObject : public NativeObject { ReportOutOfMemory(cx); return false; } - auto iter = obj->data()->Iter(); + auto iter = obj->data()->Start(); obj->data()->ReadBytes(iter, buffer.get(), size); JSString* str = JS_NewStringCopyN(cx, buffer.get(), size); if (!str) @@ -2248,6 +2247,8 @@ ParseCloneScope(JSContext* cx, HandleString str) scope.emplace(JS::StructuredCloneScope::SameProcessDifferentThread); else if (strcmp(scopeStr.ptr(), "DifferentProcess") == 0) scope.emplace(JS::StructuredCloneScope::DifferentProcess); + else if (strcmp(scopeStr.ptr(), "DifferentProcessForIndexedDB") == 0) + scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB); return scope; } @@ -4374,19 +4375,22 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0, " clone buffer object. 'policy' may be an options hash. Valid keys:\n" " 'SharedArrayBuffer' - either 'allow' (the default) or 'deny'\n" " to specify whether SharedArrayBuffers may be serialized.\n" -"\n" -" 'scope' - SameProcessSameThread, SameProcessDifferentThread, or\n" -" DifferentProcess. Determines how some values will be serialized.\n" -" Clone buffers may only be deserialized with a compatible scope."), +" 'scope' - SameProcessSameThread, SameProcessDifferentThread,\n" +" DifferentProcess, or DifferentProcessForIndexedDB. Determines how some\n" +" values will be serialized. Clone buffers may only be deserialized with a\n" +" compatible scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n" +" must also set SharedArrayBuffer:'deny' if data contains any shared memory\n" +" object."), JS_FN_HELP("deserialize", Deserialize, 1, 0, "deserialize(clonebuffer[, opts])", " Deserialize data generated by serialize. 'opts' is an options hash with one\n" " recognized key 'scope', which limits the clone buffers that are considered\n" " valid. Allowed values: 'SameProcessSameThread', 'SameProcessDifferentThread',\n" -" and 'DifferentProcess'. So for example, a DifferentProcess clone buffer\n" -" may be deserialized in any scope, but a SameProcessSameThread clone buffer\n" -" cannot be deserialized in a DifferentProcess scope."), +" 'DifferentProcess', and 'DifferentProcessForIndexedDB'. So for example, a\n" +" DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n" +" a SameProcessSameThread clone buffer cannot be deserialized in a\n" +" DifferentProcess scope."), JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0, "detachArrayBuffer(buffer)", diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index a2205dc92..a1934051d 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -1232,11 +1232,7 @@ function TypedArrayToLocaleString(locales = undefined, options = undefined) { // Steps 6-7. // Omit the 'if' clause in step 6, since typed arrays can't have undefined // or null elements. -#if EXPOSE_INTL_API var R = ToString(callContentFunction(firstElement.toLocaleString, firstElement, locales, options)); -#else - var R = ToString(callContentFunction(firstElement.toLocaleString, firstElement)); -#endif // Step 3 (reordered). // We don't (yet?) implement locale-dependent separators. @@ -1258,11 +1254,7 @@ function TypedArrayToLocaleString(locales = undefined, options = undefined) { // the error message. So despite bug 1079853, we can skip step 9.c. // Step 9.d. -#if EXPOSE_INTL_API R = ToString(callContentFunction(nextElement.toLocaleString, nextElement, locales, options)); -#else - R = ToString(callContentFunction(nextElement.toLocaleString, nextElement)); -#endif // Step 9.e. R = S + R; diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 19737c9ee..5c2576efd 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -73,7 +73,7 @@ class ChunkPool // Performs extra allocation off the main thread so that when memory is // required on the main thread it will already be available and waiting. -class BackgroundAllocTask : public GCParallelTask +class BackgroundAllocTask : public GCParallelTaskHelper<BackgroundAllocTask> { // Guarded by the GC lock. JSRuntime* runtime; @@ -85,12 +85,11 @@ class BackgroundAllocTask : public GCParallelTask BackgroundAllocTask(JSRuntime* rt, ChunkPool& pool); bool enabled() const { return enabled_; } - protected: - void run() override; + void run(); }; -// Search the provided Chunks for free arenas and decommit them. -class BackgroundDecommitTask : public GCParallelTask +// Search the provided Chunks for free arenas and recommit them. +class BackgroundDecommitTask : public GCParallelTaskHelper<BackgroundDecommitTask> { public: using ChunkVector = mozilla::Vector<Chunk*>; @@ -98,8 +97,7 @@ class BackgroundDecommitTask : public GCParallelTask explicit BackgroundDecommitTask(JSRuntime *rt) : runtime(rt) {} void setChunksToScan(ChunkVector &chunks); - protected: - void run() override; + void run(); private: JSRuntime* runtime; @@ -1171,8 +1169,10 @@ class GCRuntime /* * Concurrent sweep infrastructure. */ - void startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked); - void joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked); + void startTask(GCParallelTask& task, gcstats::Phase phase, + AutoLockHelperThreadState& locked); + void joinTask(GCParallelTask& task, gcstats::Phase phase, + AutoLockHelperThreadState& locked); /* * List head of arenas allocated during the sweep phase. diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index aa50bf29e..55ca5a059 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -43,19 +43,19 @@ using mozilla::PodZero; static const uintptr_t CanaryMagicValue = 0xDEADB15D; -struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTask +struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTaskHelper<FreeMallocedBuffersTask> { explicit FreeMallocedBuffersTask(FreeOp* fop) : fop_(fop) {} bool init() { return buffers_.init(); } void transferBuffersToFree(MallocedBuffersSet& buffersToFree, const AutoLockHelperThreadState& lock); - ~FreeMallocedBuffersTask() override { join(); } + ~FreeMallocedBuffersTask() { join(); } + + void run(); private: FreeOp* fop_; MallocedBuffersSet buffers_; - - virtual void run() override; }; struct js::Nursery::SweepAction diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index c9e5871e3..ca1969b2c 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -22,9 +22,6 @@ using mozilla::Maybe; namespace js { - -class GCParallelTask; - namespace gcstats { enum Phase : uint8_t { diff --git a/js/src/gdb/moz.build b/js/src/gdb/moz.build index 681f9807c..faa69eb7b 100644 --- a/js/src/gdb/moz.build +++ b/js/src/gdb/moz.build @@ -34,7 +34,7 @@ USE_LIBS += [ 'static:js', ] -if CONFIG['ENABLE_INTL_API'] and CONFIG['MOZ_ICU_DATA_ARCHIVE']: +if CONFIG['MOZ_ICU_DATA_ARCHIVE']: # The ICU libraries linked into libmozjs will not include the ICU data, # so link it directly. USE_LIBS += ['icudata'] diff --git a/js/src/jit/BaselineFrameInfo.h b/js/src/jit/BaselineFrameInfo.h index 13bf0358d..1691270ac 100644 --- a/js/src/jit/BaselineFrameInfo.h +++ b/js/src/jit/BaselineFrameInfo.h @@ -67,7 +67,7 @@ class StackValue union { struct { - Value v; + JS::UninitializedValue v; } constant; struct { mozilla::AlignedStorage2<ValueOperand> reg; @@ -112,7 +112,7 @@ class StackValue } Value constant() const { MOZ_ASSERT(kind_ == Constant); - return data.constant.v; + return data.constant.v.asValueRef(); } ValueOperand reg() const { MOZ_ASSERT(kind_ == Register); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 7b2f8214b..16d026092 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -11529,6 +11529,32 @@ CodeGenerator::visitHasClass(LHasClass* ins) } void +CodeGenerator::visitGuardToClass(LGuardToClass* ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register output = ToRegister(ins->output()); + Register temp = ToRegister(ins->temp()); + + Label notEqual; + + masm.branchTestObjClass(Assembler::NotEqual, lhs, temp, ins->mir()->getClass(), ¬Equal); + masm.mov(lhs, output); + + if (ins->mir()->type() == MIRType::Object) { + // Can't return null-return here, so bail + bailoutFrom(¬Equal, ins->snapshot()); + } else { + Label done; + masm.jump(&done); + + masm.bind(¬Equal); + masm.mov(ImmPtr(0), output); + + masm.bind(&done); + } +} + +void CodeGenerator::visitWasmParameter(LWasmParameter* lir) { } diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index d3126651b..b226f6cc9 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -377,6 +377,7 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitIsObject(LIsObject* lir); void visitIsObjectAndBranch(LIsObjectAndBranch* lir); void visitHasClass(LHasClass* lir); + void visitGuardToClass(LGuardToClass* lir); void visitWasmParameter(LWasmParameter* lir); void visitWasmParameterI64(LWasmParameterI64* lir); void visitWasmReturn(LWasmReturn* ret); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 15352c04f..23d3143a7 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -117,13 +117,15 @@ _(IntrinsicDefineDataProperty) \ _(IntrinsicObjectHasPrototype) \ \ - _(IntrinsicIsArrayIterator) \ - _(IntrinsicIsMapIterator) \ - _(IntrinsicIsSetIterator) \ - _(IntrinsicIsStringIterator) \ + _(IntrinsicGuardToArrayIterator) \ + _(IntrinsicGuardToMapIterator) \ + _(IntrinsicGuardToSetIterator) \ + _(IntrinsicGuardToStringIterator) \ \ + _(IntrinsicGuardToMapObject) \ _(IntrinsicGetNextMapEntryForIterator) \ \ + _(IntrinsicGuardToSetObject) \ _(IntrinsicGetNextSetEntryForIterator) \ \ _(IntrinsicArrayBufferByteLength) \ diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 35ad120f7..f24ef30c8 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -969,6 +969,7 @@ class IonBuilder const Class* clasp2 = nullptr, const Class* clasp3 = nullptr, const Class* clasp4 = nullptr); + InliningStatus inlineGuardToClass(CallInfo& callInfo, const Class* clasp); InliningStatus inlineIsConstructing(CallInfo& callInfo); InliningStatus inlineSubstringKernel(CallInfo& callInfo); InliningStatus inlineObjectHasPrototype(CallInfo& callInfo); diff --git a/js/src/jit/JitFrameIterator.h b/js/src/jit/JitFrameIterator.h index ba5efef6a..3620badbd 100644 --- a/js/src/jit/JitFrameIterator.h +++ b/js/src/jit/JitFrameIterator.h @@ -322,9 +322,7 @@ class RInstructionResults MOZ_MUST_USE bool init(JSContext* cx, uint32_t numResults); bool isInitialized() const; -#ifdef DEBUG size_t length() const; -#endif JitFrameLayout* frame() const; diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index f11f17225..019be46dd 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -1688,13 +1688,11 @@ RInstructionResults::isInitialized() const return initialized_; } -#ifdef DEBUG size_t RInstructionResults::length() const { return results_->length(); } -#endif JitFrameLayout* RInstructionResults::frame() const @@ -2150,7 +2148,7 @@ SnapshotIterator::initInstructionResults(MaybeReadFallback& fallback) } MOZ_ASSERT(results->isInitialized()); - MOZ_ASSERT(results->length() == recover_.numInstructions() - 1); + MOZ_RELEASE_ASSERT(results->length() == recover_.numInstructions() - 1); instructionResults_ = results; return true; } diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 730697163..709de9987 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4171,6 +4171,16 @@ LIRGenerator::visitHasClass(MHasClass* ins) } void +LIRGenerator::visitGuardToClass(MGuardToClass* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::ObjectOrNull|| ins->type() == MIRType::Object); + LGuardToClass* lir = new(alloc()) LGuardToClass(useRegister(ins->object()), temp()); + assignSnapshot(lir, Bailout_TypeBarrierO); + define(lir, ins); +} + +void LIRGenerator::visitWasmAddOffset(MWasmAddOffset* ins) { MOZ_ASSERT(ins->base()->type() == MIRType::Int32); diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index b2805cb7a..9b4095aec 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -289,6 +289,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitIsConstructor(MIsConstructor* ins); void visitIsObject(MIsObject* ins); void visitHasClass(MHasClass* ins); + void visitGuardToClass(MGuardToClass* ins); void visitWasmAddOffset(MWasmAddOffset* ins); void visitWasmBoundsCheck(MWasmBoundsCheck* ins); void visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 293253253..736c6c892 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -280,24 +280,28 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineIsConstructing(callInfo); case InlinableNative::IntrinsicSubstringKernel: return inlineSubstringKernel(callInfo); - case InlinableNative::IntrinsicIsArrayIterator: - return inlineHasClass(callInfo, &ArrayIteratorObject::class_); - case InlinableNative::IntrinsicIsMapIterator: - return inlineHasClass(callInfo, &MapIteratorObject::class_); - case InlinableNative::IntrinsicIsSetIterator: - return inlineHasClass(callInfo, &SetIteratorObject::class_); - case InlinableNative::IntrinsicIsStringIterator: - return inlineHasClass(callInfo, &StringIteratorObject::class_); + case InlinableNative::IntrinsicGuardToArrayIterator: + return inlineGuardToClass(callInfo, &ArrayIteratorObject::class_); + case InlinableNative::IntrinsicGuardToMapIterator: + return inlineGuardToClass(callInfo, &MapIteratorObject::class_); + case InlinableNative::IntrinsicGuardToSetIterator: + return inlineGuardToClass(callInfo, &SetIteratorObject::class_); + case InlinableNative::IntrinsicGuardToStringIterator: + return inlineGuardToClass(callInfo, &StringIteratorObject::class_); case InlinableNative::IntrinsicDefineDataProperty: return inlineDefineDataProperty(callInfo); case InlinableNative::IntrinsicObjectHasPrototype: return inlineObjectHasPrototype(callInfo); // Map intrinsics. + case InlinableNative::IntrinsicGuardToMapObject: + return inlineGuardToClass(callInfo, &MapObject::class_); case InlinableNative::IntrinsicGetNextMapEntryForIterator: return inlineGetNextEntryForIterator(callInfo, MGetNextEntryForIterator::Map); // Set intrinsics. + case InlinableNative::IntrinsicGuardToSetObject: + return inlineGuardToClass(callInfo, &SetObject::class_); case InlinableNative::IntrinsicGetNextSetEntryForIterator: return inlineGetNextEntryForIterator(callInfo, MGetNextEntryForIterator::Set); @@ -2218,6 +2222,37 @@ IonBuilder::inlineHasClass(CallInfo& callInfo, } IonBuilder::InliningStatus +IonBuilder::inlineGuardToClass(CallInfo& callInfo, const Class* clasp) +{ + MOZ_ASSERT(!callInfo.constructing()); + MOZ_ASSERT(callInfo.argc() == 1); + + if (callInfo.getArg(0)->type() != MIRType::Object) + return InliningStatus_NotInlined; + + if (getInlineReturnType() != MIRType::ObjectOrNull && + getInlineReturnType() != MIRType::Object) + { + return InliningStatus_NotInlined; + } + + TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet(); + const Class* knownClass = types ? types->getKnownClass(constraints()) : nullptr; + + if (knownClass && knownClass == clasp) { + current->push(callInfo.getArg(0)); + } else { + MGuardToClass* guardToClass = MGuardToClass::New(alloc(), callInfo.getArg(0), + clasp, getInlineReturnType()); + current->add(guardToClass); + current->push(guardToClass); + } + + callInfo.setImplicitlyUsedUnchecked(); + return InliningStatus_Inlined; +} + +IonBuilder::InliningStatus IonBuilder::inlineGetNextEntryForIterator(CallInfo& callInfo, MGetNextEntryForIterator::Mode mode) { if (callInfo.argc() != 2 || callInfo.constructing()) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 2de91e2df..6ec05af76 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -13192,6 +13192,48 @@ class MHasClass } }; +class MGuardToClass + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + const Class* class_; + + MGuardToClass(MDefinition* object, const Class* clasp, MIRType resultType) + : MUnaryInstruction(object) + , class_(clasp) + { + MOZ_ASSERT(object->type() == MIRType::Object || + (object->type() == MIRType::Value && object->mightBeType(MIRType::Object))); + MOZ_ASSERT(resultType == MIRType::Object || resultType == MIRType::ObjectOrNull); + setResultType(resultType); + setMovable(); + if (resultType == MIRType::Object) { + // We will bail out if the class type is incorrect, + // so we need to ensure we don't eliminate this instruction + setGuard(); + } + } + + public: + INSTRUCTION_HEADER(GuardToClass) + TRIVIAL_NEW_WRAPPERS + NAMED_OPERANDS((0, object)) + + const Class* getClass() const { + return class_; + } + AliasSet getAliasSet() const override { + return AliasSet::None(); + } + bool congruentTo(const MDefinition* ins) const override { + if (!ins->isGuardToClass()) + return false; + if (getClass() != ins->toGuardToClass()->getClass()) + return false; + return congruentIfOperandsEqual(ins); + } +}; + class MCheckReturn : public MBinaryInstruction, public BoxInputsPolicy::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index bb2ab8190..fddc1e637 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -272,6 +272,7 @@ namespace jit { _(IsCallable) \ _(IsObject) \ _(HasClass) \ + _(GuardToClass) \ _(CopySign) \ _(Rotate) \ _(NewDerivedTypedObject) \ diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index f633b9b7b..9dbbe7624 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -2214,6 +2214,12 @@ MacroAssembler::finish() } MacroAssemblerSpecific::finish(); + + MOZ_RELEASE_ASSERT(size() <= MaxCodeBytesPerProcess, + "AssemblerBuffer should ensure we don't exceed MaxCodeBytesPerProcess"); + + if (bytesNeeded() > MaxCodeBytesPerProcess) + setOOM(); } void diff --git a/js/src/jit/ProcessExecutableMemory.cpp b/js/src/jit/ProcessExecutableMemory.cpp index 71c2ab0dc..301541541 100644 --- a/js/src/jit/ProcessExecutableMemory.cpp +++ b/js/src/jit/ProcessExecutableMemory.cpp @@ -385,14 +385,6 @@ class PageBitSet #endif }; -// Limit on the number of bytes of executable memory to prevent JIT spraying -// attacks. -#if JS_BITS_PER_WORD == 32 -static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024; -#else -static const size_t MaxCodeBytesPerProcess = 1 * 1024 * 1024 * 1024; -#endif - // Per-process executable memory allocator. It reserves a block of memory of // MaxCodeBytesPerProcess bytes, then allocates/deallocates pages from that. // diff --git a/js/src/jit/ProcessExecutableMemory.h b/js/src/jit/ProcessExecutableMemory.h index 078ce7cb7..a0e2fab98 100644 --- a/js/src/jit/ProcessExecutableMemory.h +++ b/js/src/jit/ProcessExecutableMemory.h @@ -17,6 +17,14 @@ namespace jit { // alignment though. static const size_t ExecutableCodePageSize = 64 * 1024; +// Limit on the number of bytes of executable memory to prevent JIT spraying +// attacks. +#if JS_BITS_PER_WORD == 32 +static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024; +#else +static const size_t MaxCodeBytesPerProcess = 1 * 1024 * 1024 * 1024; +#endif + enum class ProtectionSetting { Protected, // Not readable, writable, or executable. Writable, diff --git a/js/src/jit/RegisterSets.h b/js/src/jit/RegisterSets.h index 0a4045dd7..08ae53f16 100644 --- a/js/src/jit/RegisterSets.h +++ b/js/src/jit/RegisterSets.h @@ -226,13 +226,13 @@ class ConstantOrRegister // Space to hold either a Value or a TypedOrValueRegister. union U { - Value constant; + JS::UninitializedValue constant; TypedOrValueRegister reg; } data; - const Value& dataValue() const { + Value dataValue() const { MOZ_ASSERT(constant()); - return data.constant; + return data.constant.asValueRef(); } void setDataValue(const Value& value) { MOZ_ASSERT(constant()); @@ -268,7 +268,7 @@ class ConstantOrRegister return constant_; } - const Value& value() const { + Value value() const { return dataValue(); } diff --git a/js/src/jit/RematerializedFrame.cpp b/js/src/jit/RematerializedFrame.cpp index cb324220c..32fad1267 100644 --- a/js/src/jit/RematerializedFrame.cpp +++ b/js/src/jit/RematerializedFrame.cpp @@ -61,9 +61,17 @@ RematerializedFrame::New(JSContext* cx, uint8_t* top, InlineFrameIterator& iter, { unsigned numFormals = iter.isFunctionFrame() ? iter.calleeTemplate()->nargs() : 0; unsigned argSlots = Max(numFormals, iter.numActualArgs()); - size_t numBytes = sizeof(RematerializedFrame) + - (argSlots + iter.script()->nfixed()) * sizeof(Value) - - sizeof(Value); // 1 Value included in sizeof(RematerializedFrame) + unsigned extraSlots = argSlots + iter.script()->nfixed(); + + // One Value slot is included in sizeof(RematerializedFrame), so we can + // reduce the extra slot count by one. However, if there are zero slot + // allocations total, then reducing the slots by one will lead to + // the memory allocation being smaller than sizeof(RematerializedFrame). + if (extraSlots > 0) + extraSlots -= 1; + + size_t numBytes = sizeof(RematerializedFrame) + (extraSlots * sizeof(Value)); + MOZ_ASSERT(numBytes >= sizeof(RematerializedFrame)); void* buf = cx->pod_calloc<uint8_t>(numBytes); if (!buf) diff --git a/js/src/jit/shared/IonAssemblerBuffer.h b/js/src/jit/shared/IonAssemblerBuffer.h index cc20e26d2..3a6552696 100644 --- a/js/src/jit/shared/IonAssemblerBuffer.h +++ b/js/src/jit/shared/IonAssemblerBuffer.h @@ -181,6 +181,10 @@ class AssemblerBuffer protected: virtual Slice* newSlice(LifoAlloc& a) { + if (size() > MaxCodeBytesPerProcess - sizeof(Slice)) { + fail_oom(); + return nullptr; + } Slice* tmp = static_cast<Slice*>(a.alloc(sizeof(Slice))); if (!tmp) { fail_oom(); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 9dcb527c5..f4adcc63c 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -7867,6 +7867,29 @@ class LHasClass : public LInstructionHelper<1, 1, 0> } }; +class LGuardToClass : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(GuardToClass); + explicit LGuardToClass(const LAllocation& lhs, const LDefinition& temp) + { + setOperand(0, lhs); + setTemp(0, temp); + } + + const LAllocation* lhs() { + return getOperand(0); + } + + const LDefinition* temp() { + return getTemp(0); + } + + MGuardToClass* mir() const { + return mir_->toGuardToClass(); + } +}; + template<size_t Defs, size_t Ops> class LWasmSelectBase : public LInstructionHelper<Defs, Ops, 0> { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 3eea1b449..fe2ab5ea3 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -386,6 +386,7 @@ _(IsObject) \ _(IsObjectAndBranch) \ _(HasClass) \ + _(GuardToClass) \ _(RecompileCheck) \ _(MemoryBarrier) \ _(AssertRangeI) \ diff --git a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h index 8cb557784..fe678fc7d 100644 --- a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h +++ b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h @@ -68,6 +68,33 @@ namespace js { namespace jit { + // AllocPolicy for AssemblerBuffer. OOMs when trying to allocate more than + // MaxCodeBytesPerProcess bytes. Use private inheritance to make sure we + // explicitly have to expose SystemAllocPolicy methods. + class AssemblerBufferAllocPolicy : private SystemAllocPolicy + { + public: + using SystemAllocPolicy::checkSimulatedOOM; + using SystemAllocPolicy::reportAllocOverflow; + using SystemAllocPolicy::free_; + + template <typename T> T* pod_realloc(T* p, size_t oldSize, size_t newSize) { + static_assert(sizeof(T) == 1, + "AssemblerBufferAllocPolicy should only be used with byte vectors"); + MOZ_ASSERT(oldSize <= MaxCodeBytesPerProcess); + if (MOZ_UNLIKELY(newSize > MaxCodeBytesPerProcess)) + return nullptr; + return SystemAllocPolicy::pod_realloc<T>(p, oldSize, newSize); + } + template <typename T> T* pod_malloc(size_t numElems) { + static_assert(sizeof(T) == 1, + "AssemblerBufferAllocPolicy should only be used with byte vectors"); + if (MOZ_UNLIKELY(numElems > MaxCodeBytesPerProcess)) + return nullptr; + return SystemAllocPolicy::pod_malloc<T>(numElems); + } + }; + class AssemblerBuffer { template<size_t size, typename T> @@ -93,6 +120,9 @@ namespace jit { void ensureSpace(size_t space) { + // This should only be called with small |space| values to ensure + // we don't overflow below. + MOZ_ASSERT(space <= 16); if (MOZ_UNLIKELY(!m_buffer.reserve(m_buffer.length() + space))) oomDetected(); } @@ -168,7 +198,7 @@ namespace jit { m_buffer.clear(); } - PageProtectingVector<unsigned char, 256, SystemAllocPolicy> m_buffer; + PageProtectingVector<unsigned char, 256, AssemblerBufferAllocPolicy> m_buffer; bool m_oom; }; diff --git a/js/src/js.msg b/js/src/js.msg index 8d492f523..a276dab94 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -474,6 +474,8 @@ MSG_DEF(JSMSG_INTL_OBJECT_NOT_INITED, 3, JSEXN_TYPEERR, "Intl.{0}.prototype.{1} MSG_DEF(JSMSG_INTL_OBJECT_REINITED, 0, JSEXN_TYPEERR, "can't initialize object twice as an object of an Intl constructor") MSG_DEF(JSMSG_INVALID_CURRENCY_CODE, 1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}") MSG_DEF(JSMSG_INVALID_DIGITS_VALUE, 1, JSEXN_RANGEERR, "invalid digits value: {0}") +MSG_DEF(JSMSG_INVALID_KEYS_TYPE, 0, JSEXN_TYPEERR, "calendar info keys must be an object or undefined") +MSG_DEF(JSMSG_INVALID_KEY, 1, JSEXN_RANGEERR, "invalid key: {0}") MSG_DEF(JSMSG_INVALID_LANGUAGE_TAG, 1, JSEXN_RANGEERR, "invalid language tag: {0}") MSG_DEF(JSMSG_INVALID_LOCALES_ELEMENT, 0, JSEXN_TYPEERR, "invalid element in locales argument") MSG_DEF(JSMSG_INVALID_LOCALE_MATCHER, 1, JSEXN_RANGEERR, "invalid locale matcher in supportedLocalesOf(): {0}") diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index f7a6080aa..277a145b0 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -135,7 +135,7 @@ LOCAL_INCLUDES += [ '..', ] -if CONFIG['ENABLE_INTL_API'] and CONFIG['MOZ_ICU_DATA_ARCHIVE']: +if CONFIG['MOZ_ICU_DATA_ARCHIVE']: # The ICU libraries linked into libmozjs will not include the ICU data, # so link it directly. USE_LIBS += ['icudata'] diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 989abe47c..c1195cc00 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5222,7 +5222,7 @@ JS_ResetDefaultLocale(JSContext* cx); struct JSLocaleCallbacks { JSLocaleToUpperCase localeToUpperCase; JSLocaleToLowerCase localeToLowerCase; - JSLocaleCompare localeCompare; // not used #if EXPOSE_INTL_API + JSLocaleCompare localeCompare; // not used JSLocaleToUnicode localeToUnicode; }; diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index ccaeda2a3..52294a5df 100755 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -354,10 +354,22 @@ MakeDate(double day, double time) JS_PUBLIC_API(double) JS::MakeDate(double year, unsigned month, unsigned day) { + MOZ_ASSERT(month <= 11); + MOZ_ASSERT(day >= 1 && day <= 31); + return ::MakeDate(MakeDay(year, month, day), 0); } JS_PUBLIC_API(double) +JS::MakeDate(double year, unsigned month, unsigned day, double time) +{ + MOZ_ASSERT(month <= 11); + MOZ_ASSERT(day >= 1 && day <= 31); + + return ::MakeDate(MakeDay(year, month, day), time); +} + +JS_PUBLIC_API(double) JS::YearFromTime(double time) { return ::YearFromTime(time); @@ -2731,77 +2743,6 @@ ToLocaleFormatHelper(JSContext* cx, HandleObject obj, const char* format, Mutabl return true; } -#if !EXPOSE_INTL_API -/* ES5 15.9.5.5. */ -MOZ_ALWAYS_INLINE bool -date_toLocaleString_impl(JSContext* cx, const CallArgs& args) -{ - /* - * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k - * with msvc; '%#c' requests that a full year be used in the result string. - */ - static const char format[] = -#if defined(_WIN32) && !defined(__MWERKS__) - "%#c" -#else - "%c" -#endif - ; - - Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); - return ToLocaleFormatHelper(cx, dateObj, format, args.rval()); -} - -static bool -date_toLocaleString(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod<IsDate, date_toLocaleString_impl>(cx, args); -} - -/* ES5 15.9.5.6. */ -MOZ_ALWAYS_INLINE bool -date_toLocaleDateString_impl(JSContext* cx, const CallArgs& args) -{ - /* - * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k - * with msvc; '%#x' requests that a full year be used in the result string. - */ - static const char format[] = -#if defined(_WIN32) && !defined(__MWERKS__) - "%#x" -#else - "%x" -#endif - ; - - Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); - return ToLocaleFormatHelper(cx, dateObj, format, args.rval()); -} - -static bool -date_toLocaleDateString(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod<IsDate, date_toLocaleDateString_impl>(cx, args); -} - -/* ES5 15.9.5.7. */ -MOZ_ALWAYS_INLINE bool -date_toLocaleTimeString_impl(JSContext* cx, const CallArgs& args) -{ - Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); - return ToLocaleFormatHelper(cx, dateObj, "%X", args.rval()); -} - -static bool -date_toLocaleTimeString(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod<IsDate, date_toLocaleTimeString_impl>(cx, args); -} -#endif /* !EXPOSE_INTL_API */ - MOZ_ALWAYS_INLINE bool date_toLocaleFormat_impl(JSContext* cx, const CallArgs& args) { @@ -3025,15 +2966,9 @@ static const JSFunctionSpec date_methods[] = { JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1,0), JS_FN("toUTCString", date_toGMTString, 0,0), JS_FN("toLocaleFormat", date_toLocaleFormat, 0,0), -#if EXPOSE_INTL_API JS_SELF_HOSTED_FN(js_toLocaleString_str, "Date_toLocaleString", 0,0), JS_SELF_HOSTED_FN("toLocaleDateString", "Date_toLocaleDateString", 0,0), JS_SELF_HOSTED_FN("toLocaleTimeString", "Date_toLocaleTimeString", 0,0), -#else - JS_FN(js_toLocaleString_str, date_toLocaleString, 0,0), - JS_FN("toLocaleDateString", date_toLocaleDateString, 0,0), - JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0,0), -#endif JS_FN("toDateString", date_toDateString, 0,0), JS_FN("toTimeString", date_toTimeString, 0,0), JS_FN("toISOString", date_toISOString, 0,0), diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 45301dac8..3d4dae9bb 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2156,7 +2156,7 @@ ArenasToUpdate::getArenasToUpdate(AutoLockHelperThreadState& lock, unsigned maxL return { begin, last->next }; } -struct UpdatePointersTask : public GCParallelTask +struct UpdatePointersTask : public GCParallelTaskHelper<UpdatePointersTask> { // Maximum number of arenas to update in one block. #ifdef DEBUG @@ -2172,14 +2172,13 @@ struct UpdatePointersTask : public GCParallelTask arenas_.end = nullptr; } - ~UpdatePointersTask() override { join(); } + void run(); private: JSRuntime* rt_; ArenasToUpdate* source_; ArenaListSegment arenas_; - virtual void run() override; bool getArenasToUpdate(); void updateArenas(); }; @@ -2276,7 +2275,7 @@ GCRuntime::updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, s for (size_t i = 0; i < bgTaskCount && !bgArenas.done(); i++) { bgTasks[i].emplace(rt, &bgArenas, lock); startTask(*bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS, lock); - tasksStarted = i; + tasksStarted++; } } @@ -2985,7 +2984,6 @@ js::gc::BackgroundDecommitTask::run() AutoLockGC lock(runtime); for (Chunk* chunk : toDecommit) { - // The arena list is not doubly-linked, so we have to work in the free // list order and not in the natural order. while (chunk->info.numArenasFreeCommitted) { @@ -4359,7 +4357,8 @@ GCRuntime::endMarkingZoneGroup() marker.setMarkColorBlack(); } -class GCSweepTask : public GCParallelTask +template <typename Derived> +class GCSweepTask : public GCParallelTaskHelper<Derived> { GCSweepTask(const GCSweepTask&) = delete; @@ -4369,13 +4368,13 @@ class GCSweepTask : public GCParallelTask public: explicit GCSweepTask(JSRuntime* rt) : runtime(rt) {} GCSweepTask(GCSweepTask&& other) - : GCParallelTask(mozilla::Move(other)), + : GCParallelTaskHelper<Derived>(mozilla::Move(other)), runtime(other.runtime) {} }; // Causes the given WeakCache to be swept when run. -class SweepWeakCacheTask : public GCSweepTask +class SweepWeakCacheTask : public GCSweepTask<SweepWeakCacheTask> { JS::WeakCache<void*>& cache; @@ -4387,15 +4386,15 @@ class SweepWeakCacheTask : public GCSweepTask : GCSweepTask(mozilla::Move(other)), cache(other.cache) {} - void run() override { + void run() { cache.sweep(); } }; #define MAKE_GC_SWEEP_TASK(name) \ - class name : public GCSweepTask { \ - void run() override; \ + class name : public GCSweepTask<name> { \ public: \ + void run(); \ explicit name (JSRuntime* rt) : GCSweepTask(rt) {} \ } MAKE_GC_SWEEP_TASK(SweepAtomsTask); @@ -4447,7 +4446,8 @@ SweepMiscTask::run() } void -GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked) +GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, + AutoLockHelperThreadState& locked) { if (!task.startWithLockHeld(locked)) { AutoUnlockHelperThreadState unlock(locked); @@ -4457,7 +4457,8 @@ GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperT } void -GCRuntime::joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked) +GCRuntime::joinTask(GCParallelTask& task, gcstats::Phase phase, + AutoLockHelperThreadState& locked) { gcstats::AutoPhase ap(stats, task, phase); task.joinWithLockHeld(locked); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 7ad176d84..d3cf31fe7 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -12,6 +12,7 @@ #include "mozilla/Atomics.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/Move.h" #include "mozilla/TypeTraits.h" #include "js/GCAPI.h" @@ -936,10 +937,19 @@ class GCHelperState }; // A generic task used to dispatch work to the helper thread system. -// Users should derive from GCParallelTask add what data they need and -// override |run|. +// Users supply a function pointer to call. +// +// Note that we don't use virtual functions here because destructors can write +// the vtable pointer on entry, which can causes races if synchronization +// happens there. class GCParallelTask { + public: + using TaskFunc = void (*)(GCParallelTask*); + + private: + TaskFunc func_; + // The state of the parallel computation. enum TaskState { NotStarted, @@ -956,19 +966,24 @@ class GCParallelTask // A flag to signal a request for early completion of the off-thread task. mozilla::Atomic<bool> cancel_; - virtual void run() = 0; - public: - GCParallelTask() : state(NotStarted), duration_(0) {} + explicit GCParallelTask(TaskFunc func) + : func_(func), + state(NotStarted), + duration_(0), + cancel_(false) + {} + GCParallelTask(GCParallelTask&& other) - : state(other.state), + : func_(other.func_), + state(other.state), duration_(0), cancel_(false) {} // Derived classes must override this to ensure that join() gets called // before members get destructed. - virtual ~GCParallelTask(); + ~GCParallelTask(); // Time spent in the most recent invocation of this task. int64_t duration() const { return duration_; } @@ -997,12 +1012,34 @@ class GCParallelTask bool isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const; bool isRunning() const; + void runTask() { + func_(this); + } + // This should be friended to HelperThread, but cannot be because it // would introduce several circular dependencies. public: void runFromHelperThread(AutoLockHelperThreadState& locked); }; +// CRTP template to handle cast to derived type when calling run(). +template <typename Derived> +class GCParallelTaskHelper : public GCParallelTask +{ + public: + GCParallelTaskHelper() + : GCParallelTask(&runTaskTyped) + {} + GCParallelTaskHelper(GCParallelTaskHelper&& other) + : GCParallelTask(mozilla::Move(other)) + {} + + private: + static void runTaskTyped(GCParallelTask* task) { + static_cast<Derived*>(task)->run(); + } +}; + typedef void (*IterateChunkCallback)(JSRuntime* rt, void* data, gc::Chunk* chunk); typedef void (*IterateZoneCallback)(JSRuntime* rt, void* data, JS::Zone* zone); typedef void (*IterateArenaCallback)(JSRuntime* rt, void* data, gc::Arena* arena, diff --git a/js/src/jsnativestack.cpp b/js/src/jsnativestack.cpp index 05928ea3d..166a5a4f7 100644 --- a/js/src/jsnativestack.cpp +++ b/js/src/jsnativestack.cpp @@ -21,6 +21,18 @@ # include <unistd.h> # endif +# if defined(XP_LINUX) && !defined(ANDROID) && defined(__GLIBC__) +# include <dlfcn.h> +# include <sys/syscall.h> +# include <sys/types.h> +# include <unistd.h> +static pid_t +gettid() +{ + return syscall(__NR_gettid); +} +# endif + #else # error "Unsupported platform" @@ -88,6 +100,52 @@ js::GetNativeStackBaseImpl() context.uc_stack.ss_size; } +#elif defined(XP_LINUX) && !defined(ANDROID) && defined(__GLIBC__) +void* +js::GetNativeStackBaseImpl() +{ + // On the main thread, get stack base from glibc's __libc_stack_end rather than pthread APIs + // to avoid filesystem calls /proc/self/maps. Non-main threads spawned with pthreads can read + // this information directly from their pthread struct, but when using the pthreads API, the + // main thread must go parse /proc/self/maps to figure the mapped stack address space ranges. + // We want to avoid reading from /proc/ so that the application can run in restricted + // environments where /proc may not be mounted (e.g. chroot). + if (gettid() == getpid()) { + void** pLibcStackEnd = (void**)dlsym(RTLD_DEFAULT, "__libc_stack_end"); + + // If __libc_stack_end is not found, architecture specific frame pointer hopping will need + // to be implemented. + MOZ_RELEASE_ASSERT(pLibcStackEnd, "__libc_stack_end unavailable, unable to setup stack range for JS."); + void* stackBase = *pLibcStackEnd; + MOZ_RELEASE_ASSERT(stackBase, "Invalid stack base, unable to setup stack range for JS."); + + // We don't need to fix stackBase, as it already roughly points to beginning of the stack. + return stackBase; + } + + // Non-main threads have the required info stored in memory, so no filesystem calls are made. + pthread_t thread = pthread_self(); + pthread_attr_t sattr; + pthread_attr_init(&sattr); + pthread_getattr_np(thread, &sattr); + + // stackBase will be the *lowest* address on all architectures. + void* stackBase = nullptr; + size_t stackSize = 0; + int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize); + if (rc) { + MOZ_CRASH("Call to pthread_attr_getstack failed, unable to setup stack range for JS."); + } + MOZ_RELEASE_ASSERT(stackBase, "Invalid stack base, unable to setup stack range for JS."); + pthread_attr_destroy(&sattr); + +# if JS_STACK_GROWTH_DIRECTION > 0 + return stackBase; +# else + return static_cast<char*>(stackBase) + stackSize; +# endif +} + #else /* XP_UNIX */ void* @@ -156,11 +214,15 @@ js::GetNativeStackBaseImpl() // the truth. rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize); # else + // Use the default pthread_attr_getstack() call. Note that this function + // differs between libc implementations and could imply /proc access etc. + // which may not work in restricted environments. rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize); # endif - if (rc) - MOZ_CRASH(); - MOZ_ASSERT(stackBase); + if (rc) { + MOZ_CRASH("Call to pthread_attr_getstack failed, unable to setup stack range for JS."); + } + MOZ_RELEASE_ASSERT(stackBase, "Invalid stack base, unable to setup stack range for JS."); pthread_attr_destroy(&sattr); # if JS_STACK_GROWTH_DIRECTION > 0 diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 8885737f7..28ed15159 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -724,141 +724,6 @@ js::num_toString(JSContext* cx, unsigned argc, Value* vp) return CallNonGenericMethod<IsNumber, num_toString_impl>(cx, args); } -#if !EXPOSE_INTL_API -MOZ_ALWAYS_INLINE bool -num_toLocaleString_impl(JSContext* cx, const CallArgs& args) -{ - MOZ_ASSERT(IsNumber(args.thisv())); - - double d = Extract(args.thisv()); - - RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10)); - if (!str) { - JS_ReportOutOfMemory(cx); - return false; - } - - /* - * Create the string, move back to bytes to make string twiddling - * a bit easier and so we can insert platform charset seperators. - */ - JSAutoByteString numBytes(cx, str); - if (!numBytes) - return false; - const char* num = numBytes.ptr(); - if (!num) - return false; - - /* - * Find the first non-integer value, whether it be a letter as in - * 'Infinity', a decimal point, or an 'e' from exponential notation. - */ - const char* nint = num; - if (*nint == '-') - nint++; - while (*nint >= '0' && *nint <= '9') - nint++; - int digits = nint - num; - const char* end = num + digits; - if (!digits) { - args.rval().setString(str); - return true; - } - - JSRuntime* rt = cx->runtime(); - size_t thousandsLength = strlen(rt->thousandsSeparator); - size_t decimalLength = strlen(rt->decimalSeparator); - - /* Figure out how long resulting string will be. */ - int buflen = strlen(num); - if (*nint == '.') - buflen += decimalLength - 1; /* -1 to account for existing '.' */ - - const char* numGrouping; - const char* tmpGroup; - numGrouping = tmpGroup = rt->numGrouping; - int remainder = digits; - if (*num == '-') - remainder--; - - while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') { - if (*tmpGroup >= remainder) - break; - buflen += thousandsLength; - remainder -= *tmpGroup; - tmpGroup++; - } - - int nrepeat; - if (*tmpGroup == '\0' && *numGrouping != '\0') { - nrepeat = (remainder - 1) / tmpGroup[-1]; - buflen += thousandsLength * nrepeat; - remainder -= nrepeat * tmpGroup[-1]; - } else { - nrepeat = 0; - } - tmpGroup--; - - char* buf = cx->pod_malloc<char>(buflen + 1); - if (!buf) - return false; - - char* tmpDest = buf; - const char* tmpSrc = num; - - while (*tmpSrc == '-' || remainder--) { - MOZ_ASSERT(tmpDest - buf < buflen); - *tmpDest++ = *tmpSrc++; - } - while (tmpSrc < end) { - MOZ_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen); - strcpy(tmpDest, rt->thousandsSeparator); - tmpDest += thousandsLength; - MOZ_ASSERT(tmpDest - buf + *tmpGroup <= buflen); - js_memcpy(tmpDest, tmpSrc, *tmpGroup); - tmpDest += *tmpGroup; - tmpSrc += *tmpGroup; - if (--nrepeat < 0) - tmpGroup--; - } - - if (*nint == '.') { - MOZ_ASSERT(tmpDest - buf + ptrdiff_t(decimalLength) <= buflen); - strcpy(tmpDest, rt->decimalSeparator); - tmpDest += decimalLength; - MOZ_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen); - strcpy(tmpDest, nint + 1); - } else { - MOZ_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen); - strcpy(tmpDest, nint); - } - - if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode) { - Rooted<Value> v(cx, StringValue(str)); - bool ok = !!cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, &v); - if (ok) - args.rval().set(v); - js_free(buf); - return ok; - } - - str = NewStringCopyN<CanGC>(cx, buf, buflen); - js_free(buf); - if (!str) - return false; - - args.rval().setString(str); - return true; -} - -static bool -num_toLocaleString(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod<IsNumber, num_toLocaleString_impl>(cx, args); -} -#endif /* !EXPOSE_INTL_API */ - MOZ_ALWAYS_INLINE bool num_valueOf_impl(JSContext* cx, const CallArgs& args) { @@ -1075,11 +940,7 @@ static const JSFunctionSpec number_methods[] = { JS_FN(js_toSource_str, num_toSource, 0, 0), #endif JS_FN(js_toString_str, num_toString, 1, 0), -#if EXPOSE_INTL_API JS_SELF_HOSTED_FN(js_toLocaleString_str, "Number_toLocaleString", 0,0), -#else - JS_FN(js_toLocaleString_str, num_toLocaleString, 0,0), -#endif JS_FN(js_valueOf_str, num_valueOf, 0, 0), JS_FN("toFixed", num_toFixed, 1, 0), JS_FN("toExponential", num_toExponential, 1, 0), @@ -1130,76 +991,11 @@ js::FIX_FPU() #endif } -bool +void js::InitRuntimeNumberState(JSRuntime* rt) { FIX_FPU(); - - // XXX If EXPOSE_INTL_API becomes true all the time at some point, - // js::InitRuntimeNumberState is no longer fallible, and we should - // change its return type. -#if !EXPOSE_INTL_API - /* Copy locale-specific separators into the runtime strings. */ - const char* thousandsSeparator; - const char* decimalPoint; - const char* grouping; -#ifdef HAVE_LOCALECONV - struct lconv* locale = localeconv(); - thousandsSeparator = locale->thousands_sep; - decimalPoint = locale->decimal_point; - grouping = locale->grouping; -#else - thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP"); - decimalPoint = getenv("LOCALE_DECIMAL_POINT"); - grouping = getenv("LOCALE_GROUPING"); -#endif - if (!thousandsSeparator) - thousandsSeparator = "'"; - if (!decimalPoint) - decimalPoint = "."; - if (!grouping) - grouping = "\3\0"; - - /* - * We use single malloc to get the memory for all separator and grouping - * strings. - */ - size_t thousandsSeparatorSize = strlen(thousandsSeparator) + 1; - size_t decimalPointSize = strlen(decimalPoint) + 1; - size_t groupingSize = strlen(grouping) + 1; - - char* storage = js_pod_malloc<char>(thousandsSeparatorSize + - decimalPointSize + - groupingSize); - if (!storage) - return false; - - js_memcpy(storage, thousandsSeparator, thousandsSeparatorSize); - rt->thousandsSeparator = storage; - storage += thousandsSeparatorSize; - - js_memcpy(storage, decimalPoint, decimalPointSize); - rt->decimalSeparator = storage; - storage += decimalPointSize; - - js_memcpy(storage, grouping, groupingSize); - rt->numGrouping = grouping; -#endif /* !EXPOSE_INTL_API */ - return true; -} - -#if !EXPOSE_INTL_API -void -js::FinishRuntimeNumberState(JSRuntime* rt) -{ - /* - * The free also releases the memory for decimalSeparator and numGrouping - * strings. - */ - char* storage = const_cast<char*>(rt->thousandsSeparator); - js_free(storage); } -#endif JSObject* js::InitNumberClass(JSContext* cx, HandleObject obj) diff --git a/js/src/jsnum.h b/js/src/jsnum.h index 62b3d617f..8dff45f69 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -34,13 +34,7 @@ namespace js { class StringBuffer; -extern MOZ_MUST_USE bool -InitRuntimeNumberState(JSRuntime* rt); - -#if !EXPOSE_INTL_API -extern void -FinishRuntimeNumberState(JSRuntime* rt); -#endif +void InitRuntimeNumberState(JSRuntime* rt); /* Initialize the Number class, returning its prototype object. */ extern JSObject* diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index f409dce95..dc7cdb85a 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -37,11 +37,7 @@ #define TYPED_ARRAY_CLASP(type) (&TypedArrayObject::classes[Scalar::type]) #define ERROR_CLASP(type) (&ErrorObject::classes[type]) -#ifdef EXPOSE_INTL_API #define IF_INTL(real,imaginary) real -#else -#define IF_INTL(real,imaginary) imaginary -#endif #ifdef ENABLE_BINARYDATA #define IF_BDATA(real,imaginary) real diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 7adeed620..4151d012b 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -36,9 +36,7 @@ #include "jit/InlinableNatives.h" #include "js/Conversions.h" #include "js/UniquePtr.h" -#if ENABLE_INTL_API #include "unicode/unorm.h" -#endif #include "vm/GlobalObject.h" #include "vm/Interpreter.h" #include "vm/Opcodes.h" @@ -899,38 +897,6 @@ js::str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp) return ToUpperCaseHelper(cx, args); } -#if !EXPOSE_INTL_API -bool -js::str_localeCompare(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - RootedString str(cx, ToStringForStringFunction(cx, args.thisv())); - if (!str) - return false; - - RootedString thatStr(cx, ToString<CanGC>(cx, args.get(0))); - if (!thatStr) - return false; - - if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeCompare) { - RootedValue result(cx); - if (!cx->runtime()->localeCallbacks->localeCompare(cx, str, thatStr, &result)) - return false; - - args.rval().set(result); - return true; - } - - int32_t result; - if (!CompareStrings(cx, str, thatStr, &result)) - return false; - - args.rval().setInt32(result); - return true; -} -#endif - -#if EXPOSE_INTL_API /* ES6 20140210 draft 21.1.3.12. */ bool js::str_normalize(JSContext* cx, unsigned argc, Value* vp) @@ -1007,7 +973,6 @@ js::str_normalize(JSContext* cx, unsigned argc, Value* vp) args.rval().setString(ns); return true; } -#endif bool js::str_charAt(JSContext* cx, unsigned argc, Value* vp) @@ -1569,7 +1534,7 @@ RopeMatch(JSContext* cx, JSRope* text, JSLinearString* pat, int* match) return true; } -/* ES6 draft rc4 21.1.3.7. */ +/* ES6 2015 ST 21.1.3.7 String.prototype.includes */ bool js::str_includes(JSContext* cx, unsigned argc, Value* vp) { @@ -1626,6 +1591,13 @@ js::str_includes(JSContext* cx, unsigned argc, Value* vp) return true; } +/* ES6 draft <RC4 String.prototype.contains for compatibility */ +bool +js::str_contains(JSContext* cx, unsigned argc, Value* vp) +{ + return js::str_includes(cx, argc, vp); +} + /* ES6 20120927 draft 15.5.4.7. */ bool js::str_indexOf(JSContext* cx, unsigned argc, Value* vp) @@ -2590,6 +2562,7 @@ static const JSFunctionSpec string_methods[] = { JS_SELF_HOSTED_FN("padEnd", "String_pad_end", 2,0), JS_SELF_HOSTED_FN("codePointAt", "String_codePointAt", 1,0), JS_FN("includes", str_includes, 1,0), + JS_FN("contains", str_contains, 1,0), JS_FN("indexOf", str_indexOf, 1,0), JS_FN("lastIndexOf", str_lastIndexOf, 1,0), JS_FN("startsWith", str_startsWith, 1,0), @@ -2599,15 +2572,9 @@ static const JSFunctionSpec string_methods[] = { JS_FN("trimRight", str_trimRight, 0,0), JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,0), JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,0), -#if EXPOSE_INTL_API JS_SELF_HOSTED_FN("localeCompare", "String_localeCompare", 1,0), -#else - JS_FN("localeCompare", str_localeCompare, 1,0), -#endif JS_SELF_HOSTED_FN("repeat", "String_repeat", 1,0), -#if EXPOSE_INTL_API JS_FN("normalize", str_normalize, 0,0), -#endif /* Perl-ish methods (search is actually Python-esque). */ JS_SELF_HOSTED_FN("match", "String_match", 1,0), @@ -2918,9 +2885,7 @@ static const JSFunctionSpec string_static_methods[] = { JS_SELF_HOSTED_FN("trimRight", "String_static_trimRight", 1,0), JS_SELF_HOSTED_FN("toLocaleLowerCase","String_static_toLocaleLowerCase",1,0), JS_SELF_HOSTED_FN("toLocaleUpperCase","String_static_toLocaleUpperCase",1,0), -#if EXPOSE_INTL_API JS_SELF_HOSTED_FN("normalize", "String_static_normalize", 1,0), -#endif JS_SELF_HOSTED_FN("concat", "String_static_concat", 2,0), JS_SELF_HOSTED_FN("localeCompare", "String_static_localeCompare", 2,0), diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 3b92aa21b..118118839 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -9,6 +9,7 @@ #include "mozilla/HashFunctions.h" #include "mozilla/PodOperations.h" +#include "mozilla/TextUtils.h" #include <stdio.h> @@ -95,7 +96,7 @@ struct JSSubString { #define JS7_UNOCT(c) (JS7_UNDEC(c)) #define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) #define JS7_UNHEX(c) (unsigned)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') -#define JS7_ISLET(c) ((c) < 128 && isalpha(c)) +#define JS7_ISLET(c) (mozilla::IsAsciiAlpha(c)) extern size_t js_strlen(const char16_t* s); @@ -377,13 +378,8 @@ str_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp); extern bool str_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp); -#if !EXPOSE_INTL_API -extern bool -str_localeCompare(JSContext* cx, unsigned argc, Value* vp); -#else extern bool str_normalize(JSContext* cx, unsigned argc, Value* vp); -#endif extern bool str_concat(JSContext* cx, unsigned argc, Value* vp); diff --git a/js/src/jstypes.h b/js/src/jstypes.h index 6593d2067..75774e5b8 100644 --- a/js/src/jstypes.h +++ b/js/src/jstypes.h @@ -160,10 +160,6 @@ # if defined(__64BIT__) # define JS_64BIT # endif -#elif defined(__HP_cc) || defined(__HP_aCC) /* HP-UX cc/aCC */ -# if defined(__LP64__) -# define JS_64BIT -# endif #else # error "Implement me" #endif diff --git a/js/src/make-source-package.sh b/js/src/make-source-package.sh index 6e44dd977..e6d3f6df5 100755 --- a/js/src/make-source-package.sh +++ b/js/src/make-source-package.sh @@ -151,7 +151,6 @@ case $cmd in ${TOPSRCDIR}/memory/moz.build \ ${TOPSRCDIR}/memory/build \ ${TOPSRCDIR}/memory/fallible \ - ${TOPSRCDIR}/memory/jemalloc \ ${TOPSRCDIR}/memory/mozalloc \ ${TOPSRCDIR}/memory/mozjemalloc \ ${tgtpath}/memory diff --git a/js/src/moz.build b/js/src/moz.build index a18170a75..a3283b5d6 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -645,18 +645,17 @@ else: FORCE_STATIC_LIB = True STATIC_LIBRARY_NAME = 'js_static' -if CONFIG['ENABLE_INTL_API']: - if not CONFIG['MOZ_ICU_DATA_ARCHIVE']: - USE_LIBS += [ - 'icu', - ] - else: - # Linking 'icu' will pull in the stubdata library, - # which the shell doesn't want, so link the other bits. - USE_LIBS += [ - 'icui18n', - 'icuuc', - ] +if not CONFIG['MOZ_ICU_DATA_ARCHIVE']: + USE_LIBS += [ + 'icu', + ] +else: + # Linking 'icu' will pull in the stubdata library, + # which the shell doesn't want, so link the other bits. + USE_LIBS += [ + 'icui18n', + 'icuuc', + ] USE_LIBS += [ 'nspr', @@ -708,7 +707,7 @@ if CONFIG['_MSC_VER']: CXXFLAGS += ['-wd4577'] CXXFLAGS += ['-wd4312'] -if CONFIG['OS_ARCH'] not in ('WINNT', 'HP-UX'): +if CONFIG['OS_ARCH'] not in ('WINNT'): OS_LIBS += [ 'm', ] diff --git a/js/src/old-configure.in b/js/src/old-configure.in index 1736cc5d4..8abea5956 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -734,12 +734,14 @@ case "$target" in dnl VS2012+ defaults to -arch:SSE2. We want to target nothing dnl more recent, so set that explicitly here unless another dnl target arch has already been set. + changequote(,) if test -z `echo $CFLAGS | grep -i [-/]arch:` ; then CFLAGS="$CFLAGS -arch:SSE2" - fi + fi if test -z `echo $CXXFLAGS | grep -i [-/]arch:` ; then CXXFLAGS="$CXXFLAGS -arch:SSE2" fi + changequote([,]) fi dnl VS2013+ requires -FS when parallel building by make -jN. dnl If nothing, compiler sometimes causes C1041 error. @@ -1534,6 +1536,14 @@ MOZ_ARG_ENABLE_STRING(ui-locale, AC_SUBST(MOZ_UI_LOCALE) dnl ======================================================== +dnl Build the tests? +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(tests, +[ --enable-tests Build test libraries & programs], + ENABLE_TESTS=1, + ENABLE_TESTS= ) + +dnl ======================================================== dnl = dnl = Module specific options dnl = @@ -2091,6 +2101,8 @@ AC_SUBST(MOZ_DEBUG_LDFLAGS) AC_SUBST(WARNINGS_AS_ERRORS) AC_SUBST(LIBICONV) +AC_SUBST(ENABLE_TESTS) + AC_SUBST(ENABLE_STRIP) AC_SUBST(PKG_SKIP_STRIP) AC_SUBST(INCREMENTAL_LINKER) @@ -2244,8 +2256,6 @@ AC_SUBST(JS_LIBRARY_NAME) AC_SUBST(JS_CONFIG_MOZ_JS_LIBS) AC_SUBST(JS_CONFIG_LIBS) -MOZ_SUBCONFIGURE_JEMALLOC() - # Avoid using obsolete NSPR features AC_DEFINE(NO_NSPR_10_SUPPORT) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index cc68c90d5..8d144417a 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -893,7 +893,6 @@ SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc, Value* vp) return true; } -#ifdef ENABLE_INTL_API static bool AddIntlExtras(JSContext* cx, unsigned argc, Value* vp) { @@ -906,6 +905,7 @@ AddIntlExtras(JSContext* cx, unsigned argc, Value* vp) static const JSFunctionSpec funcs[] = { JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0), + JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0), JS_FS_END }; @@ -915,7 +915,6 @@ AddIntlExtras(JSContext* cx, unsigned argc, Value* vp) args.rval().setUndefined(); return true; } -#endif // ENABLE_INTL_API static bool EvalAndPrint(JSContext* cx, const char* bytes, size_t length, @@ -6139,7 +6138,6 @@ static const JSFunctionSpecWithHelp shell_functions[] = { "Sets the callback to be invoked whenever a Promise rejection is unhandled\n" "or a previously-unhandled rejection becomes handled."), -#ifdef ENABLE_INTL_API JS_FN_HELP("addIntlExtras", AddIntlExtras, 1, 0, "addIntlExtras(obj)", "Adds various not-yet-standardized Intl functions as properties on the\n" @@ -6147,7 +6145,6 @@ static const JSFunctionSpecWithHelp shell_functions[] = { "functions and their behavior are experimental: don't depend upon them\n" "unless you're willing to update your code if these experimental APIs change\n" "underneath you."), -#endif // ENABLE_INTL_API JS_FS_HELP_END }; diff --git a/js/src/shell/moz.build b/js/src/shell/moz.build index 72ea8145c..e18bad238 100644 --- a/js/src/shell/moz.build +++ b/js/src/shell/moz.build @@ -36,7 +36,7 @@ LOCAL_INCLUDES += [ OS_LIBS += CONFIG['EDITLINE_LIBS'] OS_LIBS += CONFIG['MOZ_ZLIB_LIBS'] -if CONFIG['ENABLE_INTL_API'] and CONFIG['MOZ_ICU_DATA_ARCHIVE']: +if CONFIG['MOZ_ICU_DATA_ARCHIVE']: # The ICU libraries linked into libmozjs will not include the ICU data, # so link it directly. USE_LIBS += ['icudata'] diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js index 890b1c1d5..d87abd7be 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backward_links.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2018c +// tzdata version = 2018e const tzMapper = [ x => x, diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js index 19fd871eb..b96dac96f 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2018c +// tzdata version = 2018e const tzMapper = [ x => x, diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js index 34425acec..66ef3075d 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_backzone_links.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2018c +// tzdata version = 2018e const tzMapper = [ x => x, diff --git a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js index 8b2dedec2..8d44204bc 100644 --- a/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js +++ b/js/src/tests/Intl/DateTimeFormat/timeZone_notbackward_links.js @@ -1,7 +1,7 @@ // |reftest| skip-if(!this.hasOwnProperty("Intl")) // Generated by make_intl_data.py. DO NOT EDIT. -// tzdata version = 2018c +// tzdata version = 2018e const tzMapper = [ x => x, diff --git a/js/src/tests/Intl/PluralRules/resolvedOptions-overridden-species.js b/js/src/tests/Intl/PluralRules/resolvedOptions-overridden-species.js deleted file mode 100644 index f5f5b62a8..000000000 --- a/js/src/tests/Intl/PluralRules/resolvedOptions-overridden-species.js +++ /dev/null @@ -1,27 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras")) - -// Tests the PluralRules.resolvedOptions function for overriden Array[Symbol.species]. - -addIntlExtras(Intl); - -var pl = new Intl.PluralRules("de"); - -Object.defineProperty(Array, Symbol.species, { - value: function() { - return new Proxy(["?"], { - get(t, pk, r) { - return Reflect.get(t, pk, r); - }, - defineProperty(t, pk) { - return true; - } - }); - } -}); - -var pluralCategories = pl.resolvedOptions().pluralCategories; - -assertEqArray(pluralCategories, ["one", "other"]); - -if (typeof reportCompare === "function") - reportCompare(0, 0); diff --git a/js/src/tests/Intl/getCanonicalLocales-overridden-species.js b/js/src/tests/Intl/getCanonicalLocales-overridden-species.js deleted file mode 100644 index 858735b58..000000000 --- a/js/src/tests/Intl/getCanonicalLocales-overridden-species.js +++ /dev/null @@ -1,23 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("Intl")) - -// Tests the getCanonicalLocales function for overriden Array[Symbol.species]. - -Object.defineProperty(Array, Symbol.species, { - value: function() { - return new Proxy(["?"], { - get(t, pk, r) { - return Reflect.get(t, pk, r); - }, - defineProperty(t, pk) { - return true; - } - }); - } -}); - -var arr = Intl.getCanonicalLocales("de-x-private"); - -assertEqArray(arr, ["de-x-private"]); - -if (typeof reportCompare === "function") - reportCompare(0, 0); diff --git a/js/src/tests/Intl/getDisplayNames.js b/js/src/tests/Intl/getDisplayNames.js new file mode 100644 index 000000000..ad2dbc940 --- /dev/null +++ b/js/src/tests/Intl/getDisplayNames.js @@ -0,0 +1,238 @@ +// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) +/* 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/. */ + +// Tests the getCalendarInfo function with a diverse set of arguments. + +/* + * Test if getDisplayNames return value matches expected values. + */ +function checkDisplayNames(names, expected) +{ + assertEq(Object.getPrototypeOf(names), Object.prototype); + + assertEq(names.locale, expected.locale); + assertEq(names.style, expected.style); + + const nameValues = names.values; + const expectedValues = expected.values; + + const nameValuesKeys = Object.getOwnPropertyNames(nameValues).sort(); + const expectedValuesKeys = Object.getOwnPropertyNames(expectedValues).sort(); + + assertEqArray(nameValuesKeys, expectedValuesKeys); + + for (let key of expectedValuesKeys) + assertEq(nameValues[key], expectedValues[key]); +} + +addIntlExtras(Intl); + +let gDN = Intl.getDisplayNames; + +assertEq(gDN.length, 2); + +checkDisplayNames(gDN('en-US', { +}), { + locale: 'en-US', + style: 'long', + values: {} +}); + +checkDisplayNames(gDN('en-US', { + keys: [ + 'dates/gregorian/weekdays/wednesday' + ], + style: 'narrow' +}), { + locale: 'en-US', + style: 'narrow', + values: { + 'dates/gregorian/weekdays/wednesday': 'W' + } +}); + +checkDisplayNames(gDN('en-US', { + keys: [ + 'dates/fields/year', + 'dates/fields/month', + 'dates/fields/week', + 'dates/fields/day', + 'dates/gregorian/months/january', + 'dates/gregorian/months/february', + 'dates/gregorian/months/march', + 'dates/gregorian/weekdays/tuesday' + ] +}), { + locale: 'en-US', + style: 'long', + values: { + 'dates/fields/year': 'year', + 'dates/fields/month': 'month', + 'dates/fields/week': 'week', + 'dates/fields/day': 'day', + 'dates/gregorian/months/january': 'January', + 'dates/gregorian/months/february': 'February', + 'dates/gregorian/months/march': 'March', + 'dates/gregorian/weekdays/tuesday': 'Tuesday', + } +}); + +checkDisplayNames(gDN('fr', { + keys: [ + 'dates/fields/year', + 'dates/fields/day', + 'dates/gregorian/months/october', + 'dates/gregorian/weekdays/saturday', + 'dates/gregorian/dayperiods/pm' + ] +}), { + locale: 'fr', + style: 'long', + values: { + 'dates/fields/year': 'année', + 'dates/fields/day': 'jour', + 'dates/gregorian/months/october': 'octobre', + 'dates/gregorian/weekdays/saturday': 'samedi', + 'dates/gregorian/dayperiods/pm': 'PM' + } +}); + +checkDisplayNames(gDN('it', { + style: 'short', + keys: [ + 'dates/gregorian/weekdays/thursday', + 'dates/gregorian/months/august', + 'dates/gregorian/dayperiods/am', + 'dates/fields/month', + ] +}), { + locale: 'it', + style: 'short', + values: { + 'dates/gregorian/weekdays/thursday': 'gio', + 'dates/gregorian/months/august': 'ago', + 'dates/gregorian/dayperiods/am': 'AM', + 'dates/fields/month': 'mese' + } +}); + +checkDisplayNames(gDN('ar', { + style: 'long', + keys: [ + 'dates/gregorian/weekdays/thursday', + 'dates/gregorian/months/august', + 'dates/gregorian/dayperiods/am', + 'dates/fields/month', + ] +}), { + locale: 'ar', + style: 'long', + values: { + 'dates/gregorian/weekdays/thursday': 'الخميس', + 'dates/gregorian/months/august': 'أغسطس', + 'dates/gregorian/dayperiods/am': 'ص', + 'dates/fields/month': 'الشهر' + } +}); + +/* Invalid input */ + +assertThrowsInstanceOf(() => { + gDN('en-US', { + style: '', + keys: [ + 'dates/gregorian/weekdays/thursday', + ] + }); +}, RangeError); + +assertThrowsInstanceOf(() => { + gDN('en-US', { + style: 'bogus', + keys: [ + 'dates/gregorian/weekdays/thursday', + ] + }); +}, RangeError); + +assertThrowsInstanceOf(() => { + gDN('foo-X', { + keys: [ + 'dates/gregorian/weekdays/thursday', + ] + }); +}, RangeError); + +const typeErrorKeys = [ + null, + 'string', + Symbol.iterator, + 15, + 1, + 3.7, + NaN, + Infinity +]; + +for (let keys of typeErrorKeys) { + assertThrowsInstanceOf(() => { + gDN('en-US', { + keys + }); + }, TypeError); +} + +const rangeErrorKeys = [ + [''], + ['foo'], + ['dates/foo'], + ['/dates/foo'], + ['dates/foo/foo'], + ['dates/fields'], + ['dates/fields/'], + ['dates/fields/foo'], + ['dates/fields/foo/month'], + ['/dates/foo/faa/bar/baz'], + ['dates///bar/baz'], + ['dates/gregorian'], + ['dates/gregorian/'], + ['dates/gregorian/foo'], + ['dates/gregorian/months'], + ['dates/gregorian/months/foo'], + ['dates/gregorian/weekdays'], + ['dates/gregorian/weekdays/foo'], + ['dates/gregorian/dayperiods'], + ['dates/gregorian/dayperiods/foo'], + ['dates/gregorian/months/الشهر'], + [3], + [null], + ['d', 'a', 't', 'e', 's'], + ['datesEXTRA'], + ['dates/fieldsEXTRA'], + ['dates/gregorianEXTRA'], + ['dates/gregorian/monthsEXTRA'], + ['dates/gregorian/weekdaysEXTRA'], + ['dates/fields/dayperiods/amEXTRA'], + ['dates/gregori\u1161n/months/january'], + ["dates/fields/year/"], + ["dates/fields/month/"], + ["dates/fields/week/"], + ["dates/fields/day/"], + ["dates/gregorian/months/january/"], + ["dates/gregorian/weekdays/saturday/"], + ["dates/gregorian/dayperiods/am/"], + ["dates/fields/months/january/"], +]; + +for (let keys of rangeErrorKeys) { + assertThrowsInstanceOf(() => { + gDN('en-US', { + keys + }); + }, RangeError); +} + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/js1_8_5/extensions/clone-errors.js b/js/src/tests/js1_8_5/extensions/clone-errors.js index f65578a06..d2ccea2e8 100644 --- a/js/src/tests/js1_8_5/extensions/clone-errors.js +++ b/js/src/tests/js1_8_5/extensions/clone-errors.js @@ -25,6 +25,7 @@ check({get x() { throw new Error("fail"); }}); // Mismatched scopes. for (let [write_scope, read_scope] of [['SameProcessSameThread', 'SameProcessDifferentThread'], ['SameProcessSameThread', 'DifferentProcess'], + ['SameProcessDifferentThread', 'DifferentProcessForIndexedDB'], ['SameProcessDifferentThread', 'DifferentProcess']]) { var ab = new ArrayBuffer(12); diff --git a/js/src/tests/js1_8_5/extensions/clone-transferables.js b/js/src/tests/js1_8_5/extensions/clone-transferables.js index 673684b95..9aad27208 100644 --- a/js/src/tests/js1_8_5/extensions/clone-transferables.js +++ b/js/src/tests/js1_8_5/extensions/clone-transferables.js @@ -3,11 +3,15 @@ // http://creativecommons.org/licenses/publicdomain/ function* buffer_options() { - for (var scope of ["SameProcessSameThread", "SameProcessDifferentThread", "DifferentProcess"]) { - for (var size of [0, 8, 16, 200, 1000, 4096, 8192, 65536]) { - yield { scope, size }; + for (var scope of ["SameProcessSameThread", + "SameProcessDifferentThread", + "DifferentProcess", + "DifferentProcessForIndexedDB"]) + { + for (var size of [0, 8, 16, 200, 1000, 4096, 8192, 65536]) { + yield { scope, size }; + } } - } } diff --git a/js/src/threading/windows/ConditionVariable.cpp b/js/src/threading/windows/ConditionVariable.cpp index 3c75a0f27..92e0249b7 100644 --- a/js/src/threading/windows/ConditionVariable.cpp +++ b/js/src/threading/windows/ConditionVariable.cpp @@ -54,8 +54,8 @@ js::ConditionVariable::notify_all() void js::ConditionVariable::wait(UniqueLock<Mutex>& lock) { - CRITICAL_SECTION* cs = &lock.lock.platformData()->criticalSection; - bool r = SleepConditionVariableCS(&platformData()->cv_, cs, INFINITE); + SRWLOCK* srwlock = &lock.lock.platformData()->lock; + bool r = SleepConditionVariableSRW(&platformData()->cv_, srwlock, INFINITE, 0); MOZ_RELEASE_ASSERT(r); } @@ -70,7 +70,7 @@ js::CVStatus js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock, const mozilla::TimeDuration& rel_time) { - CRITICAL_SECTION* cs = &lock.lock.platformData()->criticalSection; + SRWLOCK* srwlock = &lock.lock.platformData()->lock; // Note that DWORD is unsigned, so we have to be careful to clamp at 0. // If rel_time is Forever, then ToMilliseconds is +inf, which evaluates as @@ -82,7 +82,7 @@ js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock, ? INFINITE : static_cast<DWORD>(msecd); - BOOL r = SleepConditionVariableCS(&platformData()->cv_, cs, msec); + BOOL r = SleepConditionVariableSRW(&platformData()->cv_, srwlock, msec, 0); if (r) return CVStatus::NoTimeout; MOZ_RELEASE_ASSERT(GetLastError() == ERROR_TIMEOUT); diff --git a/js/src/threading/windows/MutexImpl.cpp b/js/src/threading/windows/MutexImpl.cpp index 385d1c8de..e838459b5 100644 --- a/js/src/threading/windows/MutexImpl.cpp +++ b/js/src/threading/windows/MutexImpl.cpp @@ -13,35 +13,6 @@ #include "threading/Mutex.h" #include "threading/windows/MutexPlatformData.h" -namespace { - -// We build with a toolkit that supports WinXP, so we have to probe -// for modern features at runtime. This is necessary because Vista and -// later automatically allocate and subsequently leak a debug info -// object for each critical section that we allocate unless we tell it -// not to. In order to tell it not to, we need the extra flags field -// provided by the Ex version of InitializeCriticalSection. -struct MutexNativeImports -{ - using InitializeCriticalSectionExT = BOOL (WINAPI*)(CRITICAL_SECTION*, DWORD, DWORD); - InitializeCriticalSectionExT InitializeCriticalSectionEx; - - MutexNativeImports() { - HMODULE kernel32_dll = GetModuleHandle("kernel32.dll"); - MOZ_RELEASE_ASSERT(kernel32_dll != NULL); - InitializeCriticalSectionEx = reinterpret_cast<InitializeCriticalSectionExT>( - GetProcAddress(kernel32_dll, "InitializeCriticalSectionEx")); - } - - bool hasInitializeCriticalSectionEx() const { - return InitializeCriticalSectionEx; - } -}; - -static MutexNativeImports NativeImports; - -} // (anonymous namespace) - js::detail::MutexImpl::MutexImpl() { AutoEnterOOMUnsafeRegion oom; @@ -49,18 +20,7 @@ js::detail::MutexImpl::MutexImpl() if (!platformData_) oom.crash("js::Mutex::Mutex"); - // This number was adopted from NSPR. - const static DWORD LockSpinCount = 1500; - BOOL r; - if (NativeImports.hasInitializeCriticalSectionEx()) { - r = NativeImports.InitializeCriticalSectionEx(&platformData()->criticalSection, - LockSpinCount, - CRITICAL_SECTION_NO_DEBUG_INFO); - } else { - r = InitializeCriticalSectionAndSpinCount(&platformData()->criticalSection, - LockSpinCount); - } - MOZ_RELEASE_ASSERT(r); + InitializeSRWLock(&platformData()->lock); } js::detail::MutexImpl::~MutexImpl() @@ -68,18 +28,17 @@ js::detail::MutexImpl::~MutexImpl() if (!platformData_) return; - DeleteCriticalSection(&platformData()->criticalSection); js_delete(platformData()); } void js::detail::MutexImpl::lock() { - EnterCriticalSection(&platformData()->criticalSection); + AcquireSRWLockExclusive(&platformData()->lock); } void js::detail::MutexImpl::unlock() { - LeaveCriticalSection(&platformData()->criticalSection); + ReleaseSRWLockExclusive(&platformData()->lock); } diff --git a/js/src/threading/windows/MutexPlatformData.h b/js/src/threading/windows/MutexPlatformData.h index fbe7fc80d..1d741c5d0 100644 --- a/js/src/threading/windows/MutexPlatformData.h +++ b/js/src/threading/windows/MutexPlatformData.h @@ -13,7 +13,7 @@ struct js::detail::MutexImpl::PlatformData { - CRITICAL_SECTION criticalSection; + SRWLOCK lock; }; #endif // platform_win_MutexPlatformData_h diff --git a/js/src/vm/DateTime.cpp b/js/src/vm/DateTime.cpp index e35ad4285..ba3145af2 100644 --- a/js/src/vm/DateTime.cpp +++ b/js/src/vm/DateTime.cpp @@ -13,9 +13,7 @@ #include "jsutil.h" #include "js/Date.h" -#if ENABLE_INTL_API #include "unicode/timezone.h" -#endif using mozilla::Atomic; using mozilla::ReleaseAcquire; @@ -333,7 +331,7 @@ JS::ResetTimeZone() { js::DateTimeInfo::updateTimeZoneAdjustment(); -#if ENABLE_INTL_API && defined(ICU_TZ_HAS_RECREATE_DEFAULT) +#if defined(ICU_TZ_HAS_RECREATE_DEFAULT) TZInfo.acquire(); TZInfo.status = IcuTimeZoneInfo::NeedsUpdate; TZInfo.release(); @@ -343,7 +341,7 @@ JS::ResetTimeZone() void js::ResyncICUDefaultTimeZone() { -#if ENABLE_INTL_API && defined(ICU_TZ_HAS_RECREATE_DEFAULT) +#if defined(ICU_TZ_HAS_RECREATE_DEFAULT) TZInfo.acquire(); if (TZInfo.status == IcuTimeZoneInfo::NeedsUpdate) { icu::TimeZone::recreateDefault(); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 280548cd6..c90b6b85f 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -17,9 +17,7 @@ #include "builtin/AtomicsObject.h" #include "builtin/Eval.h" -#if EXPOSE_INTL_API -# include "builtin/Intl.h" -#endif +#include "builtin/Intl.h" #include "builtin/MapObject.h" #include "builtin/ModuleObject.h" #include "builtin/Object.h" diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index 7381a97b5..bd29d0c79 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -1144,7 +1144,7 @@ js::GCParallelTask::runFromMainThread(JSRuntime* rt) MOZ_ASSERT(state == NotStarted); MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt)); uint64_t timeStart = PRMJ_Now(); - run(); + runTask(); duration_ = PRMJ_Now() - timeStart; } @@ -1155,7 +1155,7 @@ js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked) AutoUnlockHelperThreadState parallelSection(locked); gc::AutoSetThreadIsPerformingGC performingGC; uint64_t timeStart = PRMJ_Now(); - run(); + runTask(); duration_ = PRMJ_Now() - timeStart; } diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp index 05cc087cc..39acdf9a8 100644 --- a/js/src/vm/Initialization.cpp +++ b/js/src/vm/Initialization.cpp @@ -20,10 +20,8 @@ #include "jit/ExecutableAllocator.h" #include "jit/Ion.h" #include "js/Utility.h" -#if ENABLE_INTL_API #include "unicode/uclean.h" #include "unicode/utypes.h" -#endif // ENABLE_INTL_API #include "vm/DateTime.h" #include "vm/HelperThreads.h" #include "vm/Runtime.h" @@ -113,12 +111,10 @@ JS::detail::InitWithFailureDiagnostic(bool isDebugBuild) js::DateTimeInfo::init(); -#if EXPOSE_INTL_API UErrorCode err = U_ZERO_ERROR; u_init(&err); if (U_FAILURE(err)) return "u_init() failed"; -#endif // EXPOSE_INTL_API RETURN_IF_FAIL(js::CreateHelperThreadsState()); RETURN_IF_FAIL(FutexRuntime::initialize()); @@ -171,9 +167,7 @@ JS_ShutDown(void) // to do it only when PRMJ_Now is eventually called. PRMJ_NowShutdown(); -#if EXPOSE_INTL_API u_cleanup(); -#endif // EXPOSE_INTL_API if (!JSRuntime::hasLiveRuntimes()) js::jit::ReleaseProcessExecutableMemory(); @@ -188,11 +182,7 @@ JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_IC "must call JS_SetICUMemoryFunctions before any other JSAPI " "operation (including JS_Init)"); -#if EXPOSE_INTL_API UErrorCode status = U_ZERO_ERROR; u_setMemoryFunctions(/* context = */ nullptr, allocFn, reallocFn, freeFn, &status); return U_SUCCESS(status); -#else - return true; -#endif } diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 0d6a3922c..174e23594 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -213,11 +213,6 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) warningReporter(nullptr), buildIdOp(nullptr), propertyRemovals(0), -#if !EXPOSE_INTL_API - thousandsSeparator(0), - decimalSeparator(0), - numGrouping(0), -#endif keepAtoms_(0), trustedPrincipals_(nullptr), beingDestroyed_(false), @@ -330,8 +325,7 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) /* The garbage collector depends on everything before this point being initialized. */ gcInitialized = true; - if (!InitRuntimeNumberState(this)) - return false; + InitRuntimeNumberState(this); JS::ResetTimeZone(); @@ -425,10 +419,6 @@ JSRuntime::destroyRuntime() */ FreeScriptData(this, lock); -#if !EXPOSE_INTL_API - FinishRuntimeNumberState(this); -#endif - gc.finish(); atomsCompartment_ = nullptr; diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 734543c4e..735adadf2 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -978,13 +978,6 @@ struct JSRuntime : public JS::shadow::Runtime, */ uint32_t propertyRemovals; -#if !EXPOSE_INTL_API - /* Number localization, used by jsnum.cpp. */ - const char* thousandsSeparator; - const char* decimalSeparator; - const char* numGrouping; -#endif - private: mozilla::Maybe<js::SharedImmutableStringsCache> sharedImmutableStrings_; diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index c7e7cc863..3e7baccad 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -189,6 +189,22 @@ intrinsic_IsInstanceOfBuiltin(JSContext* cx, unsigned argc, Value* vp) return true; } +template<typename T> +static bool +intrinsic_GuardToBuiltin(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + if (args[0].toObject().is<T>()) { + args.rval().setObject(args[0].toObject()); + return true; + } + args.rval().setNull(); + return true; +} + /** * Self-hosting intrinsic returning the original constructor for a builtin * the name of which is the first and only argument. @@ -2174,11 +2190,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("std_String_trimRight", str_trimRight, 0,0), JS_FN("std_String_toLocaleLowerCase", str_toLocaleLowerCase, 0,0), JS_FN("std_String_toLocaleUpperCase", str_toLocaleUpperCase, 0,0), -#if !EXPOSE_INTL_API - JS_FN("std_String_localeCompare", str_localeCompare, 1,0), -#else JS_FN("std_String_normalize", str_normalize, 0,0), -#endif JS_FN("std_String_concat", str_concat, 1,0), JS_FN("std_TypedArray_buffer", js::TypedArray_bufferGetter, 1,0), @@ -2265,18 +2277,18 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("_SetCanonicalName", intrinsic_SetCanonicalName, 2,0), - JS_INLINABLE_FN("IsArrayIterator", - intrinsic_IsInstanceOfBuiltin<ArrayIteratorObject>, 1,0, - IntrinsicIsArrayIterator), - JS_INLINABLE_FN("IsMapIterator", - intrinsic_IsInstanceOfBuiltin<MapIteratorObject>, 1,0, - IntrinsicIsMapIterator), - JS_INLINABLE_FN("IsSetIterator", - intrinsic_IsInstanceOfBuiltin<SetIteratorObject>, 1,0, - IntrinsicIsSetIterator), - JS_INLINABLE_FN("IsStringIterator", - intrinsic_IsInstanceOfBuiltin<StringIteratorObject>, 1,0, - IntrinsicIsStringIterator), + JS_INLINABLE_FN("GuardToArrayIterator", + intrinsic_GuardToBuiltin<ArrayIteratorObject>, 1,0, + IntrinsicGuardToArrayIterator), + JS_INLINABLE_FN("GuardToMapIterator", + intrinsic_GuardToBuiltin<MapIteratorObject>, 1,0, + IntrinsicGuardToMapIterator), + JS_INLINABLE_FN("GuardToSetIterator", + intrinsic_GuardToBuiltin<SetIteratorObject>, 1,0, + IntrinsicGuardToSetIterator), + JS_INLINABLE_FN("GuardToStringIterator", + intrinsic_GuardToBuiltin<StringIteratorObject>, 1,0, + IntrinsicGuardToStringIterator), JS_FN("_CreateMapIterationResultPair", intrinsic_CreateMapIterationResultPair, 0, 0), JS_INLINABLE_FN("_GetNextMapEntryForIterator", intrinsic_GetNextMapEntryForIterator, 2,0, @@ -2377,7 +2389,12 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("CallStarGeneratorMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 2, 0), + JS_INLINABLE_FN("GuardToMapObject", intrinsic_GuardToBuiltin<MapObject>, 1, 0, + IntrinsicGuardToMapObject), JS_FN("IsWeakSet", intrinsic_IsInstanceOfBuiltin<WeakSetObject>, 1,0), + + JS_INLINABLE_FN("GuardToSetObject", intrinsic_GuardToBuiltin<SetObject>, 1, 0, + IntrinsicGuardToSetObject), JS_FN("CallWeakSetMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0), @@ -2438,6 +2455,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0), JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0), JS_FN("intl_GetCalendarInfo", intl_GetCalendarInfo, 1,0), + JS_FN("intl_ComputeDisplayNames", intl_ComputeDisplayNames, 3,0), JS_FN("intl_IsValidTimeZoneName", intl_IsValidTimeZoneName, 1,0), JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0), JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0), diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index 3a062c3b8..42e909000 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -160,16 +160,16 @@ template<typename T, typename AllocPolicy> struct BufferIterator { typedef mozilla::BufferList<AllocPolicy> BufferList; - explicit BufferIterator(BufferList& buffer) + explicit BufferIterator(const BufferList& buffer) : mBuffer(buffer) , mIter(buffer.Iter()) { JS_STATIC_ASSERT(8 % sizeof(T) == 0); } - BufferIterator(const BufferIterator& other) - : mBuffer(other.mBuffer) - , mIter(other.mIter) + explicit BufferIterator(const JSStructuredCloneData& data) + : mBuffer(data.bufList_) + , mIter(data.Start()) { } @@ -228,17 +228,26 @@ struct BufferIterator { return mIter.HasRoomFor(sizeof(T)); } - BufferList& mBuffer; + const BufferList& mBuffer; typename BufferList::IterImpl mIter; }; +// SCOutput provides an interface to write raw data -- eg uint64_ts, doubles, +// arrays of bytes -- into a structured clone data output stream. It also knows +// how to free any transferable data within that stream. +// +// Note that it contains a full JSStructuredCloneData object, which holds the +// callbacks necessary to read/write/transfer/free the data. For the purpose of +// this class, only the freeTransfer callback is relevant; the rest of the callbacks +// are used by the higher-level JSStructuredCloneWriter interface. struct SCOutput { public: - using Iter = BufferIterator<uint64_t, TempAllocPolicy>; + using Iter = BufferIterator<uint64_t, SystemAllocPolicy>; - explicit SCOutput(JSContext* cx); + SCOutput(JSContext* cx, JS::StructuredCloneScope scope); JSContext* context() const { return cx; } + JS::StructuredCloneScope scope() const { return buf.scope(); } bool write(uint64_t u); bool writePair(uint32_t tag, uint32_t data); @@ -251,22 +260,25 @@ struct SCOutput { template <class T> bool writeArray(const T* p, size_t nbytes); - bool extractBuffer(JSStructuredCloneData* data); - void discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure); + void setCallbacks(const JSStructuredCloneCallbacks* callbacks, + void* closure, + OwnTransferablePolicy policy) + { + buf.setCallbacks(callbacks, closure, policy); + } + void extractBuffer(JSStructuredCloneData* data) { *data = Move(buf); } + void discardTransferables(); uint64_t tell() const { return buf.Size(); } uint64_t count() const { return buf.Size() / sizeof(uint64_t); } - Iter iter() { - return BufferIterator<uint64_t, TempAllocPolicy>(buf); - } + Iter iter() { return Iter(buf); } size_t offset(Iter dest) { return dest - iter(); } - private: JSContext* cx; - mozilla::BufferList<TempAllocPolicy> buf; + JSStructuredCloneData buf; }; class SCInput { @@ -356,13 +368,6 @@ struct JSStructuredCloneReader { // be valid cross-process.) JS::StructuredCloneScope allowedScope; - // The scope the buffer was generated for (what sort of buffer it is.) The - // scope is not just a permissions thing; it also affects the storage - // format (eg a Transferred ArrayBuffer can be stored as a pointer for - // SameProcessSameThread but must have its contents in the clone buffer for - // DifferentProcess.) - JS::StructuredCloneScope storedScope; - // Stack of objects with properties remaining to be read. AutoValueVector objs; @@ -386,13 +391,15 @@ struct JSStructuredCloneWriter { const JSStructuredCloneCallbacks* cb, void* cbClosure, const Value& tVal) - : out(cx), scope(scope), objs(out.context()), + : out(cx, scope), objs(out.context()), counts(out.context()), entries(out.context()), - memory(out.context()), callbacks(cb), - closure(cbClosure), transferable(out.context(), tVal), + memory(out.context()), + transferable(out.context(), tVal), transferableObjects(out.context(), GCHashSet<JSObject*>(cx)), cloneDataPolicy(cloneDataPolicy) - {} + { + out.setCallbacks(cb, cbClosure, OwnTransferablePolicy::NoTransferables); + } ~JSStructuredCloneWriter(); @@ -408,17 +415,10 @@ struct JSStructuredCloneWriter { SCOutput& output() { return out; } - bool extractBuffer(JSStructuredCloneData* data) { - bool success = out.extractBuffer(data); - if (success) { - data->setOptionalCallbacks(callbacks, closure, - OwnTransferablePolicy::OwnsTransferablesIfAny); - } - return success; + void extractBuffer(JSStructuredCloneData* newData) { + out.extractBuffer(newData); } - JS::StructuredCloneScope cloneScope() const { return scope; } - private: JSStructuredCloneWriter() = delete; JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete; @@ -449,9 +449,6 @@ struct JSStructuredCloneWriter { SCOutput out; - // The (address space, thread) scope within which this clone is valid. - JS::StructuredCloneScope scope; - // Vector of objects with properties remaining to be written. // // NB: These can span multiple compartments, so the compartment must be @@ -477,12 +474,6 @@ struct JSStructuredCloneWriter { SystemAllocPolicy>; Rooted<CloneMemory> memory; - // The user defined callbacks that will be used for cloning. - const JSStructuredCloneCallbacks* callbacks; - - // Any value passed to JS_WriteStructuredClone. - void* closure; - // Set of transferable objects RootedValue transferable; Rooted<GCHashSet<JSObject*>> transferableObjects; @@ -542,7 +533,12 @@ WriteStructuredClone(JSContext* cx, HandleValue v, JSStructuredCloneData* bufp, const Value& transferable) { JSStructuredCloneWriter w(cx, scope, cloneDataPolicy, cb, cbClosure, transferable); - return w.init() && w.write(v) && w.extractBuffer(bufp); + if (!w.init()) + return false; + if (!w.write(v)) + return false; + w.extractBuffer(bufp); + return true; } bool @@ -555,91 +551,15 @@ ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, return r.read(vp); } -// If the given buffer contains Transferables, free them. Note that custom -// Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to -// delete their transferables. -template<typename AllocPolicy> -static void -DiscardTransferables(mozilla::BufferList<AllocPolicy>& buffer, - const JSStructuredCloneCallbacks* cb, void* cbClosure) -{ - auto point = BufferIterator<uint64_t, AllocPolicy>(buffer); - if (point.done()) - return; // Empty buffer - - uint32_t tag, data; - MOZ_RELEASE_ASSERT(point.canPeek()); - SCInput::getPair(point.peek(), &tag, &data); - point.next(); - - if (tag == SCTAG_HEADER) { - if (point.done()) - return; - - MOZ_RELEASE_ASSERT(point.canPeek()); - SCInput::getPair(point.peek(), &tag, &data); - point.next(); - } - - if (tag != SCTAG_TRANSFER_MAP_HEADER) - return; - - if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED) - return; - - // freeTransfer should not GC - JS::AutoSuppressGCAnalysis nogc; - - if (point.done()) - return; - - uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek()); - point.next(); - while (numTransferables--) { - if (!point.canPeek()) - return; - - uint32_t ownership; - SCInput::getPair(point.peek(), &tag, &ownership); - point.next(); - MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY); - if (!point.canPeek()) - return; - - void* content; - SCInput::getPtr(point.peek(), &content); - point.next(); - if (!point.canPeek()) - return; - - uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek()); - point.next(); - - if (ownership < JS::SCTAG_TMO_FIRST_OWNED) - continue; - - if (ownership == JS::SCTAG_TMO_ALLOC_DATA) { - js_free(content); - } else if (ownership == JS::SCTAG_TMO_MAPPED_DATA) { - JS_ReleaseMappedArrayBufferContents(content, extraData); - } else if (cb && cb->freeTransfer) { - cb->freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, cbClosure); - } else { - MOZ_ASSERT(false, "unknown ownership"); - } - } -} - static bool StructuredCloneHasTransferObjects(const JSStructuredCloneData& data) { - auto iter = data.Iter(); - if (data.Size() < sizeof(uint64_t)) return false; uint64_t u; - data.ReadBytes(iter, reinterpret_cast<char*>(&u), sizeof(u)); + BufferIterator<uint64_t, SystemAllocPolicy> iter(data); + MOZ_ALWAYS_TRUE(iter.readBytes(reinterpret_cast<char*>(&u), sizeof(u))); uint32_t tag = uint32_t(u >> 32); return (tag == SCTAG_TRANSFER_MAP_HEADER); } @@ -650,7 +570,7 @@ SCInput::SCInput(JSContext* cx, JSStructuredCloneData& data) : cx(cx), point(data) { - static_assert(JSStructuredCloneData::kSegmentAlignment % 8 == 0, + static_assert(JSStructuredCloneData::BufferList::kSegmentAlignment % 8 == 0, "structured clone buffer reads should be aligned"); MOZ_ASSERT(data.Size() % 8 == 0); } @@ -812,9 +732,8 @@ SCInput::readPtr(void** p) return true; } -SCOutput::SCOutput(JSContext* cx) - : cx(cx) - , buf(0, 0, 4096, cx) +SCOutput::SCOutput(JSContext* cx, JS::StructuredCloneScope scope) + : cx(cx), buf(scope) { } @@ -822,7 +741,11 @@ bool SCOutput::write(uint64_t u) { uint64_t v = NativeEndian::swapToLittleEndian(u); - return buf.WriteBytes(reinterpret_cast<char*>(&v), sizeof(u)); + if (!buf.AppendBytes(reinterpret_cast<char*>(&v), sizeof(u))) { + ReportOutOfMemory(context()); + return false; + } + return true; } bool @@ -883,7 +806,7 @@ SCOutput::writeArray(const T* p, size_t nelems) for (size_t i = 0; i < nelems; i++) { T value = swapToLittleEndian(p[i]); - if (!buf.WriteBytes(reinterpret_cast<char*>(&value), sizeof(value))) + if (!buf.AppendBytes(reinterpret_cast<char*>(&value), sizeof(value))) return false; } @@ -892,7 +815,7 @@ SCOutput::writeArray(const T* p, size_t nelems) size_t padbytes = sizeof(uint64_t) * nwords - sizeof(T) * nelems; char zero = 0; for (size_t i = 0; i < padbytes; i++) { - if (!buf.WriteBytes(&zero, sizeof(zero))) + if (!buf.AppendBytes(&zero, sizeof(zero))) return false; } @@ -927,34 +850,101 @@ SCOutput::writePtr(const void* p) return write(reinterpret_cast<uint64_t>(p)); } -bool -SCOutput::extractBuffer(JSStructuredCloneData* data) -{ - bool success; - mozilla::BufferList<SystemAllocPolicy> out = - buf.MoveFallible<SystemAllocPolicy>(&success); - if (!success) { - ReportOutOfMemory(cx); - return false; - } - *data = JSStructuredCloneData(Move(out)); - return true; -} - void -SCOutput::discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure) +SCOutput::discardTransferables() { - DiscardTransferables(buf, cb, cbClosure); + buf.discardTransferables(); } } /* namespace js */ -JSStructuredCloneData::~JSStructuredCloneData() + +// If the buffer contains Transferables, free them. Note that custom +// Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to +// delete their transferables. +void +JSStructuredCloneData::discardTransferables() { if (!Size()) return; - if (ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny) - DiscardTransferables(*this, callbacks_, closure_); + + if (ownTransferables_ != OwnTransferablePolicy::OwnsTransferablesIfAny) + return; + + // DifferentProcess clones cannot contain pointers, so nothing needs to be + // released. + if (scope_ == JS::StructuredCloneScope::DifferentProcess) + return; + + FreeTransferStructuredCloneOp freeTransfer = nullptr; + if (callbacks_) + freeTransfer = callbacks_->freeTransfer; + + auto point = BufferIterator<uint64_t, SystemAllocPolicy>(*this); + if (point.done()) + return; // Empty buffer + + uint32_t tag, data; + MOZ_RELEASE_ASSERT(point.canPeek()); + SCInput::getPair(point.peek(), &tag, &data); + point.next(); + + if (tag == SCTAG_HEADER) { + if (point.done()) + return; + + MOZ_RELEASE_ASSERT(point.canPeek()); + SCInput::getPair(point.peek(), &tag, &data); + point.next(); + } + + if (tag != SCTAG_TRANSFER_MAP_HEADER) + return; + + if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED) + return; + + // freeTransfer should not GC + JS::AutoSuppressGCAnalysis nogc; + + if (point.done()) + return; + + uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek()); + point.next(); + while (numTransferables--) { + if (!point.canPeek()) + return; + + uint32_t ownership; + SCInput::getPair(point.peek(), &tag, &ownership); + point.next(); + MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY); + if (!point.canPeek()) + return; + + void* content; + SCInput::getPtr(point.peek(), &content); + point.next(); + if (!point.canPeek()) + return; + + uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek()); + point.next(); + + if (ownership < JS::SCTAG_TMO_FIRST_OWNED) + continue; + + if (ownership == JS::SCTAG_TMO_ALLOC_DATA) { + js_free(content); + } else if (ownership == JS::SCTAG_TMO_MAPPED_DATA) { + JS_ReleaseMappedArrayBufferContents(content, extraData); + } else if (freeTransfer) { + freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, closure_); + } else { + MOZ_ASSERT(false, "unknown ownership"); + } + } } JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX); @@ -962,9 +952,8 @@ JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX); JSStructuredCloneWriter::~JSStructuredCloneWriter() { // Free any transferable data left lying around in the buffer - if (out.count()) { - out.discardTransferables(callbacks, closure); - } + if (out.count()) + out.discardTransferables(); } bool @@ -1038,7 +1027,7 @@ JSStructuredCloneWriter::parseTransferable() bool JSStructuredCloneWriter::reportDataCloneError(uint32_t errorId) { - ReportDataCloneError(context(), callbacks, errorId); + ReportDataCloneError(context(), out.buf.callbacks_, errorId); return false; } @@ -1454,8 +1443,8 @@ JSStructuredCloneWriter::startWrite(HandleValue v) return traverseSavedFrame(obj); } - if (callbacks && callbacks->write) - return callbacks->write(context(), this, obj, closure); + if (out.buf.callbacks_ && out.buf.callbacks_->write) + return out.buf.callbacks_->write(context(), this, obj, out.buf.closure_); /* else fall through */ } @@ -1465,7 +1454,7 @@ JSStructuredCloneWriter::startWrite(HandleValue v) bool JSStructuredCloneWriter::writeHeader() { - return out.writePair(SCTAG_HEADER, (uint32_t)scope); + return out.writePair(SCTAG_HEADER, (uint32_t)output().scope()); } bool @@ -1523,6 +1512,7 @@ JSStructuredCloneWriter::transferOwnership() JSContext* cx = context(); RootedObject obj(cx); + JS::StructuredCloneScope scope = output().scope(); for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) { obj = tr.front(); @@ -1555,7 +1545,9 @@ JSStructuredCloneWriter::transferOwnership() return false; } - if (scope == JS::StructuredCloneScope::DifferentProcess) { + if (scope == JS::StructuredCloneScope::DifferentProcess || + scope == JS::StructuredCloneScope::DifferentProcessForIndexedDB) + { // Write Transferred ArrayBuffers in DifferentProcess scope at // the end of the clone buffer, and store the offset within the // buffer to where the ArrayBuffer was written. Note that this @@ -1592,9 +1584,9 @@ JSStructuredCloneWriter::transferOwnership() extraData = nbytes; } } else { - if (!callbacks || !callbacks->writeTransfer) + if (!out.buf.callbacks_ || !out.buf.callbacks_->writeTransfer) return reportDataCloneError(JS_SCERR_TRANSFERABLE); - if (!callbacks->writeTransfer(cx, obj, closure, &tag, &ownership, &content, &extraData)) + if (!out.buf.callbacks_->writeTransfer(cx, obj, out.buf.closure_, &tag, &ownership, &content, &extraData)) return false; MOZ_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY); } @@ -2187,25 +2179,33 @@ JSStructuredCloneReader::readHeader() if (!in.getPair(&tag, &data)) return in.reportTruncated(); - if (tag != SCTAG_HEADER) { + JS::StructuredCloneScope storedScope; + if (tag == SCTAG_HEADER) { + MOZ_ALWAYS_TRUE(in.readPair(&tag, &data)); + storedScope = JS::StructuredCloneScope(data); + } else { // Old structured clone buffer. We must have read it from disk. - storedScope = JS::StructuredCloneScope::DifferentProcess; - return true; + storedScope = JS::StructuredCloneScope::DifferentProcessForIndexedDB; } - MOZ_ALWAYS_TRUE(in.readPair(&tag, &data)); - storedScope = JS::StructuredCloneScope(data); - - if (data != uint32_t(JS::StructuredCloneScope::SameProcessSameThread) && - data != uint32_t(JS::StructuredCloneScope::SameProcessDifferentThread) && - data != uint32_t(JS::StructuredCloneScope::DifferentProcess)) + if (storedScope < JS::StructuredCloneScope::SameProcessSameThread || + storedScope > JS::StructuredCloneScope::DifferentProcessForIndexedDB) { JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA, "invalid structured clone scope"); return false; } + + if (allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB) { + // Bug 1434308 and bug 1458320 - the scopes stored in old IndexedDB + // clones are incorrect. Treat them as if they were DifferentProcess. + allowedScope = JS::StructuredCloneScope::DifferentProcess; + return true; + } + if (storedScope < allowedScope) { - JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA, + JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, + JSMSG_SC_BAD_SERIALIZED_DATA, "incompatible structured clone scope"); return false; } @@ -2249,10 +2249,14 @@ JSStructuredCloneReader::readTransferMap() return false; if (tag == SCTAG_TRANSFER_MAP_ARRAY_BUFFER) { - if (storedScope == JS::StructuredCloneScope::DifferentProcess) { + if (allowedScope == JS::StructuredCloneScope::DifferentProcess || + allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB) + { // Transferred ArrayBuffers in a DifferentProcess clone buffer - // are treated as if they weren't Transferred at all. - continue; + // are treated as if they weren't Transferred at all. We should + // only see SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER. + ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE); + return false; } size_t nbytes = extraData; @@ -2586,7 +2590,7 @@ JS_StructuredClone(JSContext* cx, HandleValue value, MutableHandleValue vp, } JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other) - : scope_(other.scope_) + : scope_(other.scope()), data_(other.scope()) { data_.ownTransferables_ = other.data_.ownTransferables_; other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_); @@ -2604,45 +2608,14 @@ JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other) } void -JSAutoStructuredCloneBuffer::clear(const JSStructuredCloneCallbacks* optionalCallbacks, - void* optionalClosure) +JSAutoStructuredCloneBuffer::clear() { - if (!data_.Size()) - return; - - const JSStructuredCloneCallbacks* callbacks = - optionalCallbacks ? optionalCallbacks : data_.callbacks_; - void* closure = optionalClosure ? optionalClosure : data_.closure_; - - if (data_.ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny) - DiscardTransferables(data_, callbacks, closure); + data_.discardTransferables(); data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables; data_.Clear(); version_ = 0; } -bool -JSAutoStructuredCloneBuffer::copy(const JSStructuredCloneData& srcData, uint32_t version, - const JSStructuredCloneCallbacks* callbacks, - void* closure) -{ - // transferable objects cannot be copied - if (StructuredCloneHasTransferObjects(srcData)) - return false; - - clear(); - - auto iter = srcData.Iter(); - while (!iter.Done()) { - data_.WriteBytes(iter.Data(), iter.RemainingInSegment()); - iter.Advance(srcData, iter.RemainingInSegment()); - } - - version_ = version; - data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables); - return true; -} - void JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t version, const JSStructuredCloneCallbacks* callbacks, @@ -2651,7 +2624,7 @@ JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t versio clear(); data_ = Move(data); version_ = version; - data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny); + data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny); } void @@ -2668,7 +2641,7 @@ JSAutoStructuredCloneBuffer::steal(JSStructuredCloneData* data, uint32_t* versio *data = Move(data_); version_ = 0; - data_.setOptionalCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables); + data_.setCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables); } bool @@ -2782,5 +2755,5 @@ JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj) JS_PUBLIC_API(JS::StructuredCloneScope) JS_GetStructuredCloneScope(JSStructuredCloneWriter* w) { - return w->cloneScope(); + return w->output().scope(); } diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index b4f41c3d5..7fade24fb 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -857,7 +857,7 @@ class NumLit private: Which which_; union { - Value scalar_; + JS::UninitializedValue scalar_; SimdConstant simd_; } u; @@ -880,7 +880,7 @@ class NumLit int32_t toInt32() const { MOZ_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned); - return u.scalar_.toInt32(); + return u.scalar_.asValueRef().toInt32(); } uint32_t toUint32() const { @@ -889,17 +889,17 @@ class NumLit RawF64 toDouble() const { MOZ_ASSERT(which_ == Double); - return RawF64(u.scalar_.toDouble()); + return RawF64(u.scalar_.asValueRef().toDouble()); } RawF32 toFloat() const { MOZ_ASSERT(which_ == Float); - return RawF32(float(u.scalar_.toDouble())); + return RawF32(float(u.scalar_.asValueRef().toDouble())); } Value scalarValue() const { MOZ_ASSERT(which_ != OutOfRangeInt); - return u.scalar_; + return u.scalar_.asValueRef(); } bool isSimd() const diff --git a/js/xpconnect/shell/moz.build b/js/xpconnect/shell/moz.build index ecc796f7f..d4f5d55af 100644 --- a/js/xpconnect/shell/moz.build +++ b/js/xpconnect/shell/moz.build @@ -35,22 +35,6 @@ if CONFIG['_MSC_VER']: if CONFIG['OS_ARCH'] == 'WINNT': RCINCLUDE = 'xpcshell.rc' - if CONFIG['MOZ_SANDBOX']: - # For sandbox includes and the include dependencies those have - LOCAL_INCLUDES += [ - '/security/sandbox/chromium', - '/security/sandbox/chromium-shim', - ] - - USE_LIBS += [ - 'sandbox_s', - ] - - DELAYLOAD_DLLS += [ - 'winmm.dll', - 'user32.dll', - ] - DELAYLOAD_DLLS += [ 'xul.dll', ] diff --git a/js/xpconnect/shell/xpcshell.cpp b/js/xpconnect/shell/xpcshell.cpp index ba979bc69..4eef3f6bf 100644 --- a/js/xpconnect/shell/xpcshell.cpp +++ b/js/xpconnect/shell/xpcshell.cpp @@ -22,9 +22,6 @@ #define XRE_DONT_PROTECT_DLL_LOAD #define XRE_WANT_ENVIRON #include "nsWindowsWMain.cpp" -#ifdef MOZ_SANDBOX -#include "mozilla/sandboxing/SandboxInitialization.h" -#endif #endif #ifdef MOZ_WIDGET_GTK @@ -53,13 +50,7 @@ main(int argc, char** argv, char** envp) DllBlocklist_Initialize(); #endif - XREShellData shellData; -#if defined(XP_WIN) && defined(MOZ_SANDBOX) - shellData.sandboxBrokerServices = - mozilla::sandboxing::GetInitializedBrokerServices(); -#endif - - int result = XRE_XPCShellMain(argc, argv, envp, &shellData); + int result = XRE_XPCShellMain(argc, argv, envp); #ifdef XP_MACOSX FinishAutoreleasePool(); diff --git a/js/xpconnect/src/ExportHelpers.cpp b/js/xpconnect/src/ExportHelpers.cpp index 3dbf83e3b..e574e708c 100644 --- a/js/xpconnect/src/ExportHelpers.cpp +++ b/js/xpconnect/src/ExportHelpers.cpp @@ -329,11 +329,20 @@ NewFunctionForwarder(JSContext* cx, HandleId idArg, HandleObject callable, if (id == JSID_VOIDHANDLE) id = GetJSIDByIndex(cx, XPCJSContext::IDX_EMPTYSTRING); + // If our callable is a (possibly wrapped) function, we can give + // the exported thing the right number of args. + unsigned nargs = 0; + RootedObject unwrapped(cx, js::UncheckedUnwrap(callable)); + if (unwrapped) { + if (JSFunction* fun = JS_GetObjectFunction(unwrapped)) + nargs = JS_GetFunctionArity(fun); + } + // We have no way of knowing whether the underlying function wants to be a // constructor or not, so we just mark all forwarders as constructors, and // let the underlying function throw for construct calls if it wants. JSFunction* fun = js::NewFunctionByIdWithReserved(cx, FunctionForwarder, - 0, JSFUN_CONSTRUCTOR, id); + nargs, JSFUN_CONSTRUCTOR, id); if (!fun) return false; diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index f5f6a11bb..f352607d4 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -59,10 +59,6 @@ #include "nsIXULRuntime.h" #include "nsJSPrincipals.h" -#if defined(MOZ_JEMALLOC4) -#include "mozmemory.h" -#endif - #ifdef XP_WIN #include <windows.h> #endif @@ -147,18 +143,6 @@ public: mActive = false; } } else { -#if defined(MOZ_JEMALLOC4) - if (mPurge) { - /* Jemalloc purges dirty pages regularly during free() when the - * ratio of dirty pages compared to active pages is higher than - * 1 << lg_dirty_mult. A high ratio can have an impact on - * performance, so we use the default ratio of 8, but force a - * regular purge of all remaining dirty pages, after cycle - * collection. */ - Telemetry::AutoTimer<Telemetry::MEMORY_FREE_PURGED_PAGES_MS> timer; - jemalloc_free_dirty_pages(); - } -#endif mActive = false; } return NS_OK; @@ -1544,12 +1528,6 @@ XPCJSContext::~XPCJSContext() delete mDyingWrappedNativeProtoMap; mDyingWrappedNativeProtoMap = nullptr; -#ifdef MOZ_ENABLE_PROFILER_SPS - // Tell the profiler that the context is gone - if (PseudoStack* stack = mozilla_get_pseudo_stack()) - stack->sampleContext(nullptr); -#endif - Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this); } @@ -3398,10 +3376,6 @@ XPCJSContext::Initialize() JS_AddWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback, this); JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks); js::SetPreserveWrapperCallback(cx, PreserveWrapper); -#ifdef MOZ_ENABLE_PROFILER_SPS - if (PseudoStack* stack = mozilla_get_pseudo_stack()) - stack->sampleContext(cx); -#endif JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback); js::SetActivityCallback(cx, ActivityCallback, this); JS_AddInterruptCallback(cx, InterruptCallback); diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp index 45d00d390..a6432856d 100644 --- a/js/xpconnect/src/XPCShellImpl.cpp +++ b/js/xpconnect/src/XPCShellImpl.cpp @@ -44,9 +44,6 @@ #ifdef XP_WIN #include "mozilla/widget/AudioSession.h" #include <windows.h> -#if defined(MOZ_SANDBOX) -#include "SandboxBroker.h" -#endif #endif // all this crap is needed to do the interactive shell stuff @@ -1235,11 +1232,8 @@ GetCurrentWorkingDirectory(nsAString& workingDirectory) static JSSecurityCallbacks shellSecurityCallbacks; int -XRE_XPCShellMain(int argc, char** argv, char** envp, - const XREShellData* aShellData) +XRE_XPCShellMain(int argc, char** argv, char** envp) { - MOZ_ASSERT(aShellData); - JSContext* cx; int result = 0; nsresult rv; @@ -1484,16 +1478,6 @@ XRE_XPCShellMain(int argc, char** argv, char** envp, // Plugin may require audio session if installed plugin can initialize // asynchronized. AutoAudioSession audioSession; - -#if defined(MOZ_SANDBOX) - // Required for sandboxed child processes. - if (aShellData->sandboxBrokerServices) { - SandboxBroker::Initialize(aShellData->sandboxBrokerServices); - } else { - NS_WARNING("Failed to initialize broker services, sandboxed " - "processes will fail to start."); - } -#endif #endif { diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index acf92f3c3..a12e36baa 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -1785,9 +1785,12 @@ CallMethodHelper::ConvertIndependentParam(uint8_t i) // indirectly, regardless of in/out-ness. if (type_tag == nsXPTType::T_JSVAL) { // Root the value. - dp->val.j.setUndefined(); - if (!js::AddRawValueRoot(mCallContext, &dp->val.j, "XPCWrappedNative::CallMethod param")) + dp->val.j.asValueRef().setUndefined(); + if (!js::AddRawValueRoot(mCallContext, &dp->val.j.asValueRef(), + "XPCWrappedNative::CallMethod param")) + { return false; + } } // Flag cleanup for anything that isn't self-contained. diff --git a/js/xpconnect/tests/unit/test_exportFunction.js b/js/xpconnect/tests/unit/test_exportFunction.js index 830816342..9e1bf2082 100644 --- a/js/xpconnect/tests/unit/test_exportFunction.js +++ b/js/xpconnect/tests/unit/test_exportFunction.js @@ -10,12 +10,14 @@ function run_test() { epsb.do_check_true = do_check_true; epsb.do_check_eq = do_check_eq; subsb.do_check_true = do_check_true; + subsb.do_check_eq = do_check_eq; // Exporting should work if prinicipal of the source sandbox // subsumes the principal of the target sandbox. Cu.evalInSandbox("(" + function() { var wasCalled = false; this.funToExport = function(expectedThis, a, obj, native, mixed, callback) { + do_check_eq(arguments.callee.length, 6); do_check_eq(a, 42); do_check_eq(obj, subsb.tobecloned); do_check_eq(obj.cloned, "cloned"); @@ -53,6 +55,7 @@ function run_test() { invokedCallback = false; callback = function() { invokedCallback = true; }; imported(this, 42, tobecloned, native, mixed, callback); + do_check_eq(imported.length, 6); do_check_true(invokedCallback); }.toSource() + ")()", subsb); |