mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 23:38:38 +09:00
Issue #1756 - Initial wrapped implementation in C++
This commit is contained in:
parent
618fa768c8
commit
5df0028108
10 changed files with 760 additions and 27 deletions
134
intl/icu-patches/unum_formatDoubleForFields.diff
Normal file
134
intl/icu-patches/unum_formatDoubleForFields.diff
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
Add an ICU API for formatting a number into constituent parts (sign, integer, grouping separator, decimal, fraction, &c.) for use in implementing Intl.NumberFormat.prototype.formatToParts.
|
||||
|
||||
https://ssl.icu-project.org/trac/ticket/12684
|
||||
|
||||
diff --git a/intl/icu/source/i18n/unicode/unum.h b/intl/icu/source/i18n/unicode/unum.h
|
||||
--- a/intl/icu/source/i18n/unicode/unum.h
|
||||
+++ b/intl/icu/source/i18n/unicode/unum.h
|
||||
@@ -20,16 +20,17 @@
|
||||
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/uloc.h"
|
||||
#include "unicode/ucurr.h"
|
||||
#include "unicode/umisc.h"
|
||||
#include "unicode/parseerr.h"
|
||||
#include "unicode/uformattable.h"
|
||||
#include "unicode/udisplaycontext.h"
|
||||
+#include "unicode/ufieldpositer.h"
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \brief C API: NumberFormat
|
||||
*
|
||||
* <h2> Number Format C API </h2>
|
||||
*
|
||||
* Number Format C API Provides functions for
|
||||
@@ -647,16 +648,67 @@ U_STABLE int32_t U_EXPORT2
|
||||
unum_formatUFormattable(const UNumberFormat* fmt,
|
||||
const UFormattable *number,
|
||||
UChar *result,
|
||||
int32_t resultLength,
|
||||
UFieldPosition *pos,
|
||||
UErrorCode *status);
|
||||
|
||||
/**
|
||||
+* Format a double using a UNumberFormat according to the UNumberFormat's locale,
|
||||
+* and initialize a UFieldPositionIterator that enumerates the subcomponents of
|
||||
+* the resulting string.
|
||||
+*
|
||||
+* @param format
|
||||
+* The formatter to use.
|
||||
+* @param number
|
||||
+* The number to format.
|
||||
+* @param result
|
||||
+* A pointer to a buffer to receive the NULL-terminated formatted
|
||||
+* number. If the formatted number fits into dest but cannot be
|
||||
+* NULL-terminated (length == resultLength) then the error code is set
|
||||
+* to U_STRING_NOT_TERMINATED_WARNING. If the formatted number doesn't
|
||||
+* fit into result then the error code is set to
|
||||
+* U_BUFFER_OVERFLOW_ERROR.
|
||||
+* @param resultLength
|
||||
+* The maximum size of result.
|
||||
+* @param fpositer
|
||||
+* A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open}
|
||||
+* (may be NULL if field position information is not needed, but in this
|
||||
+* case it's preferable to use {@link #unum_formatDouble}). Iteration
|
||||
+* information already present in the UFieldPositionIterator is deleted,
|
||||
+* and the iterator is reset to apply to the fields in the formatted
|
||||
+* string created by this function call. The field values and indexes
|
||||
+* returned by {@link #ufieldpositer_next} represent fields denoted by
|
||||
+* the UNumberFormatFields enum. Fields are not returned in a guaranteed
|
||||
+* order. Fields cannot overlap, but they may nest. For example, 1234
|
||||
+* could format as "1,234" which might consist of a grouping separator
|
||||
+* field for ',' and an integer field encompassing the entire string.
|
||||
+* @param status
|
||||
+* A pointer to an UErrorCode to receive any errors
|
||||
+* @return
|
||||
+* The total buffer size needed; if greater than resultLength, the
|
||||
+* output was truncated.
|
||||
+* @see unum_formatDouble
|
||||
+* @see unum_parse
|
||||
+* @see unum_parseDouble
|
||||
+* @see UFieldPositionIterator
|
||||
+* @see UNumberFormatFields
|
||||
+* @draft ICU 59
|
||||
+*/
|
||||
+U_DRAFT int32_t U_EXPORT2
|
||||
+unum_formatDoubleForFields(const UNumberFormat* format,
|
||||
+ double number,
|
||||
+ UChar* result,
|
||||
+ int32_t resultLength,
|
||||
+ UFieldPositionIterator* fpositer,
|
||||
+ UErrorCode* status);
|
||||
+#define ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS
|
||||
+
|
||||
+/**
|
||||
* Parse a string into an integer using a UNumberFormat.
|
||||
* The string will be parsed according to the UNumberFormat's locale.
|
||||
* Note: parsing is not supported for styles UNUM_DECIMAL_COMPACT_SHORT
|
||||
* and UNUM_DECIMAL_COMPACT_LONG.
|
||||
* @param fmt The formatter to use.
|
||||
* @param text The text to parse.
|
||||
* @param textLength The length of text, or -1 if null-terminated.
|
||||
* @param parsePos If not NULL, on input a pointer to an integer specifying the offset at which
|
||||
diff --git a/intl/icu/source/i18n/unum.cpp b/intl/icu/source/i18n/unum.cpp
|
||||
--- a/intl/icu/source/i18n/unum.cpp
|
||||
+++ b/intl/icu/source/i18n/unum.cpp
|
||||
@@ -870,9 +870,37 @@ unum_formatUFormattable(const UNumberFor
|
||||
if(pos != 0) {
|
||||
pos->beginIndex = fp.getBeginIndex();
|
||||
pos->endIndex = fp.getEndIndex();
|
||||
}
|
||||
|
||||
return res.extract(result, resultLength, *status);
|
||||
}
|
||||
|
||||
+U_CAPI int32_t U_EXPORT2
|
||||
+unum_formatDoubleForFields(const UNumberFormat* format,
|
||||
+ double number,
|
||||
+ UChar* result,
|
||||
+ int32_t resultLength,
|
||||
+ UFieldPositionIterator* fpositer,
|
||||
+ UErrorCode* status)
|
||||
+{
|
||||
+ if (U_FAILURE(*status))
|
||||
+ return -1;
|
||||
+
|
||||
+ if (result == NULL ? resultLength != 0 : resultLength < 0) {
|
||||
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ UnicodeString res;
|
||||
+ if (result != NULL) {
|
||||
+ // NULL destination for pure preflighting: empty dummy string
|
||||
+ // otherwise, alias the destination buffer
|
||||
+ res.setTo(result, 0, resultLength);
|
||||
+ }
|
||||
+
|
||||
+ ((const NumberFormat*)format)->format(number, res, (FieldPositionIterator*)fpositer, *status);
|
||||
+
|
||||
+ return res.extract(result, resultLength, *status);
|
||||
+}
|
||||
+
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
#include "unicode/parseerr.h"
|
||||
#include "unicode/uformattable.h"
|
||||
#include "unicode/udisplaycontext.h"
|
||||
#include "unicode/ufieldpositer.h"
|
||||
|
||||
/**
|
||||
* \file
|
||||
|
|
@ -651,6 +652,57 @@ unum_formatUFormattable(const UNumberFormat* fmt,
|
|||
UFieldPosition *pos,
|
||||
UErrorCode *status);
|
||||
|
||||
/**
|
||||
* Format a double using a UNumberFormat according to the UNumberFormat's locale,
|
||||
* and initialize a UFieldPositionIterator that enumerates the subcomponents of
|
||||
* the resulting string.
|
||||
*
|
||||
* @param format
|
||||
* The formatter to use.
|
||||
* @param number
|
||||
* The number to format.
|
||||
* @param result
|
||||
* A pointer to a buffer to receive the NULL-terminated formatted
|
||||
* number. If the formatted number fits into dest but cannot be
|
||||
* NULL-terminated (length == resultLength) then the error code is set
|
||||
* to U_STRING_NOT_TERMINATED_WARNING. If the formatted number doesn't
|
||||
* fit into result then the error code is set to
|
||||
* U_BUFFER_OVERFLOW_ERROR.
|
||||
* @param resultLength
|
||||
* The maximum size of result.
|
||||
* @param fpositer
|
||||
* A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open}
|
||||
* (may be NULL if field position information is not needed, but in this
|
||||
* case it's preferable to use {@link #unum_formatDouble}). Iteration
|
||||
* information already present in the UFieldPositionIterator is deleted,
|
||||
* and the iterator is reset to apply to the fields in the formatted
|
||||
* string created by this function call. The field values and indexes
|
||||
* returned by {@link #ufieldpositer_next} represent fields denoted by
|
||||
* the UNumberFormatFields enum. Fields are not returned in a guaranteed
|
||||
* order. Fields cannot overlap, but they may nest. For example, 1234
|
||||
* could format as "1,234" which might consist of a grouping separator
|
||||
* field for ',' and an integer field encompassing the entire string.
|
||||
* @param status
|
||||
* A pointer to an UErrorCode to receive any errors
|
||||
* @return
|
||||
* The total buffer size needed; if greater than resultLength, the
|
||||
* output was truncated.
|
||||
* @see unum_formatDouble
|
||||
* @see unum_parse
|
||||
* @see unum_parseDouble
|
||||
* @see UFieldPositionIterator
|
||||
* @see UNumberFormatFields
|
||||
* @draft ICU 59
|
||||
*/
|
||||
U_DRAFT int32_t U_EXPORT2
|
||||
unum_formatDoubleForFields(const UNumberFormat* format,
|
||||
double number,
|
||||
UChar* result,
|
||||
int32_t resultLength,
|
||||
UFieldPositionIterator* fpositer,
|
||||
UErrorCode* status);
|
||||
#define ICU_UNUM_HAS_FORMATDOUBLEFORFIELDS
|
||||
|
||||
/**
|
||||
* Parse a string into an integer using a UNumberFormat.
|
||||
* The string will be parsed according to the UNumberFormat's locale.
|
||||
|
|
|
|||
|
|
@ -875,4 +875,32 @@ unum_formatUFormattable(const UNumberFormat* fmt,
|
|||
return res.extract(result, resultLength, *status);
|
||||
}
|
||||
|
||||
U_CAPI int32_t U_EXPORT2
|
||||
unum_formatDoubleForFields(const UNumberFormat* format,
|
||||
double number,
|
||||
UChar* result,
|
||||
int32_t resultLength,
|
||||
UFieldPositionIterator* fpositer,
|
||||
UErrorCode* status)
|
||||
{
|
||||
if (U_FAILURE(*status))
|
||||
return -1;
|
||||
|
||||
if (result == NULL ? resultLength != 0 : resultLength < 0) {
|
||||
*status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
UnicodeString res;
|
||||
if (result != NULL) {
|
||||
// NULL destination for pure preflighting: empty dummy string
|
||||
// otherwise, alias the destination buffer
|
||||
res.setTo(result, 0, resultLength);
|
||||
}
|
||||
|
||||
((const NumberFormat*)format)->format(number, res, (FieldPositionIterator*)fpositer, *status);
|
||||
|
||||
return res.extract(result, resultLength, *status);
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ for patch in \
|
|||
bug-1198952-workaround-make-3.82-bug.diff \
|
||||
bug-1228227-bug-1263325-libc++-gcc_hidden.diff \
|
||||
ucol_getKeywordValuesForLocale-ulist_resetList.diff \
|
||||
unum_formatDoubleForFields.diff \
|
||||
; do
|
||||
echo "Applying local patch $patch"
|
||||
patch -d ${icu_dir}/../../ -p1 --no-backup-if-mismatch < ${icu_dir}/../icu-patches/$patch
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
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;
|
||||
using FormattedNumberChars = Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE>;
|
||||
|
||||
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);
|
||||
static bool
|
||||
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
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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") \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue