tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

NumberFormat.js (34397B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /* Portions Copyright Norbert Lindenberg 2011-2012. */
      6 
      7 #include "NumberingSystemsGenerated.h"
      8 
      9 /**
     10 * NumberFormat internal properties.
     11 *
     12 * 9.1 Internal slots of Service Constructors
     13 * 15.2.3 Properties of the Intl.NumberFormat Constructor, Internal slots
     14 *
     15 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
     16 */
     17 var numberFormatInternalProperties = {
     18  localeData: numberFormatLocaleData,
     19  relevantExtensionKeys: ["nu"],
     20 };
     21 
     22 /**
     23 * 15.1.1 Intl.NumberFormat ( [ locales [ , options ] ] )
     24 *
     25 * Compute an internal properties object from |lazyNumberFormatData|.
     26 *
     27 * ES2025 Intl draft rev 5ea95f8a98d660e94c177d6f5e88c6d2962123b1
     28 */
     29 function resolveNumberFormatInternals(lazyNumberFormatData) {
     30  assert(IsObject(lazyNumberFormatData), "lazy data not an object?");
     31 
     32  var internalProps = std_Object_create(null);
     33 
     34  var NumberFormat = numberFormatInternalProperties;
     35 
     36  // Compute effective locale.
     37 
     38  // Step 11.
     39  var r = ResolveLocale(
     40    "NumberFormat",
     41    lazyNumberFormatData.requestedLocales,
     42    lazyNumberFormatData.opt,
     43    NumberFormat.relevantExtensionKeys,
     44    NumberFormat.localeData
     45  );
     46 
     47  // Steps 12-14. (Step 13 is not relevant to our implementation.)
     48  internalProps.locale = r.locale;
     49  internalProps.numberingSystem = r.nu;
     50 
     51  // Compute formatting options.
     52 
     53  // Step 15. SetNumberFormatUnitOptions, step 2.
     54  var style = lazyNumberFormatData.style;
     55  internalProps.style = style;
     56 
     57  // Step 15. SetNumberFormatUnitOptions, step 12.
     58  if (style === "currency") {
     59    internalProps.currency = lazyNumberFormatData.currency;
     60    internalProps.currencyDisplay = lazyNumberFormatData.currencyDisplay;
     61    internalProps.currencySign = lazyNumberFormatData.currencySign;
     62  }
     63 
     64  // Step 15. SetNumberFormatUnitOptions, step 13.
     65  if (style === "unit") {
     66    internalProps.unit = lazyNumberFormatData.unit;
     67    internalProps.unitDisplay = lazyNumberFormatData.unitDisplay;
     68  }
     69 
     70  // Step 18.
     71  var notation = lazyNumberFormatData.notation;
     72  internalProps.notation = notation;
     73 
     74  // Step 21. SetNumberFormatDigitOptions, step 6.
     75  internalProps.minimumIntegerDigits =
     76    lazyNumberFormatData.minimumIntegerDigits;
     77 
     78  // Step 21. SetNumberFormatDigitOptions, step 14.
     79  internalProps.roundingIncrement = lazyNumberFormatData.roundingIncrement;
     80 
     81  // Step 21. SetNumberFormatDigitOptions, step 15.
     82  internalProps.roundingMode = lazyNumberFormatData.roundingMode;
     83 
     84  // Step 21. SetNumberFormatDigitOptions, step 16.
     85  internalProps.trailingZeroDisplay = lazyNumberFormatData.trailingZeroDisplay;
     86 
     87  // Step 21. SetNumberFormatDigitOptions, steps 25-26.
     88  if ("minimumFractionDigits" in lazyNumberFormatData) {
     89    // Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
     90    // actual presence (versus undefined-ness) of these properties.
     91    assert(
     92      "maximumFractionDigits" in lazyNumberFormatData,
     93      "min/max frac digits mismatch"
     94    );
     95    internalProps.minimumFractionDigits =
     96      lazyNumberFormatData.minimumFractionDigits;
     97    internalProps.maximumFractionDigits =
     98      lazyNumberFormatData.maximumFractionDigits;
     99  }
    100 
    101  // Step 21. SetNumberFormatDigitOptions, steps 24 and 26.
    102  if ("minimumSignificantDigits" in lazyNumberFormatData) {
    103    // Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
    104    // actual presence (versus undefined-ness) of these properties.
    105    assert(
    106      "maximumSignificantDigits" in lazyNumberFormatData,
    107      "min/max sig digits mismatch"
    108    );
    109    internalProps.minimumSignificantDigits =
    110      lazyNumberFormatData.minimumSignificantDigits;
    111    internalProps.maximumSignificantDigits =
    112      lazyNumberFormatData.maximumSignificantDigits;
    113  }
    114 
    115  // Step 21. SetNumberFormatDigitOptions, steps 26-30.
    116  internalProps.roundingPriority = lazyNumberFormatData.roundingPriority;
    117 
    118  // Step 24.
    119  if (notation === "compact") {
    120    internalProps.compactDisplay = lazyNumberFormatData.compactDisplay;
    121  }
    122 
    123  // Step 29.
    124  internalProps.useGrouping = lazyNumberFormatData.useGrouping;
    125 
    126  // Step 31.
    127  internalProps.signDisplay = lazyNumberFormatData.signDisplay;
    128 
    129  // The caller is responsible for associating |internalProps| with the right
    130  // object using |setInternalProperties|.
    131  return internalProps;
    132 }
    133 
    134 /**
    135 * Returns an object containing the NumberFormat internal properties of |obj|.
    136 */
    137 function getNumberFormatInternals(obj) {
    138  assert(IsObject(obj), "getNumberFormatInternals called with non-object");
    139  assert(
    140    intl_GuardToNumberFormat(obj) !== null,
    141    "getNumberFormatInternals called with non-NumberFormat"
    142  );
    143 
    144  var internals = getIntlObjectInternals(obj);
    145  assert(
    146    internals.type === "NumberFormat",
    147    "bad type escaped getIntlObjectInternals"
    148  );
    149 
    150  // If internal properties have already been computed, use them.
    151  var internalProps = maybeInternalProperties(internals);
    152  if (internalProps) {
    153    return internalProps;
    154  }
    155 
    156  // Otherwise it's time to fully create them.
    157  internalProps = resolveNumberFormatInternals(internals.lazyData);
    158  setInternalProperties(internals, internalProps);
    159  return internalProps;
    160 }
    161 
    162 /**
    163 * 15.5.10 UnwrapNumberFormat ( nf )
    164 *
    165 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
    166 */
    167 function UnwrapNumberFormat(nf) {
    168  // Steps 1-3 (error handling moved to caller).
    169  if (
    170    IsObject(nf) &&
    171    intl_GuardToNumberFormat(nf) === null &&
    172    !intl_IsWrappedNumberFormat(nf) &&
    173    callFunction(
    174      std_Object_isPrototypeOf,
    175      GetBuiltinPrototype("NumberFormat"),
    176      nf
    177    )
    178  ) {
    179    return nf[intlFallbackSymbol()];
    180  }
    181  return nf;
    182 }
    183 
    184 /* eslint-disable complexity */
    185 /**
    186 * 15.1.3 SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault, notation )
    187 *
    188 * Applies digit options used for number formatting onto the intl object.
    189 *
    190 * ES2024 Intl draft rev a1db4567870dbe505121a4255f1210338757190a
    191 */
    192 function SetNumberFormatDigitOptions(
    193  lazyData,
    194  options,
    195  mnfdDefault,
    196  mxfdDefault,
    197  notation
    198 ) {
    199  assert(IsObject(options), "SetNumberFormatDigitOptions");
    200  assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions");
    201  assert(typeof mxfdDefault === "number", "SetNumberFormatDigitOptions");
    202  assert(mnfdDefault <= mxfdDefault, "SetNumberFormatDigitOptions");
    203  assert(typeof notation === "string", "SetNumberFormatDigitOptions");
    204 
    205  // Steps 1-5.
    206  var mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
    207  var mnfd = options.minimumFractionDigits;
    208  var mxfd = options.maximumFractionDigits;
    209  var mnsd = options.minimumSignificantDigits;
    210  var mxsd = options.maximumSignificantDigits;
    211 
    212  // Step 6.
    213  lazyData.minimumIntegerDigits = mnid;
    214 
    215  // Step 7.
    216  var roundingIncrement = GetNumberOption(
    217    options,
    218    "roundingIncrement",
    219    1,
    220    5000,
    221    1
    222  );
    223 
    224  // Step 8.
    225  switch (roundingIncrement) {
    226    case 1:
    227    case 2:
    228    case 5:
    229    case 10:
    230    case 20:
    231    case 25:
    232    case 50:
    233    case 100:
    234    case 200:
    235    case 250:
    236    case 500:
    237    case 1000:
    238    case 2000:
    239    case 2500:
    240    case 5000:
    241      break;
    242    default:
    243      ThrowRangeError(
    244        JSMSG_INVALID_OPTION_VALUE,
    245        "roundingIncrement",
    246        roundingIncrement
    247      );
    248  }
    249 
    250  // Step 9.
    251  var roundingMode = GetOption(
    252    options,
    253    "roundingMode",
    254    "string",
    255    [
    256      "ceil",
    257      "floor",
    258      "expand",
    259      "trunc",
    260      "halfCeil",
    261      "halfFloor",
    262      "halfExpand",
    263      "halfTrunc",
    264      "halfEven",
    265    ],
    266    "halfExpand"
    267  );
    268 
    269  // Step 10.
    270  var roundingPriority = GetOption(
    271    options,
    272    "roundingPriority",
    273    "string",
    274    ["auto", "morePrecision", "lessPrecision"],
    275    "auto"
    276  );
    277 
    278  // Step 11.
    279  var trailingZeroDisplay = GetOption(
    280    options,
    281    "trailingZeroDisplay",
    282    "string",
    283    ["auto", "stripIfInteger"],
    284    "auto"
    285  );
    286 
    287  // Step 12. (This step is a note.)
    288 
    289  // Step 13.
    290  if (roundingIncrement !== 1) {
    291    mxfdDefault = mnfdDefault;
    292  }
    293 
    294  // Step 14.
    295  lazyData.roundingIncrement = roundingIncrement;
    296 
    297  // Step 15.
    298  lazyData.roundingMode = roundingMode;
    299 
    300  // Step 16.
    301  lazyData.trailingZeroDisplay = trailingZeroDisplay;
    302 
    303  // Step 17.
    304  var hasSignificantDigits = mnsd !== undefined || mxsd !== undefined;
    305 
    306  // Step 28.
    307  var hasFractionDigits = mnfd !== undefined || mxfd !== undefined;
    308 
    309  // Steps 19 and 21.a.
    310  var needSignificantDigits =
    311    roundingPriority !== "auto" || hasSignificantDigits;
    312 
    313  // Steps 20 and 21.b.i.
    314  var needFractionalDigits =
    315    roundingPriority !== "auto" ||
    316    !(hasSignificantDigits || (!hasFractionDigits && notation === "compact"));
    317 
    318  // Step 22.
    319  if (needSignificantDigits) {
    320    // Step 22.a.
    321    if (hasSignificantDigits) {
    322      // Step 22.a.i.
    323      mnsd = DefaultNumberOption(mnsd, 1, 21, 1);
    324      lazyData.minimumSignificantDigits = mnsd;
    325 
    326      // Step 22.a.ii.
    327      mxsd = DefaultNumberOption(mxsd, mnsd, 21, 21);
    328      lazyData.maximumSignificantDigits = mxsd;
    329    } else {
    330      // Step 22.b.i.
    331      lazyData.minimumSignificantDigits = 1;
    332 
    333      // Step 22.b.ii.
    334      lazyData.maximumSignificantDigits = 21;
    335    }
    336  }
    337 
    338  // Step 23.
    339  if (needFractionalDigits) {
    340    // Step 23.a.
    341    if (hasFractionDigits) {
    342      // Step 23.a.i.
    343      mnfd = DefaultNumberOption(mnfd, 0, 100, undefined);
    344 
    345      // Step 23.a.ii.
    346      mxfd = DefaultNumberOption(mxfd, 0, 100, undefined);
    347 
    348      // Step 23.a.iii.
    349      if (mnfd === undefined) {
    350        assert(
    351          mxfd !== undefined,
    352          "mxfd isn't undefined when mnfd is undefined"
    353        );
    354        mnfd = std_Math_min(mnfdDefault, mxfd);
    355      }
    356 
    357      // Step 23.a.iv.
    358      else if (mxfd === undefined) {
    359        mxfd = std_Math_max(mxfdDefault, mnfd);
    360      }
    361 
    362      // Step 23.a.v.
    363      else if (mnfd > mxfd) {
    364        ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, mxfd);
    365      }
    366 
    367      // Step 23.a.vi.
    368      lazyData.minimumFractionDigits = mnfd;
    369 
    370      // Step 23.a.vii.
    371      lazyData.maximumFractionDigits = mxfd;
    372    } else {
    373      // Step 23.b.i.
    374      lazyData.minimumFractionDigits = mnfdDefault;
    375 
    376      // Step 23.b.ii.
    377      lazyData.maximumFractionDigits = mxfdDefault;
    378    }
    379  }
    380 
    381  // Steps 24-28.
    382  if (!needSignificantDigits && !needFractionalDigits) {
    383    assert(!hasSignificantDigits, "bad significant digits in fallback case");
    384    assert(
    385      roundingPriority === "auto",
    386      `bad rounding in fallback case: ${roundingPriority}`
    387    );
    388    assert(
    389      notation === "compact",
    390      `bad notation in fallback case: ${notation}`
    391    );
    392 
    393    // Steps 24.a-f.
    394    lazyData.minimumFractionDigits = 0;
    395    lazyData.maximumFractionDigits = 0;
    396    lazyData.minimumSignificantDigits = 1;
    397    lazyData.maximumSignificantDigits = 2;
    398    lazyData.roundingPriority = "morePrecision";
    399  } else {
    400    // Steps 25-28.
    401    //
    402    // Our implementation stores |roundingPriority| instead of using
    403    // [[RoundingType]].
    404    lazyData.roundingPriority = roundingPriority;
    405  }
    406 
    407  // Step 29.
    408  if (roundingIncrement !== 1) {
    409    // Step 29.a.
    410    //
    411    // [[RoundingType]] is `fractionDigits` if |roundingPriority| is equal to
    412    // "auto" and |hasSignificantDigits| is false.
    413    if (roundingPriority !== "auto") {
    414      ThrowTypeError(
    415        JSMSG_INVALID_NUMBER_OPTION,
    416        "roundingIncrement",
    417        "roundingPriority"
    418      );
    419    }
    420    if (hasSignificantDigits) {
    421      ThrowTypeError(
    422        JSMSG_INVALID_NUMBER_OPTION,
    423        "roundingIncrement",
    424        "minimumSignificantDigits"
    425      );
    426    }
    427 
    428    // Step 29.b.
    429    //
    430    // Minimum and maximum fraction digits must be equal.
    431    if (
    432      lazyData.minimumFractionDigits !==
    433      lazyData.maximumFractionDigits
    434    ) {
    435      ThrowRangeError(JSMSG_UNEQUAL_FRACTION_DIGITS);
    436    }
    437  }
    438 }
    439 /* eslint-enable complexity */
    440 
    441 /**
    442 * Convert s to upper case, but limited to characters a-z.
    443 *
    444 * Spec: ECMAScript Internationalization API Specification, 6.1.
    445 */
    446 function toASCIIUpperCase(s) {
    447  assert(typeof s === "string", "toASCIIUpperCase");
    448 
    449  // String.prototype.toUpperCase may map non-ASCII characters into ASCII,
    450  // so go character by character (actually code unit by code unit, but
    451  // since we only care about ASCII characters here, that's OK).
    452  var result = "";
    453  for (var i = 0; i < s.length; i++) {
    454    var c = callFunction(std_String_charCodeAt, s, i);
    455    result +=
    456      0x61 <= c && c <= 0x7a
    457        ? callFunction(std_String_fromCharCode, null, c & ~0x20)
    458        : s[i];
    459  }
    460  return result;
    461 }
    462 
    463 /**
    464 * 6.3.1 IsWellFormedCurrencyCode ( currency )
    465 *
    466 * Verifies that the given string is a well-formed ISO 4217 currency code.
    467 *
    468 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
    469 */
    470 function IsWellFormedCurrencyCode(currency) {
    471  assert(typeof currency === "string", "currency is a string value");
    472 
    473  return currency.length === 3 && IsASCIIAlphaString(currency);
    474 }
    475 
    476 /**
    477 * 6.6.1 IsWellFormedUnitIdentifier ( unitIdentifier )
    478 *
    479 * Verifies that the given string is a well-formed core unit identifier as
    480 * defined in UTS #35, Part 2, Section 6. In addition to obeying the UTS #35
    481 * core unit identifier syntax, |unitIdentifier| must be one of the identifiers
    482 * sanctioned by UTS #35 or be a compound unit composed of two sanctioned simple
    483 * units.
    484 *
    485 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
    486 */
    487 function IsWellFormedUnitIdentifier(unitIdentifier) {
    488  assert(
    489    typeof unitIdentifier === "string",
    490    "unitIdentifier is a string value"
    491  );
    492 
    493  // Step 1.
    494  if (IsSanctionedSimpleUnitIdentifier(unitIdentifier)) {
    495    return true;
    496  }
    497 
    498  // Steps 2-3.
    499  var pos = callFunction(std_String_indexOf, unitIdentifier, "-per-");
    500  if (pos < 0) {
    501    return false;
    502  }
    503 
    504  // Step 4.
    505  //
    506  // Sanctioned single unit identifiers don't include the substring "-per-",
    507  // so we can skip searching for the second "-per-" substring.
    508 
    509  var next = pos + "-per-".length;
    510 
    511  // Steps 5-6.
    512  var numerator = Substring(unitIdentifier, 0, pos);
    513  var denominator = Substring(
    514    unitIdentifier,
    515    next,
    516    unitIdentifier.length - next
    517  );
    518 
    519  // Steps 7-8.
    520  return (
    521    IsSanctionedSimpleUnitIdentifier(numerator) &&
    522    IsSanctionedSimpleUnitIdentifier(denominator)
    523  );
    524 }
    525 
    526 #if DEBUG || MOZ_SYSTEM_ICU
    527 var availableMeasurementUnits = {
    528  value: null,
    529 };
    530 #endif
    531 
    532 /**
    533 * 6.6.2 IsSanctionedSingleUnitIdentifier ( unitIdentifier )
    534 *
    535 * Verifies that the given string is a sanctioned simple core unit identifier.
    536 *
    537 * Also see: https://unicode.org/reports/tr35/tr35-general.html#Unit_Elements
    538 *
    539 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
    540 */
    541 function IsSanctionedSimpleUnitIdentifier(unitIdentifier) {
    542  assert(
    543    typeof unitIdentifier === "string",
    544    "unitIdentifier is a string value"
    545  );
    546 
    547  var isSanctioned = hasOwn(unitIdentifier, sanctionedSimpleUnitIdentifiers);
    548 
    549 #if DEBUG || MOZ_SYSTEM_ICU
    550  if (isSanctioned) {
    551    if (availableMeasurementUnits.value === null) {
    552      availableMeasurementUnits.value = intl_availableMeasurementUnits();
    553    }
    554 
    555    var isSupported = hasOwn(unitIdentifier, availableMeasurementUnits.value);
    556 
    557 #if MOZ_SYSTEM_ICU
    558    // A system ICU may support fewer measurement units, so we need to make
    559    // sure the unit is actually supported.
    560    isSanctioned = isSupported;
    561 #else
    562    // Otherwise just assert that the sanctioned unit is also supported.
    563    assert(
    564      isSupported,
    565      `"${unitIdentifier}" is sanctioned but not supported. Did you forget to update
    566      intl/icu/data_filter.json to include the unit (and any implicit compound units)?
    567      For example "speed/kilometer-per-hour" is implied by "length/kilometer" and
    568      "duration/hour" and must therefore also be present.`
    569    );
    570 #endif
    571  }
    572 #endif
    573 
    574  return isSanctioned;
    575 }
    576 
    577 /* eslint-disable complexity */
    578 /**
    579 * 15.1.1 Intl.NumberFormat ( [ locales [ , options ] ] )
    580 *
    581 * Initializes an object as a NumberFormat.
    582 *
    583 * This method is complicated a moderate bit by its implementing initialization
    584 * as a *lazy* concept.  Everything that must happen now, does -- but we defer
    585 * all the work we can until the object is actually used as a NumberFormat.
    586 * This later work occurs in |resolveNumberFormatInternals|; steps not noted
    587 * here occur there.
    588 *
    589 * ES2025 Intl draft rev 5ea95f8a98d660e94c177d6f5e88c6d2962123b1
    590 */
    591 function InitializeNumberFormat(numberFormat, thisValue, locales, options) {
    592  assert(
    593    IsObject(numberFormat),
    594    "InitializeNumberFormat called with non-object"
    595  );
    596  assert(
    597    intl_GuardToNumberFormat(numberFormat) !== null,
    598    "InitializeNumberFormat called with non-NumberFormat"
    599  );
    600 
    601  // Lazy NumberFormat data has the following structure:
    602  //
    603  //   {
    604  //     requestedLocales: List of locales,
    605  //     style: "decimal" / "percent" / "currency" / "unit",
    606  //
    607  //     // fields present only if style === "currency":
    608  //     currency: a well-formed currency code (IsWellFormedCurrencyCode),
    609  //     currencyDisplay: "code" / "symbol" / "narrowSymbol" / "name",
    610  //     currencySign: "standard" / "accounting",
    611  //
    612  //     // fields present only if style === "unit":
    613  //     unit: a well-formed unit identifier (IsWellFormedUnitIdentifier),
    614  //     unitDisplay: "short" / "narrow" / "long",
    615  //
    616  //     opt: // opt object computed in InitializeNumberFormat
    617  //       {
    618  //         localeMatcher: "lookup" / "best fit",
    619  //
    620  //         nu: string matching a Unicode extension type, // optional
    621  //       }
    622  //
    623  //     minimumIntegerDigits: integer ∈ [1, 21],
    624  //
    625  //     // optional, mutually exclusive with the significant-digits option
    626  //     minimumFractionDigits: integer ∈ [0, 100],
    627  //     maximumFractionDigits: integer ∈ [0, 100],
    628  //
    629  //     // optional, mutually exclusive with the fraction-digits option
    630  //     minimumSignificantDigits: integer ∈ [1, 21],
    631  //     maximumSignificantDigits: integer ∈ [1, 21],
    632  //
    633  //     roundingPriority: "auto" / "lessPrecision" / "morePrecision",
    634  //
    635  //     useGrouping: "auto" / "always" / "min2" / false,
    636  //
    637  //     notation: "standard" / "scientific" / "engineering" / "compact",
    638  //
    639  //     // optional, if notation is "compact"
    640  //     compactDisplay: "short" / "long",
    641  //
    642  //     signDisplay: "auto" / "never" / "always" / "exceptZero" / "negative",
    643  //
    644  //     trailingZeroDisplay: "auto" / "stripIfInteger",
    645  //
    646  //     roundingIncrement: integer ∈ (1, 2, 5,
    647  //                                   10, 20, 25, 50,
    648  //                                   100, 200, 250, 500,
    649  //                                   1000, 2000, 2500, 5000),
    650  //
    651  //     roundingMode: "ceil" / "floor" / "expand" / "trunc" /
    652  //                   "halfCeil" / "halfFloor" / "halfExpand" / "halfTrunc" / "halfEven",
    653  //   }
    654  //
    655  // Note that lazy data is only installed as a final step of initialization,
    656  // so every NumberFormat lazy data object has *all* these properties, never a
    657  // subset of them.
    658  var lazyNumberFormatData = std_Object_create(null);
    659 
    660  // Step 3.
    661  var requestedLocales = CanonicalizeLocaleList(locales);
    662  lazyNumberFormatData.requestedLocales = requestedLocales;
    663 
    664  // Step 4. (Inlined call to CoerceOptionsToObject.)
    665  //
    666  // If we ever need more speed here at startup, we should try to detect the
    667  // case where |options === undefined| and then directly use the default
    668  // value for each option.  For now, just keep it simple.
    669  if (options === undefined) {
    670    options = std_Object_create(null);
    671  } else {
    672    options = ToObject(options);
    673  }
    674 
    675  // Compute options that impact interpretation of locale.
    676 
    677  // Step 5.
    678  var opt = NEW_RECORD();
    679  lazyNumberFormatData.opt = opt;
    680 
    681  // Steps 6-7.
    682  var matcher = GetOption(
    683    options,
    684    "localeMatcher",
    685    "string",
    686    ["lookup", "best fit"],
    687    "best fit"
    688  );
    689  opt.localeMatcher = matcher;
    690 
    691  // Step 8.
    692  var numberingSystem = GetOption(
    693    options,
    694    "numberingSystem",
    695    "string",
    696    undefined,
    697    undefined
    698  );
    699 
    700  // Step 9.
    701  if (numberingSystem !== undefined) {
    702    numberingSystem = intl_ValidateAndCanonicalizeUnicodeExtensionType(
    703      numberingSystem,
    704      "numberingSystem",
    705      "nu"
    706    );
    707  }
    708 
    709  // Step 10.
    710  opt.nu = numberingSystem;
    711 
    712  // Compute formatting options.
    713 
    714  // Step 15. SetNumberFormatUnitOptions, steps 1-2.
    715  var style = GetOption(
    716    options,
    717    "style",
    718    "string",
    719    ["decimal", "percent", "currency", "unit"],
    720    "decimal"
    721  );
    722  lazyNumberFormatData.style = style;
    723 
    724  // Step 15. SetNumberFormatUnitOptions, step 3.
    725  var currency = GetOption(options, "currency", "string", undefined, undefined);
    726 
    727  // Step 15. SetNumberFormatUnitOptions, steps 4-5.
    728  if (currency === undefined) {
    729    if (style === "currency") {
    730      ThrowTypeError(JSMSG_UNDEFINED_CURRENCY);
    731    }
    732  } else {
    733    if (!IsWellFormedCurrencyCode(currency)) {
    734      ThrowRangeError(JSMSG_INVALID_CURRENCY_CODE, currency);
    735    }
    736  }
    737 
    738  // Step 15. SetNumberFormatUnitOptions, step 6.
    739  var currencyDisplay = GetOption(
    740    options,
    741    "currencyDisplay",
    742    "string",
    743    ["code", "symbol", "narrowSymbol", "name"],
    744    "symbol"
    745  );
    746 
    747  // Step 15. SetNumberFormatUnitOptions, step 7.
    748  var currencySign = GetOption(
    749    options,
    750    "currencySign",
    751    "string",
    752    ["standard", "accounting"],
    753    "standard"
    754  );
    755 
    756  // Step 15. SetNumberFormatUnitOptions, step 12. (Reordered)
    757  if (style === "currency") {
    758    // Step 15. SetNumberFormatUnitOptions, step 12.a.
    759    currency = toASCIIUpperCase(currency);
    760    lazyNumberFormatData.currency = currency;
    761 
    762    // Step 15. SetNumberFormatUnitOptions, step 12.b.
    763    lazyNumberFormatData.currencyDisplay = currencyDisplay;
    764 
    765    // Step 15. SetNumberFormatUnitOptions, step 12.c.
    766    lazyNumberFormatData.currencySign = currencySign;
    767  }
    768 
    769  // Step 15. SetNumberFormatUnitOptions, step 8.
    770  var unit = GetOption(options, "unit", "string", undefined, undefined);
    771 
    772  // Step 15. SetNumberFormatUnitOptions, steps 9-10.
    773  if (unit === undefined) {
    774    if (style === "unit") {
    775      ThrowTypeError(JSMSG_UNDEFINED_UNIT);
    776    }
    777  } else {
    778    if (!IsWellFormedUnitIdentifier(unit)) {
    779      ThrowRangeError(JSMSG_INVALID_UNIT_IDENTIFIER, unit);
    780    }
    781  }
    782 
    783  // Step 15. SetNumberFormatUnitOptions, step 11.
    784  var unitDisplay = GetOption(
    785    options,
    786    "unitDisplay",
    787    "string",
    788    ["short", "narrow", "long"],
    789    "short"
    790  );
    791 
    792  // Step 15. SetNumberFormatUnitOptions, step 13.
    793  if (style === "unit") {
    794    lazyNumberFormatData.unit = unit;
    795    lazyNumberFormatData.unitDisplay = unitDisplay;
    796  }
    797 
    798  // Step 16. (Not applicable in our implementation.)
    799 
    800  // Steps 17-18.
    801  var notation = GetOption(
    802    options,
    803    "notation",
    804    "string",
    805    ["standard", "scientific", "engineering", "compact"],
    806    "standard"
    807  );
    808  lazyNumberFormatData.notation = notation;
    809 
    810  // Steps 19-20.
    811  var mnfdDefault, mxfdDefault;
    812  if (style === "currency" && notation === "standard") {
    813    var cDigits = CurrencyDigits(currency);
    814    mnfdDefault = cDigits;
    815    mxfdDefault = cDigits;
    816  } else {
    817    mnfdDefault = 0;
    818    mxfdDefault = style === "percent" ? 0 : 3;
    819  }
    820 
    821  // Step 21.
    822  SetNumberFormatDigitOptions(
    823    lazyNumberFormatData,
    824    options,
    825    mnfdDefault,
    826    mxfdDefault,
    827    notation
    828  );
    829 
    830  // Steps 22 and 24.a.
    831  var compactDisplay = GetOption(
    832    options,
    833    "compactDisplay",
    834    "string",
    835    ["short", "long"],
    836    "short"
    837  );
    838  if (notation === "compact") {
    839    lazyNumberFormatData.compactDisplay = compactDisplay;
    840  }
    841 
    842  // Steps 23 and 24.b.
    843  var defaultUseGrouping = notation !== "compact" ? "auto" : "min2";
    844 
    845  // Steps 25-26.
    846  var useGrouping = GetStringOrBooleanOption(
    847    options,
    848    "useGrouping",
    849    ["min2", "auto", "always", "true", "false"],
    850    defaultUseGrouping
    851  );
    852 
    853  // Steps 27-28.
    854  if (useGrouping === "true" || useGrouping === "false") {
    855    useGrouping = defaultUseGrouping;
    856  } else if (useGrouping === true) {
    857    useGrouping = "always";
    858  }
    859 
    860  // Step 29.
    861  assert(
    862    useGrouping === "min2" ||
    863    useGrouping === "auto" ||
    864    useGrouping === "always" ||
    865    useGrouping === false,
    866    `invalid 'useGrouping' value: ${useGrouping}`
    867  );
    868  lazyNumberFormatData.useGrouping = useGrouping;
    869 
    870  // Steps 30-31.
    871  var signDisplay = GetOption(
    872    options,
    873    "signDisplay",
    874    "string",
    875    ["auto", "never", "always", "exceptZero", "negative"],
    876    "auto"
    877  );
    878  lazyNumberFormatData.signDisplay = signDisplay;
    879 
    880  // We've done everything that must be done now: mark the lazy data as fully
    881  // computed and install it.
    882  initializeIntlObject(numberFormat, "NumberFormat", lazyNumberFormatData);
    883 
    884  // Step 32. (Inlined call to ChainNumberFormat.)
    885  if (
    886    numberFormat !== thisValue &&
    887    callFunction(
    888      std_Object_isPrototypeOf,
    889      GetBuiltinPrototype("NumberFormat"),
    890      thisValue
    891    )
    892  ) {
    893    DefineDataProperty(
    894      thisValue,
    895      intlFallbackSymbol(),
    896      numberFormat,
    897      ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE
    898    );
    899 
    900    return thisValue;
    901  }
    902 
    903  // Step 33.
    904  return numberFormat;
    905 }
    906 /* eslint-enable complexity */
    907 
    908 /**
    909 * 15.5.1 CurrencyDigits ( currency )
    910 *
    911 * Returns the number of decimal digits to be used for the given currency.
    912 *
    913 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
    914 */
    915 function CurrencyDigits(currency) {
    916  assert(typeof currency === "string", "currency is a string value");
    917  assert(IsWellFormedCurrencyCode(currency), "currency is well-formed");
    918  assert(currency === toASCIIUpperCase(currency), "currency is all upper-case");
    919 
    920  // Step 1.
    921  if (hasOwn(currency, currencyDigits)) {
    922    return currencyDigits[currency];
    923  }
    924  return 2;
    925 }
    926 
    927 function getNumberingSystems(locale) {
    928  // ICU doesn't have an API to determine the set of numbering systems
    929  // supported for a locale; it generally pretends that any numbering system
    930  // can be used with any locale. Supporting a decimal numbering system
    931  // (where only the digits are replaced) is easy, so we offer them all here.
    932  // Algorithmic numbering systems are typically tied to one locale, so for
    933  // lack of information we don't offer them.
    934  // The one thing we can find out from ICU is the default numbering system
    935  // for a locale.
    936  var defaultNumberingSystem = intl_numberingSystem(locale);
    937  return [defaultNumberingSystem, NUMBERING_SYSTEMS_WITH_SIMPLE_DIGIT_MAPPINGS];
    938 }
    939 
    940 function numberFormatLocaleData() {
    941  return {
    942    nu: getNumberingSystems,
    943    default: {
    944      nu: intl_numberingSystem,
    945    },
    946  };
    947 }
    948 
    949 /**
    950 * 15.5.2 Number Format Functions
    951 *
    952 * Create function to be cached and returned by Intl.NumberFormat.prototype.format.
    953 *
    954 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
    955 */
    956 function createNumberFormatFormat(nf) {
    957  // This function is not inlined in $Intl_NumberFormat_format_get to avoid
    958  // creating a call-object on each call to $Intl_NumberFormat_format_get.
    959  return function(value) {
    960    // Step 1 (implicit).
    961 
    962    // Step 2.
    963    assert(IsObject(nf), "InitializeNumberFormat called with non-object");
    964    assert(
    965      intl_GuardToNumberFormat(nf) !== null,
    966      "InitializeNumberFormat called with non-NumberFormat"
    967    );
    968 
    969    // Steps 3-5.
    970    return intl_FormatNumber(nf, value, /* formatToParts = */ false);
    971  };
    972 }
    973 
    974 /**
    975 * 15.3.3 get Intl.NumberFormat.prototype.format
    976 *
    977 * Returns a function bound to this NumberFormat that returns a String value
    978 * representing the result of calling ToNumber(value) according to the
    979 * effective locale and the formatting options of this NumberFormat.
    980 *
    981 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
    982 */
    983 // Uncloned functions with `$` prefix are allocated as extended function
    984 // to store the original name in `SetCanonicalName`.
    985 function $Intl_NumberFormat_format_get() {
    986  // Steps 1-3.
    987  var thisArg = UnwrapNumberFormat(this);
    988  var nf = thisArg;
    989  if (!IsObject(nf) || (nf = intl_GuardToNumberFormat(nf)) === null) {
    990    return callFunction(
    991      intl_CallNumberFormatMethodIfWrapped,
    992      thisArg,
    993      "$Intl_NumberFormat_format_get"
    994    );
    995  }
    996 
    997  var internals = getNumberFormatInternals(nf);
    998 
    999  // Step 4.
   1000  if (internals.boundFormat === undefined) {
   1001    // Steps 4.a-c.
   1002    internals.boundFormat = createNumberFormatFormat(nf);
   1003  }
   1004 
   1005  // Step 5.
   1006  return internals.boundFormat;
   1007 }
   1008 SetCanonicalName($Intl_NumberFormat_format_get, "get format");
   1009 
   1010 /**
   1011 * 15.3.4 Intl.NumberFormat.prototype.formatToParts ( value )
   1012 *
   1013 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
   1014 */
   1015 function Intl_NumberFormat_formatToParts(value) {
   1016  // Step 1.
   1017  var nf = this;
   1018 
   1019  // Step 2.
   1020  if (!IsObject(nf) || (nf = intl_GuardToNumberFormat(nf)) === null) {
   1021    return callFunction(
   1022      intl_CallNumberFormatMethodIfWrapped,
   1023      this,
   1024      value,
   1025      "Intl_NumberFormat_formatToParts"
   1026    );
   1027  }
   1028 
   1029  // Steps 3-4.
   1030  return intl_FormatNumber(nf, value, /* formatToParts = */ true);
   1031 }
   1032 
   1033 /**
   1034 * 15.3.5 Intl.NumberFormat.prototype.formatRange ( start, end )
   1035 *
   1036 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
   1037 */
   1038 function Intl_NumberFormat_formatRange(start, end) {
   1039  // Step 1.
   1040  var nf = this;
   1041 
   1042  // Step 2.
   1043  if (!IsObject(nf) || (nf = intl_GuardToNumberFormat(nf)) === null) {
   1044    return callFunction(
   1045      intl_CallNumberFormatMethodIfWrapped,
   1046      this,
   1047      start,
   1048      end,
   1049      "Intl_NumberFormat_formatRange"
   1050    );
   1051  }
   1052 
   1053  // Step 3.
   1054  if (start === undefined || end === undefined) {
   1055    ThrowTypeError(
   1056      JSMSG_UNDEFINED_NUMBER,
   1057      start === undefined ? "start" : "end",
   1058      "NumberFormat",
   1059      "formatRange"
   1060    );
   1061  }
   1062 
   1063  // Steps 4-6.
   1064  return intl_FormatNumberRange(nf, start, end, /* formatToParts = */ false);
   1065 }
   1066 
   1067 /**
   1068 * 15.3.6 Intl.NumberFormat.prototype.formatRangeToParts ( start, end )
   1069 *
   1070 * ES2024 Intl draft rev 74ca7099f103d143431b2ea422ae640c6f43e3e6
   1071 */
   1072 function Intl_NumberFormat_formatRangeToParts(start, end) {
   1073  // Step 1.
   1074  var nf = this;
   1075 
   1076  // Step 2.
   1077  if (!IsObject(nf) || (nf = intl_GuardToNumberFormat(nf)) === null) {
   1078    return callFunction(
   1079      intl_CallNumberFormatMethodIfWrapped,
   1080      this,
   1081      start,
   1082      end,
   1083      "Intl_NumberFormat_formatRangeToParts"
   1084    );
   1085  }
   1086 
   1087  // Step 3.
   1088  if (start === undefined || end === undefined) {
   1089    ThrowTypeError(
   1090      JSMSG_UNDEFINED_NUMBER,
   1091      start === undefined ? "start" : "end",
   1092      "NumberFormat",
   1093      "formatRangeToParts"
   1094    );
   1095  }
   1096 
   1097  // Steps 4-6.
   1098  return intl_FormatNumberRange(nf, start, end, /* formatToParts = */ true);
   1099 }
   1100 
   1101 /**
   1102 * 15.3.7 Intl.NumberFormat.prototype.resolvedOptions ( )
   1103 *
   1104 * Returns the resolved options for a NumberFormat object.
   1105 *
   1106 * ES2024 Intl draft rev a1db4567870dbe505121a4255f1210338757190a
   1107 */
   1108 function Intl_NumberFormat_resolvedOptions() {
   1109  // Steps 1-3.
   1110  var thisArg = UnwrapNumberFormat(this);
   1111  var nf = thisArg;
   1112  if (!IsObject(nf) || (nf = intl_GuardToNumberFormat(nf)) === null) {
   1113    return callFunction(
   1114      intl_CallNumberFormatMethodIfWrapped,
   1115      thisArg,
   1116      "Intl_NumberFormat_resolvedOptions"
   1117    );
   1118  }
   1119 
   1120  var internals = getNumberFormatInternals(nf);
   1121 
   1122  // Steps 4-5.
   1123  var result = {
   1124    locale: internals.locale,
   1125    numberingSystem: internals.numberingSystem,
   1126    style: internals.style,
   1127  };
   1128 
   1129  // currency, currencyDisplay, and currencySign are only present for currency
   1130  // formatters.
   1131  assert(
   1132    hasOwn("currency", internals) === (internals.style === "currency"),
   1133    "currency is present iff style is 'currency'"
   1134  );
   1135  assert(
   1136    hasOwn("currencyDisplay", internals) === (internals.style === "currency"),
   1137    "currencyDisplay is present iff style is 'currency'"
   1138  );
   1139  assert(
   1140    hasOwn("currencySign", internals) === (internals.style === "currency"),
   1141    "currencySign is present iff style is 'currency'"
   1142  );
   1143 
   1144  if (hasOwn("currency", internals)) {
   1145    DefineDataProperty(result, "currency", internals.currency);
   1146    DefineDataProperty(result, "currencyDisplay", internals.currencyDisplay);
   1147    DefineDataProperty(result, "currencySign", internals.currencySign);
   1148  }
   1149 
   1150  // unit and unitDisplay are only present for unit formatters.
   1151  assert(
   1152    hasOwn("unit", internals) === (internals.style === "unit"),
   1153    "unit is present iff style is 'unit'"
   1154  );
   1155  assert(
   1156    hasOwn("unitDisplay", internals) === (internals.style === "unit"),
   1157    "unitDisplay is present iff style is 'unit'"
   1158  );
   1159 
   1160  if (hasOwn("unit", internals)) {
   1161    DefineDataProperty(result, "unit", internals.unit);
   1162    DefineDataProperty(result, "unitDisplay", internals.unitDisplay);
   1163  }
   1164 
   1165  DefineDataProperty(
   1166    result,
   1167    "minimumIntegerDigits",
   1168    internals.minimumIntegerDigits
   1169  );
   1170 
   1171  // Min/Max fraction digits are either both present or not present at all.
   1172  assert(
   1173    hasOwn("minimumFractionDigits", internals) ===
   1174      hasOwn("maximumFractionDigits", internals),
   1175    "minimumFractionDigits is present iff maximumFractionDigits is present"
   1176  );
   1177 
   1178  if (hasOwn("minimumFractionDigits", internals)) {
   1179    DefineDataProperty(
   1180      result,
   1181      "minimumFractionDigits",
   1182      internals.minimumFractionDigits
   1183    );
   1184    DefineDataProperty(
   1185      result,
   1186      "maximumFractionDigits",
   1187      internals.maximumFractionDigits
   1188    );
   1189  }
   1190 
   1191  // Min/Max significant digits are either both present or not present at all.
   1192  assert(
   1193    hasOwn("minimumSignificantDigits", internals) ===
   1194      hasOwn("maximumSignificantDigits", internals),
   1195    "minimumSignificantDigits is present iff maximumSignificantDigits is present"
   1196  );
   1197 
   1198  if (hasOwn("minimumSignificantDigits", internals)) {
   1199    DefineDataProperty(
   1200      result,
   1201      "minimumSignificantDigits",
   1202      internals.minimumSignificantDigits
   1203    );
   1204    DefineDataProperty(
   1205      result,
   1206      "maximumSignificantDigits",
   1207      internals.maximumSignificantDigits
   1208    );
   1209  }
   1210 
   1211  DefineDataProperty(result, "useGrouping", internals.useGrouping);
   1212 
   1213  var notation = internals.notation;
   1214  DefineDataProperty(result, "notation", notation);
   1215 
   1216  // compactDisplay is only present when `notation` is "compact".
   1217  if (notation === "compact") {
   1218    DefineDataProperty(result, "compactDisplay", internals.compactDisplay);
   1219  }
   1220 
   1221  DefineDataProperty(result, "signDisplay", internals.signDisplay);
   1222  DefineDataProperty(result, "roundingIncrement", internals.roundingIncrement);
   1223  DefineDataProperty(result, "roundingMode", internals.roundingMode);
   1224  DefineDataProperty(result, "roundingPriority", internals.roundingPriority);
   1225  DefineDataProperty(
   1226    result,
   1227    "trailingZeroDisplay",
   1228    internals.trailingZeroDisplay
   1229  );
   1230 
   1231  // Step 6.
   1232  return result;
   1233 }