diff options
author | Moonchild <moonchild@palemoon.org> | 2021-03-31 11:54:20 +0000 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2021-03-31 11:54:20 +0000 |
commit | be437d498efe62597993e4df90595def641e105b (patch) | |
tree | e3b907e2b22734e5b8a0c4042e3fbdcbbb446212 /js | |
parent | 232abfc22fd519cd3aec4c352daffd865f8cffc7 (diff) | |
download | uxp-be437d498efe62597993e4df90595def641e105b.tar.gz |
Issue mcp-graveyard/UXP#1756 - Initial wrapped implementation in C++
Diffstat (limited to 'js')
-rw-r--r-- | js/src/builtin/Intl.cpp | 517 | ||||
-rw-r--r-- | js/src/builtin/Intl.js | 15 | ||||
-rw-r--r-- | js/src/builtin/Number.js | 2 | ||||
-rw-r--r-- | js/src/jsapi.h | 19 | ||||
-rw-r--r-- | js/src/shell/js.cpp | 5 | ||||
-rw-r--r-- | js/src/vm/CommonPropertyNames.h | 14 |
6 files changed, 545 insertions, 27 deletions
diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 622e773e0f..71e40a2d54 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -13,7 +13,6 @@ #include "mozilla/Casting.h" #include "mozilla/PodOperations.h" #include "mozilla/Range.h" -#include "mozilla/ScopeExit.h" #include <string.h> @@ -23,6 +22,7 @@ #include "jsobj.h" #include "builtin/IntlTimeZoneData.h" +#include "ds/Sort.h" #include "unicode/plurrule.h" #include "unicode/ucal.h" #include "unicode/ucol.h" @@ -48,8 +48,8 @@ using namespace js; using mozilla::AssertedCast; using mozilla::IsFinite; +using mozilla::IsNaN; using mozilla::IsNegativeZero; -using mozilla::MakeScopeExit; using mozilla::PodCopy; @@ -905,6 +905,24 @@ CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObjec return nullptr; } +#if defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) + // If the still-experimental NumberFormat.prototype.formatToParts method is + // enabled, also add it. + if (cx->compartment()->creationOptions().experimentalNumberFormatFormatToPartsEnabled()) { + RootedValue ftp(cx); + HandlePropertyName name = cx->names().formatToParts; + if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), + cx->names().NumberFormatFormatToParts, + name, 1, &ftp)) + { + return nullptr; + } + + if (!DefineProperty(cx, proto, cx->names().formatToParts, ftp, nullptr, nullptr, 0)) + return nullptr; + } +#endif // defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) + RootedValue options(cx); if (!CreateDefaultOptions(cx, &options)) return nullptr; @@ -1186,31 +1204,72 @@ NewUNumberFormat(JSContext* cx, HandleObject numberFormat) return toClose.forget(); } +using FormattedNumberChars = Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE>; + static bool -intl_FormatNumber(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue result) -{ - // FormatNumber doesn't consider -0.0 to be negative. - if (IsNegativeZero(x)) - x = 0.0; +PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, double* x, + UFieldPositionIterator* fpositer, FormattedNumberChars& formattedChars) +{ + // PartitionNumberPattern doesn't consider -0.0 to be negative. + if (IsNegativeZero(*x)) + *x = 0.0; + + MOZ_ASSERT(formattedChars.length() == 0, + "formattedChars must initially be empty"); + MOZ_ALWAYS_TRUE(formattedChars.resize(INITIAL_CHAR_BUFFER_SIZE)); + UErrorCode status = U_ZERO_ERROR; + +#if !defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) + MOZ_ASSERT(fpositer == nullptr, + "shouldn't be requesting field information from an ICU that " + "can't provide it"); +#endif - Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx); - if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE)) - return false; - UErrorCode status = U_ZERO_ERROR; - int size = unum_formatDouble(nf, x, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE, - nullptr, &status); + int32_t resultSize; +#if defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) + resultSize = + unum_formatDoubleForFields(nf, *x, + Char16ToUChar(formattedChars.begin()), INITIAL_CHAR_BUFFER_SIZE, + fpositer, &status); +#else + resultSize = + unum_formatDouble(nf, *x, Char16ToUChar(formattedChars.begin()), INITIAL_CHAR_BUFFER_SIZE, + nullptr, &status); +#endif // defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) if (status == U_BUFFER_OVERFLOW_ERROR) { - if (!chars.resize(size)) + if (!formattedChars.resize(size_t(resultSize))) return false; status = U_ZERO_ERROR; - unum_formatDouble(nf, x, Char16ToUChar(chars.begin()), size, nullptr, &status); +#ifdef DEBUG + int32_t size = +#endif +#if defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) + unum_formatDoubleForFields(nf, *x, Char16ToUChar(formattedChars.begin()), resultSize, + fpositer, &status); +#else + unum_formatDouble(nf, *x, Char16ToUChar(formattedChars.begin()), resultSize, + nullptr, &status); +#endif // defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) + MOZ_ASSERT(size == resultSize); } if (U_FAILURE(status)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR); return false; } - JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size); + return formattedChars.resize(size_t(resultSize)); +} + +static bool +intl_FormatNumber(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue result) +{ + // Passing null for |fpositer| will just not compute partition information, + // letting us common up all ICU number-formatting code. + FormattedNumberChars chars(cx); + if (!PartitionNumberPattern(cx, nf, &x, nullptr, chars)) + return false; + + JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), chars.length()); if (!str) return false; @@ -1218,13 +1277,414 @@ intl_FormatNumber(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue return true; } +using FieldType = ImmutablePropertyNamePtr JSAtomState::*; + +#if defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) + +static FieldType +GetFieldTypeForNumberField(UNumberFormatFields fieldName, double d) +{ + // See intl/icu/source/i18n/unicode/unum.h for a detailed field list. This + // list is deliberately exhaustive: cases might have to be added/removed if + // this code is compiled with a different ICU with more UNumberFormatFields + // enum initializers. Please guard such cases with appropriate ICU + // version-testing #ifdefs, should cross-version divergence occur. + switch (fieldName) { + case UNUM_INTEGER_FIELD: + if (IsNaN(d)) + return &JSAtomState::nan; + if (!IsFinite(d)) + return &JSAtomState::infinity; + return &JSAtomState::integer; + + case UNUM_GROUPING_SEPARATOR_FIELD: + return &JSAtomState::group; + + case UNUM_DECIMAL_SEPARATOR_FIELD: + return &JSAtomState::decimal; + + case UNUM_FRACTION_FIELD: + return &JSAtomState::fraction; + + case UNUM_SIGN_FIELD: { + MOZ_ASSERT(!IsNegativeZero(d), + "-0 should have been excluded by PartitionNumberPattern"); + + // Manual trawling through the ICU call graph appears to indicate that + // the basic formatting we request will never include a positive sign. + // But this analysis may be mistaken, so don't absolutely trust it. + return d < 0 ? &JSAtomState::minusSign : &JSAtomState::plusSign; + } + + case UNUM_PERCENT_FIELD: + return &JSAtomState::percentSign; + + case UNUM_CURRENCY_FIELD: + return &JSAtomState::currency; + + case UNUM_PERMILL_FIELD: + MOZ_ASSERT_UNREACHABLE("unexpected permill field found, even though " + "we don't use any user-defined patterns that " + "would require a permill field"); + break; + + case UNUM_EXPONENT_SYMBOL_FIELD: + case UNUM_EXPONENT_SIGN_FIELD: + case UNUM_EXPONENT_FIELD: + MOZ_ASSERT_UNREACHABLE("exponent field unexpectedly found in " + "formatted number, even though UNUM_SCIENTIFIC " + "and scientific notation were never requested"); + break; + + case UNUM_FIELD_COUNT: + MOZ_ASSERT_UNREACHABLE("format field sentinel value returned by " + "iterator!"); + break; + } + + MOZ_ASSERT_UNREACHABLE("unenumerated, undocumented format field returned " + "by iterator"); + return nullptr; +} + +static bool +intl_FormatNumberToParts(JSContext* cx, UNumberFormat* nf, double x, MutableHandleValue result) +{ + UErrorCode status = U_ZERO_ERROR; + + UFieldPositionIterator* fpositer = ufieldpositer_open(&status); + if (U_FAILURE(status)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR); + return false; + } + + MOZ_ASSERT(fpositer); + ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer); + + FormattedNumberChars chars(cx); + if (!PartitionNumberPattern(cx, nf, &x, fpositer, chars)) + return false; + + RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx)); + if (!partsArray) + return false; + + RootedString overallResult(cx, NewStringCopyN<CanGC>(cx, chars.begin(), chars.length())); + if (!overallResult) + return false; + + // First, vacuum up fields in the overall formatted string. + + struct Field + { + uint32_t begin; + uint32_t end; + FieldType type; + + // Needed for vector-resizing scratch space. + Field() = default; + + Field(uint32_t begin, uint32_t end, FieldType type) + : begin(begin), end(end), type(type) + {} + }; + + using FieldsVector = Vector<Field, 16>; + FieldsVector fields(cx); + + int32_t fieldInt, beginIndexInt, endIndexInt; + while ((fieldInt = ufieldpositer_next(fpositer, &beginIndexInt, &endIndexInt)) >= 0) { + MOZ_ASSERT(beginIndexInt >= 0); + MOZ_ASSERT(endIndexInt >= 0); + MOZ_ASSERT(beginIndexInt < endIndexInt, + "erm, aren't fields always non-empty?"); + + FieldType type = GetFieldTypeForNumberField(UNumberFormatFields(fieldInt), x); + if (!fields.emplaceBack(uint32_t(beginIndexInt), uint32_t(endIndexInt), type)) + return false; + } + + // Second, merge sort the fields vector. Expand the vector to have scratch + // space for performing the sort. + size_t fieldsLen = fields.length(); + if (!fields.resizeUninitialized(fieldsLen * 2)) + return false; + + MOZ_ALWAYS_TRUE(MergeSort(fields.begin(), fieldsLen, fields.begin() + fieldsLen, + [](const Field& left, const Field& right, + bool* lessOrEqual) + { + // Sort first by begin index, then to place + // enclosing fields before nested fields. + *lessOrEqual = left.begin < right.begin || + (left.begin == right.begin && + left.end > right.end); + return true; + })); + + // Deallocate the scratch space. + if (!fields.resize(fieldsLen)) + return false; + + // Third, iterate over the sorted field list to generate a sequence of + // parts (what ECMA-402 actually exposes). A part is a maximal character + // sequence entirely within no field or a single most-nested field. + // + // Diagrams may be helpful to illustrate how fields map to parts. Consider + // formatting -28,114,774,228,750.32, the US national surplus (negative + // because it's actually a debt) on March 31, 2021. + // + // var options = + // { style: "currency", currency: "USD", currencyDisplay: "name" }; + // var usdFormatter = new Intl.NumberFormat("en-US", options); + // usdFormatter.format(-28114774228750.32); + // + // The formatted result is "-28,114,774,228,750.32 US dollars". ICU + // identifies these fields in the string: + // + // UNUM_GROUPING_SEPARATOR_FIELD + // | + // UNUM_SIGN_FIELD | UNUM_DECIMAL_SEPARATOR_FIELD + // | __________/| | + // | / | | | | + // "-28,114,774,228,750.32 US dollars" + // \________________/ |/ \_______/ + // | | | + // UNUM_INTEGER_FIELD | UNUM_CURRENCY_FIELD + // | + // UNUM_FRACTION_FIELD + // + // These fields map to parts as follows: + // + // integer decimal + // _____|________ | + // / /| |\ |\ |\ | literal + // /| / | | \ | \ | \| | + // "-28,114,774,228,750.32 US dollars" + // | \___|___|___/ |/ \________/ + // | | | | + // | group | currency + // | | + // minusSign fraction + // + // The sign is a part. Each comma is a part, splitting the integer field + // into parts for trillions/billions/&c. digits. The decimal point is a + // part. Cents are a part. The space between cents and currency is a part + // (outside any field). Last, the currency field is a part. + // + // Because parts fully partition the formatted string, we only track the + // end of each part -- the beginning is implicitly the last part's end. + struct Part + { + uint32_t end; + FieldType type; + }; + + class PartGenerator + { + // The fields in order from start to end, then least to most nested. + const FieldsVector& fields; + + // Index of the current field, in |fields|, being considered to + // determine part boundaries. |lastEnd <= fields[index].begin| is an + // invariant. + size_t index; + + // The end index of the last part produced, always less than or equal + // to |limit|, strictly increasing. + uint32_t lastEnd; + + // The length of the overall formatted string. + const uint32_t limit; + + Vector<size_t, 4> enclosingFields; + + void popEnclosingFieldsEndingAt(uint32_t end) { + MOZ_ASSERT_IF(enclosingFields.length() > 0, + fields[enclosingFields.back()].end >= end); + + while (enclosingFields.length() > 0 && fields[enclosingFields.back()].end == end) + enclosingFields.popBack(); + } + + bool nextPartInternal(Part* part) { + size_t len = fields.length(); + MOZ_ASSERT(index <= len); + + // If we're out of fields, all that remains are part(s) consisting + // of trailing portions of enclosing fields, and maybe a final + // literal part. + if (index == len) { + if (enclosingFields.length() > 0) { + const auto& enclosing = fields[enclosingFields.popCopy()]; + part->end = enclosing.end; + part->type = enclosing.type; + + // If additional enclosing fields end where this part ends, + // pop them as well. + popEnclosingFieldsEndingAt(part->end); + } else { + part->end = limit; + part->type = &JSAtomState::literal; + } + + return true; + } + + // Otherwise we still have a field to process. + const Field* current = &fields[index]; + MOZ_ASSERT(lastEnd <= current->begin); + MOZ_ASSERT(current->begin < current->end); + + // But first, deal with inter-field space. + if (lastEnd < current->begin) { + if (enclosingFields.length() > 0) { + // Space between fields, within an enclosing field, is part + // of that enclosing field, until the start of the current + // field or the end of the enclosing field, whichever is + // earlier. + const auto& enclosing = fields[enclosingFields.back()]; + part->end = std::min(enclosing.end, current->begin); + part->type = enclosing.type; + popEnclosingFieldsEndingAt(part->end); + } else { + // If there's no enclosing field, the space is a literal. + part->end = current->begin; + part->type = &JSAtomState::literal; + } + + return true; + } + + // Otherwise, the part spans a prefix of the current field. Find + // the most-nested field containing that prefix. + const Field* next; + do { + current = &fields[index]; + + // If the current field is last, the part extends to its end. + if (++index == len) { + part->end = current->end; + part->type = current->type; + return true; + } + + next = &fields[index]; + MOZ_ASSERT(current->begin <= next->begin); + MOZ_ASSERT(current->begin < next->end); + + // If the next field nests within the current field, push an + // enclosing field. (If there are no nested fields, don't + // bother pushing a field that'd be immediately popped.) + if (current->end > next->begin) { + if (!enclosingFields.append(index - 1)) + return false; + } + + // Do so until the next field begins after this one. + } while (current->begin == next->begin); + + part->type = current->type; + + if (current->end <= next->begin) { + // The next field begins after the current field ends. Therefore + // the current part ends at the end of the current field. + part->end = current->end; + popEnclosingFieldsEndingAt(part->end); + } else { + // The current field encloses the next one. The current part + // ends where the next field/part will start. + part->end = next->begin; + } + + return true; + } + + public: + PartGenerator(JSContext* cx, const FieldsVector& vec, uint32_t limit) + : fields(vec), index(0), lastEnd(0), limit(limit), enclosingFields(cx) + {} + + bool nextPart(bool* hasPart, Part* part) { + // There are no parts left if we've partitioned the entire string. + if (lastEnd == limit) { + MOZ_ASSERT(enclosingFields.length() == 0); + *hasPart = false; + return true; + } + + if (!nextPartInternal(part)) + return false; + + *hasPart = true; + lastEnd = part->end; + return true; + } + }; + + // Finally, generate the result array. + size_t lastEndIndex = 0; + uint32_t partIndex = 0; + RootedObject singlePart(cx); + RootedValue propVal(cx); + + PartGenerator gen(cx, fields, chars.length()); + do { + bool hasPart; + Part part; + if (!gen.nextPart(&hasPart, &part)) + return false; + + if (!hasPart) + break; + + FieldType type = part.type; + size_t endIndex = part.end; + + MOZ_ASSERT(lastEndIndex < endIndex); + + singlePart = NewBuiltinClassInstance<PlainObject>(cx); + if (!singlePart) + return false; + + propVal.setString(cx->names().*type); + if (!DefineProperty(cx, singlePart, cx->names().type, propVal)) + return false; + + JSLinearString* partSubstr = + NewDependentString(cx, overallResult, lastEndIndex, endIndex - lastEndIndex); + if (!partSubstr) + return false; + + propVal.setString(partSubstr); + if (!DefineProperty(cx, singlePart, cx->names().value, propVal)) + return false; + + propVal.setObject(*singlePart); + if (!DefineElement(cx, partsArray, partIndex, propVal)) + return false; + + lastEndIndex = endIndex; + partIndex++; + } while (true); + + MOZ_ASSERT(lastEndIndex == chars.length(), + "result array must partition the entire string"); + + result.setObject(*partsArray); + return true; +} + +#endif // defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) + bool js::intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - MOZ_ASSERT(args.length() == 2); + MOZ_ASSERT(args.length() == 3); MOZ_ASSERT(args[0].isObject()); MOZ_ASSERT(args[1].isNumber()); + MOZ_ASSERT(args[2].isBoolean()); RootedObject numberFormat(cx, &args[0].toObject()); @@ -1252,8 +1712,21 @@ js::intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp) } // Use the UNumberFormat to actually format the number. + double d = args[1].toNumber(); RootedValue result(cx); - bool success = intl_FormatNumber(cx, nf, args[1].toNumber(), &result); + + bool success; +#if defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) + if (args[2].toBoolean()) { + success = intl_FormatNumberToParts(cx, nf, d, &result); + } else +#endif // defined(ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS) + { + MOZ_ASSERT(!args[2].toBoolean(), + "shouldn't be doing formatToParts without an ICU that " + "supports it"); + success = intl_FormatNumber(cx, nf, d, &result); + } if (!isNumberFormatInstance) unum_close(nf); @@ -2145,8 +2618,6 @@ intl_FormatDateTime(JSContext* cx, UDateFormat* df, double x, MutableHandleValue return true; } -using FieldType = ImmutablePropertyNamePtr JSAtomState::*; - static FieldType GetFieldTypeForFormatField(UDateFormatField fieldName) { @@ -2251,7 +2722,7 @@ intl_FormatToPartsDateTime(JSContext* cx, UDateFormat* df, double x, MutableHand JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR); return false; } - auto closeFieldPosIter = MakeScopeExit([&]() { ufieldpositer_close(fpositer); }); + ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer); int resultSize = udat_formatForFields(df, x, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE, @@ -2285,7 +2756,6 @@ intl_FormatToPartsDateTime(JSContext* cx, UDateFormat* df, double x, MutableHand uint32_t partIndex = 0; RootedObject singlePart(cx); RootedValue partType(cx); - RootedString partSubstr(cx); RootedValue val(cx); auto AppendPart = [&](FieldType type, size_t beginIndex, size_t endIndex) { @@ -2297,7 +2767,8 @@ intl_FormatToPartsDateTime(JSContext* cx, UDateFormat* df, double x, MutableHand if (!DefineProperty(cx, singlePart, cx->names().type, partType)) return false; - partSubstr = SubstringKernel(cx, overallResult, beginIndex, endIndex - beginIndex); + JSLinearString* partSubstr = + NewDependentString(cx, overallResult, beginIndex, endIndex - beginIndex); if (!partSubstr) return false; diff --git a/js/src/builtin/Intl.js b/js/src/builtin/Intl.js index 281b0f4243..ef0aa986a6 100644 --- a/js/src/builtin/Intl.js +++ b/js/src/builtin/Intl.js @@ -2140,7 +2140,7 @@ function numberFormatFormatToBind(value) { // Step 1.a.ii-iii. var x = ToNumber(value); - return intl_FormatNumber(this, x); + return intl_FormatNumber(this, x, /* formatToParts = */ false); } @@ -2168,6 +2168,19 @@ function Intl_NumberFormat_format_get() { return internals.boundFormat; } +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); +} /** * Returns the resolved options for a NumberFormat object. diff --git a/js/src/builtin/Number.js b/js/src/builtin/Number.js index 07b2be57ab..323d2666b2 100644 --- a/js/src/builtin/Number.js +++ b/js/src/builtin/Number.js @@ -36,7 +36,7 @@ function Number_toLocaleString() { } // Step 5. - return intl_FormatNumber(numberFormat, x); + return intl_FormatNumber(numberFormat, x, /* formatToParts = */ false); } // ES6 draft ES6 20.1.2.4 diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 1eecdbf749..63119cb288 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2206,6 +2206,7 @@ class JS_PUBLIC_API(CompartmentCreationOptions) mergeable_(false), preserveJitCode_(false), cloneSingletons_(false), + experimentalNumberFormatFormatToPartsEnabled_(false), sharedMemoryAndAtomics_(false), secureContext_(false) { @@ -2270,6 +2271,23 @@ class JS_PUBLIC_API(CompartmentCreationOptions) return *this; } + // ECMA-402 is considering adding a "formatToParts" NumberFormat method, + // that exposes not just a formatted string but its subcomponents. The + // method, its semantics, and its name aren't finalized, so for now it's + // exposed *only* if requested. + // + // Until "formatToParts" is included in a final specification edition, it's + // subject to change or removal at any time. Do *not* rely on it in + // mission-critical code that can't be changed if ECMA-402 decides not to + // accept the method in its current form. + bool experimentalNumberFormatFormatToPartsEnabled() const { + return experimentalNumberFormatFormatToPartsEnabled_; + } + CompartmentCreationOptions& setExperimentalNumberFormatFormatToPartsEnabled(bool flag) { + experimentalNumberFormatFormatToPartsEnabled_ = flag; + return *this; + } + bool getSharedMemoryAndAtomicsEnabled() const; CompartmentCreationOptions& setSharedMemoryAndAtomicsEnabled(bool flag); @@ -2294,6 +2312,7 @@ class JS_PUBLIC_API(CompartmentCreationOptions) bool mergeable_; bool preserveJitCode_; bool cloneSingletons_; + bool experimentalNumberFormatFormatToPartsEnabled_; bool sharedMemoryAndAtomics_; bool secureContext_; }; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 6e155d3ff6..088551c302 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -4684,6 +4684,11 @@ NewGlobal(JSContext* cx, unsigned argc, Value* vp) if (v.isBoolean()) creationOptions.setCloneSingletons(v.toBoolean()); + if (!JS_GetProperty(cx, opts, "experimentalNumberFormatFormatToPartsEnabled", &v)) + return false; + if (v.isBoolean()) + creationOptions.setExperimentalNumberFormatFormatToPartsEnabled(v.toBoolean()); + if (!JS_GetProperty(cx, opts, "sameZoneAs", &v)) return false; if (v.isObject()) diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index a88406bc61..ed75802b1b 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -82,10 +82,10 @@ macro(currencyDisplay, currencyDisplay, "currencyDisplay") \ macro(DateTimeFormat, DateTimeFormat, "DateTimeFormat") \ macro(DateTimeFormatFormatGet, DateTimeFormatFormatGet, "Intl_DateTimeFormat_format_get") \ - macro(DateTimeFormatFormatToParts, DateTimeFormatFormatToParts, "Intl_DateTimeFormat_formatToParts") \ macro(day, day, "day") \ macro(dayPeriod, dayPeriod, "dayPeriod") \ macro(debugger, debugger, "debugger") \ + macro(decimal, decimal, "decimal") \ macro(decodeURI, decodeURI, "decodeURI") \ macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \ macro(DefaultBaseClassConstructor, DefaultBaseClassConstructor, "DefaultBaseClassConstructor") \ @@ -142,6 +142,8 @@ macro(forceInterpreter, forceInterpreter, "forceInterpreter") \ macro(forEach, forEach, "forEach") \ macro(format, format, "format") \ + macro(formatToParts, formatToParts, "formatToParts") \ + macro(fraction, fraction, "fraction") \ macro(frame, frame, "frame") \ macro(from, from, "from") \ macro(fulfilled, fulfilled, "fulfilled") \ @@ -160,6 +162,7 @@ macro(getPrototypeOf, getPrototypeOf, "getPrototypeOf") \ macro(global, global, "global") \ macro(globalThis, globalThis, "globalThis") \ + macro(group, group, "group") \ macro(Handle, Handle, "Handle") \ macro(has, has, "has") \ macro(hasOwn, hasOwn, "hasOwn") \ @@ -174,6 +177,7 @@ macro(includes, includes, "includes") \ macro(incumbentGlobal, incumbentGlobal, "incumbentGlobal") \ macro(index, index, "index") \ + macro(infinity, infinity, "infinity") \ macro(Infinity, Infinity, "Infinity") \ macro(InitializeCollator, InitializeCollator, "InitializeCollator") \ macro(InitializeDateTimeFormat, InitializeDateTimeFormat, "InitializeDateTimeFormat") \ @@ -189,6 +193,7 @@ macro(Int8x16, Int8x16, "Int8x16") \ macro(Int16x8, Int16x8, "Int16x8") \ macro(Int32x4, Int32x4, "Int32x4") \ + macro(integer, integer, "integer") \ macro(interface, interface, "interface") \ macro(InterpretGeneratorResume, InterpretGeneratorResume, "InterpretGeneratorResume") \ macro(isEntryPoint, isEntryPoint, "isEntryPoint") \ @@ -223,6 +228,7 @@ macro(minimumFractionDigits, minimumFractionDigits, "minimumFractionDigits") \ macro(minimumIntegerDigits, minimumIntegerDigits, "minimumIntegerDigits") \ macro(minimumSignificantDigits, minimumSignificantDigits, "minimumSignificantDigits") \ + macro(minusSign, minusSign, "minusSign") \ macro(minute, minute, "minute") \ macro(missingArguments, missingArguments, "missingArguments") \ macro(module, module, "module") \ @@ -232,6 +238,7 @@ macro(month, month, "month") \ macro(multiline, multiline, "multiline") \ macro(name, name, "name") \ + macro(nan, nan, "nan") \ macro(NaN, NaN, "NaN") \ macro(NegativeInfinity, NegativeInfinity, "-Infinity") \ macro(new, new_, "new") \ @@ -246,6 +253,7 @@ macro(notes, notes, "notes") \ macro(NumberFormat, NumberFormat, "NumberFormat") \ macro(NumberFormatFormatGet, NumberFormatFormatGet, "Intl_NumberFormat_format_get") \ + macro(NumberFormatFormatToParts, NumberFormatFormatToParts, "Intl_NumberFormat_formatToParts") \ macro(numeric, numeric, "numeric") \ macro(objectArguments, objectArguments, "[object Arguments]") \ macro(objectArray, objectArray, "[object Array]") \ @@ -271,9 +279,10 @@ macro(parseInt, parseInt, "parseInt") \ macro(pattern, pattern, "pattern") \ macro(pending, pending, "pending") \ + macro(percentSign, percentSign, "percentSign") \ macro(PluralRules, PluralRules, "PluralRules") \ macro(PluralRulesSelect, PluralRulesSelect, "Intl_PluralRules_Select") \ - macro(public, public_, "public") \ + macro(plusSign, plusSign, "plusSign") \ macro(preventExtensions, preventExtensions, "preventExtensions") \ macro(private, private_, "private") \ macro(promise, promise, "promise") \ @@ -282,6 +291,7 @@ macro(proto, proto, "__proto__") \ macro(prototype, prototype, "prototype") \ macro(proxy, proxy, "proxy") \ + macro(public, public_, "public") \ macro(raw, raw, "raw") \ macro(reason, reason, "reason") \ macro(RegExpFlagsGetter, RegExpFlagsGetter, "RegExpFlagsGetter") \ |