v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
js-number-format.cc
Go to the documentation of this file.
1// Copyright 2018 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_INTL_SUPPORT
6#error Internationalization is expected to be enabled.
7#endif // V8_INTL_SUPPORT
8
10
11#include <set>
12#include <string>
13
22#pragma GCC diagnostic push
23#pragma GCC diagnostic ignored "-Wshadow"
24#include "unicode/currunit.h"
25#include "unicode/locid.h"
26#include "unicode/numberformatter.h"
27#include "unicode/numberrangeformatter.h"
28#include "unicode/numsys.h"
29#include "unicode/ucurr.h"
30#include "unicode/uloc.h"
31#include "unicode/unumberformatter.h"
32#include "unicode/uvernum.h" // for U_ICU_VERSION_MAJOR_NUM
33#pragma GCC diagnostic pop
34
35namespace v8 {
36namespace internal {
37
38namespace {
39
40// This is to work around ICU's comparison operators not being compliant with
41// clang's -Wambiguous-reversed-operator in >=C++20.
42#define AVOID_AMBIGUOUS_OP_WARNING(x) *static_cast<icu::UObject*>(&x)
43
44// [[Style]] is one of the values "decimal", "percent", "currency",
45// or "unit" identifying the style of the number format.
46enum class Style { DECIMAL, PERCENT, CURRENCY, UNIT };
47
48// [[CurrencyDisplay]] is one of the values "code", "symbol", "name",
49// or "narrowSymbol" identifying the display of the currency number format.
50enum class CurrencyDisplay {
51 CODE,
52 SYMBOL,
53 NAME,
54 NARROW_SYMBOL,
55};
56
57// [[CurrencySign]] is one of the String values "standard" or "accounting",
58// specifying whether to render negative numbers in accounting format, often
59// signified by parenthesis. It is only used when [[Style]] has the value
60// "currency" and when [[SignDisplay]] is not "never".
61enum class CurrencySign {
62 STANDARD,
63 ACCOUNTING,
64};
65
66// [[UnitDisplay]] is one of the String values "short", "narrow", or "long",
67// specifying whether to display the unit as a symbol, narrow symbol, or
68// localized long name if formatting with the "unit" style. It is
69// only used when [[Style]] has the value "unit".
70enum class UnitDisplay {
71 SHORT,
72 NARROW,
73 LONG,
74};
75
76// [[Notation]] is one of the String values "standard", "scientific",
77// "engineering", or "compact", specifying whether the number should be
78// displayed without scaling, scaled to the units place with the power of ten
79// in scientific notation, scaled to the nearest thousand with the power of
80// ten in scientific notation, or scaled to the nearest locale-dependent
81// compact decimal notation power of ten with the corresponding compact
82// decimal notation affix.
83
84enum class Notation {
85 STANDARD,
86 SCIENTIFIC,
87 ENGINEERING,
88 COMPACT,
89};
90
91// [[CompactDisplay]] is one of the String values "short" or "long",
92// specifying whether to display compact notation affixes in short form ("5K")
93// or long form ("5 thousand") if formatting with the "compact" notation. It
94// is only used when [[Notation]] has the value "compact".
95enum class CompactDisplay {
96 SHORT,
97 LONG,
98};
99
100// [[SignDisplay]] is one of the String values "auto", "always", "never", or
101// "exceptZero", specifying whether to show the sign on negative numbers
102// only, positive and negative numbers including zero, neither positive nor
103// negative numbers, or positive and negative numbers but not zero.
104enum class SignDisplay {
105 AUTO,
106 ALWAYS,
107 NEVER,
108 EXCEPT_ZERO,
109 NEGATIVE,
110};
111
112// [[UseGrouping]] is ....
113enum class UseGrouping {
114 OFF,
115 MIN2,
116 AUTO,
117 ALWAYS,
118};
119
120UNumberUnitWidth ToUNumberUnitWidth(CurrencyDisplay currency_display) {
121 switch (currency_display) {
122 case CurrencyDisplay::SYMBOL:
123 return UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT;
124 case CurrencyDisplay::CODE:
125 return UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE;
126 case CurrencyDisplay::NAME:
127 return UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME;
128 case CurrencyDisplay::NARROW_SYMBOL:
129 return UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW;
130 }
131}
132
133UNumberUnitWidth ToUNumberUnitWidth(UnitDisplay unit_display) {
134 switch (unit_display) {
135 case UnitDisplay::SHORT:
136 return UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT;
137 case UnitDisplay::LONG:
138 return UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME;
139 case UnitDisplay::NARROW:
140 return UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW;
141 }
142}
143
144UNumberSignDisplay ToUNumberSignDisplay(SignDisplay sign_display,
145 CurrencySign currency_sign) {
146 switch (sign_display) {
147 case SignDisplay::AUTO:
148 if (currency_sign == CurrencySign::ACCOUNTING) {
149 return UNumberSignDisplay::UNUM_SIGN_ACCOUNTING;
150 }
151 DCHECK(currency_sign == CurrencySign::STANDARD);
152 return UNumberSignDisplay::UNUM_SIGN_AUTO;
153 case SignDisplay::NEVER:
154 return UNumberSignDisplay::UNUM_SIGN_NEVER;
155 case SignDisplay::ALWAYS:
156 if (currency_sign == CurrencySign::ACCOUNTING) {
157 return UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS;
158 }
159 DCHECK(currency_sign == CurrencySign::STANDARD);
160 return UNumberSignDisplay::UNUM_SIGN_ALWAYS;
161 case SignDisplay::EXCEPT_ZERO:
162 if (currency_sign == CurrencySign::ACCOUNTING) {
163 return UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
164 }
165 DCHECK(currency_sign == CurrencySign::STANDARD);
166 return UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO;
167 case SignDisplay::NEGATIVE:
168 if (currency_sign == CurrencySign::ACCOUNTING) {
169 return UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_NEGATIVE;
170 }
171 DCHECK(currency_sign == CurrencySign::STANDARD);
172 return UNumberSignDisplay::UNUM_SIGN_NEGATIVE;
173 }
174}
175
176icu::number::Notation ToICUNotation(Notation notation,
177 CompactDisplay compact_display) {
178 switch (notation) {
179 case Notation::STANDARD:
180 return icu::number::Notation::simple();
181 case Notation::SCIENTIFIC:
182 return icu::number::Notation::scientific();
183 case Notation::ENGINEERING:
184 return icu::number::Notation::engineering();
185 // 29. If notation is "compact", then
186 case Notation::COMPACT:
187 // 29. a. Set numberFormat.[[CompactDisplay]] to compactDisplay.
188 if (compact_display == CompactDisplay::SHORT) {
189 return icu::number::Notation::compactShort();
190 }
191 DCHECK(compact_display == CompactDisplay::LONG);
192 return icu::number::Notation::compactLong();
193 }
194}
195
196UNumberFormatRoundingMode ToUNumberFormatRoundingMode(
198 switch (rounding_mode) {
200 return UNumberFormatRoundingMode::UNUM_ROUND_CEILING;
202 return UNumberFormatRoundingMode::UNUM_ROUND_FLOOR;
204 return UNumberFormatRoundingMode::UNUM_ROUND_UP;
206 return UNumberFormatRoundingMode::UNUM_ROUND_DOWN;
208 return UNumberFormatRoundingMode::UNUM_ROUND_HALF_CEILING;
210 return UNumberFormatRoundingMode::UNUM_ROUND_HALF_FLOOR;
212 return UNumberFormatRoundingMode::UNUM_ROUND_HALFUP;
214 return UNumberFormatRoundingMode::UNUM_ROUND_HALFDOWN;
216 return UNumberFormatRoundingMode::UNUM_ROUND_HALFEVEN;
217 }
218}
219
220UNumberGroupingStrategy ToUNumberGroupingStrategy(UseGrouping use_grouping) {
221 switch (use_grouping) {
222 case UseGrouping::OFF:
223 return UNumberGroupingStrategy::UNUM_GROUPING_OFF;
224 case UseGrouping::MIN2:
225 return UNumberGroupingStrategy::UNUM_GROUPING_MIN2;
226 case UseGrouping::AUTO:
227 return UNumberGroupingStrategy::UNUM_GROUPING_AUTO;
228 case UseGrouping::ALWAYS:
229 return UNumberGroupingStrategy::UNUM_GROUPING_ON_ALIGNED;
230 }
231}
232
233std::map<const std::string, icu::MeasureUnit> CreateUnitMap() {
234 UErrorCode status = U_ZERO_ERROR;
235 int32_t total = icu::MeasureUnit::getAvailable(nullptr, 0, status);
236 DCHECK(U_FAILURE(status));
237 status = U_ZERO_ERROR;
238 std::vector<icu::MeasureUnit> units(total);
239 total = icu::MeasureUnit::getAvailable(units.data(), total, status);
240 DCHECK(U_SUCCESS(status));
241 std::map<const std::string, icu::MeasureUnit> map;
242 std::set<std::string> sanctioned(Intl::SanctionedSimpleUnits());
243 for (auto it = units.begin(); it != units.end(); ++it) {
244 // Need to skip none/percent
245 if (sanctioned.count(it->getSubtype()) > 0 &&
246 strcmp("none", it->getType()) != 0) {
247 map[it->getSubtype()] = *it;
248 }
249 }
250 return map;
251}
252
253class UnitFactory {
254 public:
255 UnitFactory() : map_(CreateUnitMap()) {}
256 virtual ~UnitFactory() = default;
257
258 // ecma402 #sec-issanctionedsimpleunitidentifier
259 icu::MeasureUnit create(const std::string& unitIdentifier) {
260 // 1. If unitIdentifier is in the following list, return true.
261 auto found = map_.find(unitIdentifier);
262 if (found != map_.end()) {
263 return found->second;
264 }
265 // 2. Return false.
266 return icu::MeasureUnit();
267 }
268
269 private:
270 std::map<const std::string, icu::MeasureUnit> map_;
271};
272
273// ecma402 #sec-issanctionedsimpleunitidentifier
274icu::MeasureUnit IsSanctionedUnitIdentifier(const std::string& unit) {
277 return factory.Pointer()->create(unit);
278}
279
280// ecma402 #sec-iswellformedunitidentifier
281Maybe<std::pair<icu::MeasureUnit, icu::MeasureUnit>> IsWellFormedUnitIdentifier(
282 Isolate* isolate, const std::string& unit) {
283 icu::MeasureUnit result = IsSanctionedUnitIdentifier(unit);
284 icu::MeasureUnit none = icu::MeasureUnit();
285 // 1. If the result of IsSanctionedUnitIdentifier(unitIdentifier) is true,
286 // then
288 // a. Return true.
289 std::pair<icu::MeasureUnit, icu::MeasureUnit> pair(result, none);
290 return Just(pair);
291 }
292 // 2. If the substring "-per-" does not occur exactly once in unitIdentifier,
293 // then
294 size_t first_per = unit.find("-per-");
295 if (first_per == std::string::npos ||
296 unit.find("-per-", first_per + 5) != std::string::npos) {
297 // a. Return false.
299 }
300 // 3. Let numerator be the substring of unitIdentifier from the beginning to
301 // just before "-per-".
302 std::string numerator = unit.substr(0, first_per);
303
304 // 4. If the result of IsSanctionedUnitIdentifier(numerator) is false, then
305 result = IsSanctionedUnitIdentifier(numerator);
307 // a. Return false.
309 }
310 // 5. Let denominator be the substring of unitIdentifier from just after
311 // "-per-" to the end.
312 std::string denominator = unit.substr(first_per + 5);
313
314 // 6. If the result of IsSanctionedUnitIdentifier(denominator) is false, then
315 icu::MeasureUnit den_result = IsSanctionedUnitIdentifier(denominator);
316 if (den_result == AVOID_AMBIGUOUS_OP_WARNING(none)) {
317 // a. Return false.
319 }
320 // 7. Return true.
321 std::pair<icu::MeasureUnit, icu::MeasureUnit> pair(result, den_result);
322 return Just(pair);
323}
324
325// ecma-402/#sec-currencydigits
326// The currency is expected to an all upper case string value.
327int CurrencyDigits(const icu::UnicodeString& currency) {
328 UErrorCode status = U_ZERO_ERROR;
329 uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
330 reinterpret_cast<const UChar*>(currency.getBuffer()), &status);
331 // For missing currency codes, default to the most common, 2
332 return U_SUCCESS(status) ? fraction_digits : 2;
333}
334
335bool IsAToZ(char ch) {
336 return base::IsInRange(AsciiAlphaToLower(ch), 'a', 'z');
337}
338
339// ecma402/#sec-iswellformedcurrencycode
340bool IsWellFormedCurrencyCode(const std::string& currency) {
341 // Verifies that the input is a well-formed ISO 4217 currency code.
342 // ecma402/#sec-currency-codes
343 // 2. If the number of elements in normalized is not 3, return false.
344 if (currency.length() != 3) return false;
345 // 1. Let normalized be the result of mapping currency to upper case as
346 // described in 6.1.
347 //
348 // 3. If normalized contains any character that is not in
349 // the range "A" to "Z" (U+0041 to U+005A), return false.
350 //
351 // 4. Return true.
352 // Don't uppercase to test. It could convert invalid code into a valid one.
353 // For example \u00DFP (Eszett+P) becomes SSP.
354 return (IsAToZ(currency[0]) && IsAToZ(currency[1]) && IsAToZ(currency[2]));
355}
356
357// Return the style as a String.
358DirectHandle<String> StyleAsString(Isolate* isolate, Style style) {
359 switch (style) {
360 case Style::PERCENT:
361 return isolate->factory()->percent_string();
362 case Style::CURRENCY:
363 return isolate->factory()->currency_string();
364 case Style::UNIT:
365 return isolate->factory()->unit_string();
366 case Style::DECIMAL:
367 return isolate->factory()->decimal_string();
368 }
369 UNREACHABLE();
370}
371
372// Parse the 'currencyDisplay' from the skeleton.
373DirectHandle<String> CurrencyDisplayString(Isolate* isolate,
374 const icu::UnicodeString& skeleton) {
375 // Ex: skeleton as
376 // "currency/TWD .00 rounding-mode-half-up unit-width-iso-code"
377 if (skeleton.indexOf("unit-width-iso-code") >= 0) {
378 return isolate->factory()->code_string();
379 }
380 // Ex: skeleton as
381 // "currency/TWD .00 rounding-mode-half-up unit-width-full-name;"
382 if (skeleton.indexOf("unit-width-full-name") >= 0) {
383 return isolate->factory()->name_string();
384 }
385 // Ex: skeleton as
386 // "currency/TWD .00 rounding-mode-half-up unit-width-narrow;
387 if (skeleton.indexOf("unit-width-narrow") >= 0) {
388 return isolate->factory()->narrowSymbol_string();
389 }
390 // Ex: skeleton as "currency/TWD .00 rounding-mode-half-up"
391 return isolate->factory()->symbol_string();
392}
393
394DirectHandle<Object> UseGroupingFromSkeleton(
395 Isolate* isolate, const icu::UnicodeString& skeleton) {
396 Factory* factory = isolate->factory();
397 static const char* group = "group-";
398 int32_t start = skeleton.indexOf(group);
399 if (start >= 0) {
400 DCHECK_EQ(6, strlen(group));
401 icu::UnicodeString check = skeleton.tempSubString(start + 6);
402 // Ex: skeleton as
403 // .### rounding-mode-half-up group-off
404 if (check.startsWith("off")) {
405 return factory->false_value();
406 }
407 // Ex: skeleton as
408 // .### rounding-mode-half-up group-min2
409 if (check.startsWith("min2")) {
410 return isolate->factory()->min2_string();
411 }
412 // Ex: skeleton as
413 // .### rounding-mode-half-up group-on-aligned
414 if (check.startsWith("on-aligned")) {
415 return isolate->factory()->always_string();
416 }
417 }
418 // Ex: skeleton as
419 // .###
420 return isolate->factory()->auto_string();
421}
422
423// Parse currency code from skeleton. For example, skeleton as
424// "currency/TWD .00 rounding-mode-half-up unit-width-full-name;"
425const icu::UnicodeString CurrencyFromSkeleton(
426 const icu::UnicodeString& skeleton) {
427 const char currency[] = "currency/";
428 int32_t index = skeleton.indexOf(currency);
429 if (index < 0) return "";
430 index += static_cast<int32_t>(std::strlen(currency));
431 return skeleton.tempSubString(index, 3);
432}
433
434} // namespace
436 const icu::UnicodeString& skeleton) {
437 const char numbering_system[] = "numbering-system/";
438 int32_t index = skeleton.indexOf(numbering_system);
439 if (index < 0) return "latn";
440 index += static_cast<int32_t>(std::strlen(numbering_system));
441 const icu::UnicodeString res = skeleton.tempSubString(index);
442 index = res.indexOf(" ");
443 if (index < 0) return res;
444 return res.tempSubString(0, index);
445}
446
447namespace {
448
449// Return CurrencySign as string based on skeleton.
450DirectHandle<String> CurrencySignString(Isolate* isolate,
451 const icu::UnicodeString& skeleton) {
452 // Ex: skeleton as
453 // "currency/TWD .00 rounding-mode-half-up sign-accounting-always" OR
454 // "currency/TWD .00 rounding-mode-half-up sign-accounting-except-zero"
455 if (skeleton.indexOf("sign-accounting") >= 0) {
456 return isolate->factory()->accounting_string();
457 }
458 return isolate->factory()->standard_string();
459}
460
461// Return UnitDisplay as string based on skeleton.
462DirectHandle<String> UnitDisplayString(Isolate* isolate,
463 const icu::UnicodeString& skeleton) {
464 // Ex: skeleton as
465 // "unit/length-meter .### rounding-mode-half-up unit-width-full-name"
466 if (skeleton.indexOf("unit-width-full-name") >= 0) {
467 return isolate->factory()->long_string();
468 }
469 // Ex: skeleton as
470 // "unit/length-meter .### rounding-mode-half-up unit-width-narrow".
471 if (skeleton.indexOf("unit-width-narrow") >= 0) {
472 return isolate->factory()->narrow_string();
473 }
474 // Ex: skeleton as
475 // "unit/length-foot .### rounding-mode-half-up"
476 return isolate->factory()->short_string();
477}
478
479// Parse Notation from skeleton.
480Notation NotationFromSkeleton(const icu::UnicodeString& skeleton) {
481 // Ex: skeleton as
482 // "scientific .### rounding-mode-half-up"
483 if (skeleton.indexOf("scientific") >= 0) {
484 return Notation::SCIENTIFIC;
485 }
486 // Ex: skeleton as
487 // "engineering .### rounding-mode-half-up"
488 if (skeleton.indexOf("engineering") >= 0) {
489 return Notation::ENGINEERING;
490 }
491 // Ex: skeleton as
492 // "compact-short .### rounding-mode-half-up" or
493 // "compact-long .### rounding-mode-half-up
494 if (skeleton.indexOf("compact-") >= 0) {
495 return Notation::COMPACT;
496 }
497 // Ex: skeleton as
498 // "unit/length-foot .### rounding-mode-half-up"
499 return Notation::STANDARD;
500}
501
502DirectHandle<String> NotationAsString(Isolate* isolate, Notation notation) {
503 switch (notation) {
504 case Notation::SCIENTIFIC:
505 return isolate->factory()->scientific_string();
506 case Notation::ENGINEERING:
507 return isolate->factory()->engineering_string();
508 case Notation::COMPACT:
509 return isolate->factory()->compact_string();
510 case Notation::STANDARD:
511 return isolate->factory()->standard_string();
512 }
513 UNREACHABLE();
514}
515
516// Return CompactString as string based on skeleton.
517DirectHandle<String> CompactDisplayString(Isolate* isolate,
518 const icu::UnicodeString& skeleton) {
519 // Ex: skeleton as
520 // "compact-long .### rounding-mode-half-up"
521 if (skeleton.indexOf("compact-long") >= 0) {
522 return isolate->factory()->long_string();
523 }
524 // Ex: skeleton as
525 // "compact-short .### rounding-mode-half-up"
526 DCHECK_GE(skeleton.indexOf("compact-short"), 0);
527 return isolate->factory()->short_string();
528}
529
530// Return SignDisplay as string based on skeleton.
531DirectHandle<String> SignDisplayString(Isolate* isolate,
532 const icu::UnicodeString& skeleton) {
533 // Ex: skeleton as
534 // "currency/TWD .00 rounding-mode-half-up sign-never"
535 if (skeleton.indexOf("sign-never") >= 0) {
536 return isolate->factory()->never_string();
537 }
538 // Ex: skeleton as
539 // ".### rounding-mode-half-up sign-always" or
540 // "currency/TWD .00 rounding-mode-half-up sign-accounting-always"
541 if (skeleton.indexOf("sign-always") >= 0 ||
542 skeleton.indexOf("sign-accounting-always") >= 0) {
543 return isolate->factory()->always_string();
544 }
545 // Ex: skeleton as
546 // "currency/TWD .00 rounding-mode-half-up sign-accounting-except-zero" or
547 // "currency/TWD .00 rounding-mode-half-up sign-except-zero"
548 if (skeleton.indexOf("sign-accounting-except-zero") >= 0 ||
549 skeleton.indexOf("sign-except-zero") >= 0) {
550 return isolate->factory()->exceptZero_string();
551 }
552 // Ex: skeleton as
553 // ".### rounding-mode-half-up sign-negative" or
554 // "currency/TWD .00 rounding-mode-half-up sign-accounting-negative"
555 if (skeleton.indexOf("sign-accounting-negative") >= 0 ||
556 skeleton.indexOf("sign-negative") >= 0) {
557 return isolate->factory()->negative_string();
558 }
559 return isolate->factory()->auto_string();
560}
561
562} // anonymous namespace
563
564// Return RoundingMode as string based on skeleton.
566 Isolate* isolate, const icu::UnicodeString& skeleton) {
567 static const char* rounding_mode = "rounding-mode-";
568 int32_t start = skeleton.indexOf(rounding_mode);
569 if (start >= 0) {
570 DCHECK_EQ(14, strlen(rounding_mode));
571 icu::UnicodeString check = skeleton.tempSubString(start + 14);
572
573 // Ex: skeleton as
574 // .### rounding-mode-ceiling
575 if (check.startsWith("ceiling")) {
576 return isolate->factory()->ceil_string();
577 }
578 // Ex: skeleton as
579 // .### rounding-mode-down
580 if (check.startsWith("down")) {
581 return isolate->factory()->trunc_string();
582 }
583 // Ex: skeleton as
584 // .### rounding-mode-floor
585 if (check.startsWith("floor")) {
586 return isolate->factory()->floor_string();
587 }
588 // Ex: skeleton as
589 // .### rounding-mode-half-ceiling
590 if (check.startsWith("half-ceiling")) {
591 return isolate->factory()->halfCeil_string();
592 }
593 // Ex: skeleton as
594 // .### rounding-mode-half-down
595 if (check.startsWith("half-down")) {
596 return isolate->factory()->halfTrunc_string();
597 }
598 // Ex: skeleton as
599 // .### rounding-mode-half-floor
600 if (check.startsWith("half-floor")) {
601 return isolate->factory()->halfFloor_string();
602 }
603 // Ex: skeleton as
604 // .### rounding-mode-half-up
605 if (check.startsWith("half-up")) {
606 return isolate->factory()->halfExpand_string();
607 }
608 // Ex: skeleton as
609 // .### rounding-mode-up
610 if (check.startsWith("up")) {
611 return isolate->factory()->expand_string();
612 }
613 }
614 // Ex: skeleton as
615 // .###
616 return isolate->factory()->halfEven_string();
617}
618
620 Isolate* isolate, const icu::UnicodeString& skeleton) {
621 int32_t cur = skeleton.indexOf(u"precision-increment/");
622 if (cur < 0) return isolate->factory()->NewNumberFromInt(1);
623 cur += 20; // length of "precision-increment/"
624 int32_t increment = 0;
625 while (cur < skeleton.length()) {
626 char16_t c = skeleton[cur++];
627 if (c == u'.') continue;
628 if (!IsDecimalDigit(c)) break;
629 increment = increment * 10 + (c - '0');
630 }
631 return isolate->factory()->NewNumberFromInt(increment);
632}
633
634// Return RoundingPriority as string based on skeleton.
636 Isolate* isolate, const icu::UnicodeString& skeleton) {
637 int32_t found;
638 // If #r or @r is followed by a SPACE or in the end of line.
639 if ((found = skeleton.indexOf("#r")) >= 0 ||
640 (found = skeleton.indexOf("@r")) >= 0) {
641 if (found + 2 == skeleton.length() || skeleton[found + 2] == ' ') {
642 return isolate->factory()->morePrecision_string();
643 }
644 }
645 // If #s or @s is followed by a SPACE or in the end of line.
646 if ((found = skeleton.indexOf("#s")) >= 0 ||
647 (found = skeleton.indexOf("@s")) >= 0) {
648 if (found + 2 == skeleton.length() || skeleton[found + 2] == ' ') {
649 return isolate->factory()->lessPrecision_string();
650 }
651 }
652 return isolate->factory()->auto_string();
653}
654
655// Return trailingZeroDisplay as string based on skeleton.
657 Isolate* isolate, const icu::UnicodeString& skeleton) {
658 int32_t found;
659 if ((found = skeleton.indexOf("/w")) >= 0) {
660 if (found + 2 == skeleton.length() || skeleton[found + 2] == ' ') {
661 return isolate->factory()->stripIfInteger_string();
662 }
663 }
664 return isolate->factory()->auto_string();
665}
666
667// Return the minimum integer digits by counting the number of '0' after
668// "integer-width/*" in the skeleton.
669// Ex: Return 15 for skeleton as
670// “currency/TWD .00 rounding-mode-half-up integer-width/*000000000000000”
671// 1
672// 123456789012345
673// Return default value as 1 if there are no "integer-width/*".
675 const icu::UnicodeString& skeleton) {
676 // count the number of 0 after "integer-width/*"
677 icu::UnicodeString search("integer-width/*");
678 int32_t index = skeleton.indexOf(search);
679 if (index < 0) return 1; // return 1 if cannot find it.
680 index += search.length();
681 int32_t matched = 0;
682 while (index < skeleton.length() && skeleton[index] == '0') {
683 matched++;
684 index++;
685 }
686 DCHECK_GT(matched, 0);
687 return matched;
688}
689
690// Return true if there are fraction digits, false if not.
691// The minimum fraction digits is the number of '0' after '.' in the skeleton
692// The maximum fraction digits is the number of '#' after the above '0's plus
693// the minimum fraction digits.
694// For example, as skeleton “.000#### rounding-mode-half-up”
695// 123
696// 4567
697// Set The minimum as 3 and maximum as 7.
698// We also treat the following special cases as both minimum and maximum are 0
699// while there are no . in the skeleton:
700// 1. While there are "precision-integer" in the skeleton.
701// 2. While there are "precision-increment/" in the skeleton but no . after it.
702// Examples:
703// "currency/JPY precision-integer rounding-mode-half-up"
704// "precision-increment/2 rounding-mode-half-up"
706 const icu::UnicodeString& skeleton, int32_t* minimum, int32_t* maximum) {
707 int32_t index = skeleton.indexOf(".");
708 if (index < 0) {
709 // https://unicode-org.github.io/icu/userguide/format_parse/numbers/skeletons.html#precision
710 // Note that the stem . is considered valid and is equivalent to
711 // precision-integer.
712 // Also, if there are "precision-increment/" but no "." we consider both
713 // minimum and maximum fraction digits as 0.
714 if (skeleton.indexOf("precision-integer") >= 0 ||
715 skeleton.indexOf("precision-increment/") >= 0) {
716 *minimum = *maximum = 0;
717 return true;
718 }
719 return false;
720 }
721 *minimum = 0;
722 index++; // skip the '.'
723 while (index < skeleton.length() && IsDecimalDigit(skeleton[index])) {
724 (*minimum)++;
725 index++;
726 }
727 *maximum = *minimum;
728 while (index < skeleton.length() && skeleton[index] == '#') {
729 (*maximum)++;
730 index++;
731 }
732 return true;
733}
734
735// Return true if there are significant digits, false if not.
736// The minimum significant digits is the number of '@' in the skeleton
737// The maximum significant digits is the number of '#' after these '@'s plus
738// the minimum significant digits.
739// Ex: Skeleton as "@@@@@####### rounding-mode-half-up"
740// 12345
741// 6789012
742// Set The minimum as 5 and maximum as 12.
744 const icu::UnicodeString& skeleton, int32_t* minimum, int32_t* maximum) {
745 int32_t index = skeleton.indexOf("@");
746 if (index < 0) return false;
747 *minimum = 1;
748 index++; // skip the first '@'
749 while (index < skeleton.length() && skeleton[index] == '@') {
750 (*minimum)++;
751 index++;
752 }
753 *maximum = *minimum;
754 while (index < skeleton.length() && skeleton[index] == '#') {
755 (*maximum)++;
756 index++;
757 }
758 return true;
759}
760
761namespace {
762
763// Ex: percent .### rounding-mode-half-up
764// Special case for "percent"
765// Ex: "unit/milliliter-per-acre .### rounding-mode-half-up"
766// should return "milliliter-per-acre".
767// Ex: "unit/year .### rounding-mode-half-up" should return
768// "year".
769std::string UnitFromSkeleton(const icu::UnicodeString& skeleton) {
770 std::string str;
771 str = skeleton.toUTF8String<std::string>(str);
772 std::string search("unit/");
773 size_t begin = str.find(search);
774 if (begin == str.npos) {
775 // Special case for "percent".
776 if (str.find("percent") != str.npos) {
777 return "percent";
778 }
779 return "";
780 }
781 // Ex:
782 // "unit/acre .### rounding-mode-half-up"
783 // b
784 // Ex:
785 // "unit/milliliter-per-acre .### rounding-mode-half-up"
786 // b
787 begin += search.size();
788 if (begin == str.npos) {
789 return "";
790 }
791 // Find the end of the subtype.
792 size_t end = str.find(' ', begin);
793 // Ex:
794 // "unit/acre .### rounding-mode-half-up"
795 // b e
796 // Ex:
797 // "unit/milliliter-per-acre .### rounding-mode-half-up"
798 // b e
799 if (end == str.npos) {
800 end = str.size();
801 }
802 return str.substr(begin, end - begin);
803}
804
805Style StyleFromSkeleton(const icu::UnicodeString& skeleton) {
806 if (skeleton.indexOf("currency/") >= 0) {
807 return Style::CURRENCY;
808 }
809 if (skeleton.indexOf("percent") >= 0) {
810 // percent precision-integer rounding-mode-half-up scale/100
811 if (skeleton.indexOf("scale/100") >= 0) {
812 return Style::PERCENT;
813 } else {
814 return Style::UNIT;
815 }
816 }
817 // Before ICU68: "measure-unit/", since ICU68 "unit/"
818 if (skeleton.indexOf("unit/") >= 0) {
819 return Style::UNIT;
820 }
821 return Style::DECIMAL;
822}
823
824} // anonymous namespace
825
826icu::number::UnlocalizedNumberFormatter
828 const icu::number::UnlocalizedNumberFormatter& settings,
829 const Intl::NumberFormatDigitOptions& digit_options) {
830 icu::number::UnlocalizedNumberFormatter result = settings.roundingMode(
831 ToUNumberFormatRoundingMode(digit_options.rounding_mode));
832
833 if (digit_options.minimum_integer_digits > 1) {
834 result = result.integerWidth(icu::number::IntegerWidth::zeroFillTo(
835 digit_options.minimum_integer_digits));
836 }
837
838 icu::number::Precision precision = icu::number::Precision::unlimited();
839 bool relaxed = false;
840 switch (digit_options.rounding_type) {
842 precision = icu::number::Precision::minMaxSignificantDigits(
843 digit_options.minimum_significant_digits,
844 digit_options.maximum_significant_digits);
845 break;
847 precision = icu::number::Precision::minMaxFraction(
848 digit_options.minimum_fraction_digits,
849 digit_options.maximum_fraction_digits);
850 break;
852 relaxed = true;
853 [[fallthrough]];
855 precision =
856 icu::number::Precision::minMaxFraction(
857 digit_options.minimum_fraction_digits,
858 digit_options.maximum_fraction_digits)
859 .withSignificantDigits(digit_options.minimum_significant_digits,
860 digit_options.maximum_significant_digits,
861 relaxed ? UNUM_ROUNDING_PRIORITY_RELAXED
862 : UNUM_ROUNDING_PRIORITY_STRICT);
863 break;
864 }
865 if (digit_options.rounding_increment != 1) {
866 precision = ::icu::number::Precision::incrementExact(
867 digit_options.rounding_increment,
868 -digit_options.maximum_fraction_digits)
869 .withMinFraction(digit_options.minimum_fraction_digits);
870 }
871 if (digit_options.trailing_zero_display ==
873 precision = precision.trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE);
874 }
875 return result.precision(precision);
876}
877
878// static
879// ecma402 #sec-intl.numberformat.prototype.resolvedoptions
881 Isolate* isolate, DirectHandle<JSNumberFormat> number_format) {
882 Factory* factory = isolate->factory();
883
884 UErrorCode status = U_ZERO_ERROR;
885 icu::number::LocalizedNumberFormatter* fmt =
886 number_format->icu_number_formatter()->raw();
887 icu::UnicodeString skeleton = fmt->toSkeleton(status);
888 DCHECK(U_SUCCESS(status));
889
890 // 4. Let options be ! ObjectCreate(%ObjectPrototype%).
891 DirectHandle<JSObject> options =
892 factory->NewJSObject(isolate->object_function());
893
894 DirectHandle<String> locale =
895 DirectHandle<String>(number_format->locale(), isolate);
896 const icu::UnicodeString numberingSystem_ustr =
898 // 5. For each row of Table 4, except the header row, in table order, do
899 // Table 4: Resolved Options of NumberFormat Instances
900 // Internal Slot Property
901 // [[Locale]] "locale"
902 // [[NumberingSystem]] "numberingSystem"
903 // [[Style]] "style"
904 // [[Currency]] "currency"
905 // [[CurrencyDisplay]] "currencyDisplay"
906 // [[CurrencySign]] "currencySign"
907 // [[Unit]] "unit"
908 // [[UnitDisplay]] "unitDisplay"
909 // [[MinimumIntegerDigits]] "minimumIntegerDigits"
910 // [[MinimumFractionDigits]] "minimumFractionDigits"
911 // [[MaximumFractionDigits]] "maximumFractionDigits"
912 // [[MinimumSignificantDigits]] "minimumSignificantDigits"
913 // [[MaximumSignificantDigits]] "maximumSignificantDigits"
914 // [[UseGrouping]] "useGrouping"
915 // [[Notation]] "notation"
916 // [[CompactDisplay]] "compactDisplay"
917 // [[SignDisplay]] "signDisplay"
918 // [[RoundingIncrement]] "roundingIncrement"
919 // [[RoundingMode]] "roundingMode"
920 // [[ComputedRoundingPriority]] "roundingPriority"
921 // [[TrailingZeroDisplay]] "trailingZeroDisplay"
922
923 CHECK(JSReceiver::CreateDataProperty(isolate, options,
924 factory->locale_string(), locale,
926 .FromJust());
927 DirectHandle<String> numberingSystem_string;
928 CHECK(Intl::ToString(isolate, numberingSystem_ustr)
929 .ToHandle(&numberingSystem_string));
930 CHECK(JSReceiver::CreateDataProperty(isolate, options,
931 factory->numberingSystem_string(),
932 numberingSystem_string, Just(kDontThrow))
933 .FromJust());
934 Style style = StyleFromSkeleton(skeleton);
936 isolate, options, factory->style_string(),
937 StyleAsString(isolate, style), Just(kDontThrow))
938 .FromJust());
939 const icu::UnicodeString currency_ustr = CurrencyFromSkeleton(skeleton);
940 if (!currency_ustr.isEmpty()) {
941 DirectHandle<String> currency_string;
942 CHECK(Intl::ToString(isolate, currency_ustr).ToHandle(&currency_string));
943 CHECK(JSReceiver::CreateDataProperty(isolate, options,
944 factory->currency_string(),
945 currency_string, Just(kDontThrow))
946 .FromJust());
947
949 isolate, options, factory->currencyDisplay_string(),
950 CurrencyDisplayString(isolate, skeleton), Just(kDontThrow))
951 .FromJust());
953 isolate, options, factory->currencySign_string(),
954 CurrencySignString(isolate, skeleton), Just(kDontThrow))
955 .FromJust());
956 }
957
958 if (style == Style::UNIT) {
959 std::string unit = UnitFromSkeleton(skeleton);
960 if (!unit.empty()) {
962 isolate, options, factory->unit_string(),
963 isolate->factory()->NewStringFromAsciiChecked(unit.c_str()),
965 .FromJust());
966 }
968 isolate, options, factory->unitDisplay_string(),
969 UnitDisplayString(isolate, skeleton), Just(kDontThrow))
970 .FromJust());
971 }
972
973 CHECK(
975 isolate, options, factory->minimumIntegerDigits_string(),
978 .FromJust());
979
980 int32_t mnsd = 0, mxsd = 0, mnfd = 0, mxfd = 0;
981 if (FractionDigitsFromSkeleton(skeleton, &mnfd, &mxfd)) {
983 isolate, options, factory->minimumFractionDigits_string(),
984 factory->NewNumberFromInt(mnfd), Just(kDontThrow))
985 .FromJust());
987 isolate, options, factory->maximumFractionDigits_string(),
988 factory->NewNumberFromInt(mxfd), Just(kDontThrow))
989 .FromJust());
990 }
991 if (SignificantDigitsFromSkeleton(skeleton, &mnsd, &mxsd)) {
993 isolate, options, factory->minimumSignificantDigits_string(),
994 factory->NewNumberFromInt(mnsd), Just(kDontThrow))
995 .FromJust());
997 isolate, options, factory->maximumSignificantDigits_string(),
998 factory->NewNumberFromInt(mxsd), Just(kDontThrow))
999 .FromJust());
1000 }
1001
1003 isolate, options, factory->useGrouping_string(),
1004 UseGroupingFromSkeleton(isolate, skeleton), Just(kDontThrow))
1005 .FromJust());
1006
1007 Notation notation = NotationFromSkeleton(skeleton);
1009 isolate, options, factory->notation_string(),
1010 NotationAsString(isolate, notation), Just(kDontThrow))
1011 .FromJust());
1012 // Only output compactDisplay when notation is compact.
1013 if (notation == Notation::COMPACT) {
1015 isolate, options, factory->compactDisplay_string(),
1016 CompactDisplayString(isolate, skeleton), Just(kDontThrow))
1017 .FromJust());
1018 }
1020 isolate, options, factory->signDisplay_string(),
1021 SignDisplayString(isolate, skeleton), Just(kDontThrow))
1022 .FromJust());
1024 isolate, options, factory->roundingIncrement_string(),
1025 RoundingIncrement(isolate, skeleton), Just(kDontThrow))
1026 .FromJust());
1028 isolate, options, factory->roundingMode_string(),
1029 RoundingModeString(isolate, skeleton), Just(kDontThrow))
1030 .FromJust());
1032 isolate, options, factory->roundingPriority_string(),
1033 RoundingPriorityString(isolate, skeleton), Just(kDontThrow))
1034 .FromJust());
1036 isolate, options, factory->trailingZeroDisplay_string(),
1037 TrailingZeroDisplayString(isolate, skeleton), Just(kDontThrow))
1038 .FromJust());
1039 return options;
1040}
1041
1042// ecma402/#sec-unwrapnumberformat
1044 Isolate* isolate, DirectHandle<JSReceiver> format_holder) {
1045 // old code copy from NumberFormat::Unwrap that has no spec comment and
1046 // compiled but fail unit tests.
1047 DirectHandle<Context> native_context(isolate->context()->native_context(),
1048 isolate);
1049 DirectHandle<JSFunction> constructor(
1050 Cast<JSFunction>(native_context->intl_number_format_function()), isolate);
1051 DirectHandle<Object> object;
1053 isolate, object,
1054 Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor,
1055 IsJSNumberFormat(*format_holder)));
1056 // 4. If ... or nf does not have an [[InitializedNumberFormat]] internal slot,
1057 // then
1058 if (!IsJSNumberFormat(*object)) {
1059 // a. Throw a TypeError exception.
1060 THROW_NEW_ERROR(isolate,
1061 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
1062 isolate->factory()->NewStringFromAsciiChecked(
1063 "UnwrapNumberFormat")));
1064 }
1065 // 5. Return nf.
1066 return Cast<JSNumberFormat>(object);
1067}
1068
1069// static
1071 Isolate* isolate, DirectHandle<Map> map, DirectHandle<Object> locales,
1072 DirectHandle<Object> options_obj, const char* service) {
1073 Factory* factory = isolate->factory();
1074
1075 // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
1076 Maybe<std::vector<std::string>> maybe_requested_locales =
1077 Intl::CanonicalizeLocaleList(isolate, locales);
1078 MAYBE_RETURN(maybe_requested_locales, DirectHandle<JSNumberFormat>());
1079 std::vector<std::string> requested_locales =
1080 maybe_requested_locales.FromJust();
1081
1082 // 2. Set options to ? CoerceOptionsToObject(options).
1085 isolate, options, CoerceOptionsToObject(isolate, options_obj, service));
1086
1087 // 3. Let opt be a new Record.
1088 // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", «
1089 // "lookup", "best fit" », "best fit").
1090 // 5. Set opt.[[localeMatcher]] to matcher.
1091 Maybe<Intl::MatcherOption> maybe_locale_matcher =
1092 Intl::GetLocaleMatcher(isolate, options, service);
1093 MAYBE_RETURN(maybe_locale_matcher, MaybeDirectHandle<JSNumberFormat>());
1094 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
1095
1096 std::unique_ptr<char[]> numbering_system_str = nullptr;
1097 // 6. Let _numberingSystem_ be ? GetOption(_options_, `"numberingSystem"`,
1098 // `"string"`, *undefined*, *undefined*).
1099 Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem(
1100 isolate, options, service, &numbering_system_str);
1101 // 7. If _numberingSystem_ is not *undefined*, then
1102 // 8. If _numberingSystem_ does not match the
1103 // `(3*8alphanum) *("-" (3*8alphanum))` sequence, throw a *RangeError*
1104 // exception.
1105 MAYBE_RETURN(maybe_numberingSystem, MaybeDirectHandle<JSNumberFormat>());
1106
1107 // 9. Let localeData be %NumberFormat%.[[LocaleData]].
1108 // 10. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]],
1109 // requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]],
1110 // localeData).
1111 std::set<std::string> relevant_extension_keys{"nu"};
1112 Maybe<Intl::ResolvedLocale> maybe_resolve_locale =
1114 requested_locales, matcher, relevant_extension_keys);
1115 if (maybe_resolve_locale.IsNothing()) {
1116 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
1117 }
1118 Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
1119
1120 icu::Locale icu_locale = r.icu_locale;
1121 UErrorCode status = U_ZERO_ERROR;
1122 if (numbering_system_str != nullptr) {
1123 auto nu_extension_it = r.extensions.find("nu");
1124 if (nu_extension_it != r.extensions.end() &&
1125 nu_extension_it->second != numbering_system_str.get()) {
1126 icu_locale.setUnicodeKeywordValue("nu", nullptr, status);
1127 DCHECK(U_SUCCESS(status));
1128 }
1129 }
1130
1131 // 9. Set numberFormat.[[Locale]] to r.[[locale]].
1132 Maybe<std::string> maybe_locale_str = Intl::ToLanguageTag(icu_locale);
1134 DirectHandle<String> locale_str =
1135 isolate->factory()->NewStringFromAsciiChecked(
1136 maybe_locale_str.FromJust().c_str());
1137
1138 if (numbering_system_str != nullptr &&
1139 Intl::IsValidNumberingSystem(numbering_system_str.get())) {
1140 icu_locale.setUnicodeKeywordValue("nu", numbering_system_str.get(), status);
1141 DCHECK(U_SUCCESS(status));
1142 }
1143
1144 std::string numbering_system = Intl::GetNumberingSystem(icu_locale);
1145
1146 // 11. Let dataLocale be r.[[dataLocale]].
1147
1148 icu::number::UnlocalizedNumberFormatter settings =
1149 icu::number::UnlocalizedNumberFormatter().roundingMode(UNUM_ROUND_HALFUP);
1150
1151 // For 'latn' numbering system, skip the adoptSymbols which would cause
1152 // 10.1%-13.7% of regression of JSTests/Intl-NewIntlNumberFormat
1153 // See crbug/1052751 so we skip calling adoptSymbols and depending on the
1154 // default instead.
1155 if (!numbering_system.empty() && numbering_system != "latn") {
1156 settings = settings.adoptSymbols(icu::NumberingSystem::createInstanceByName(
1157 numbering_system.c_str(), status));
1158 DCHECK(U_SUCCESS(status));
1159 }
1160
1161 // ==== Start SetNumberFormatUnitOptions ====
1162 // 3. Let style be ? GetOption(options, "style", "string", « "decimal",
1163 // "percent", "currency", "unit" », "decimal").
1164
1166 isolate, options, "style", service,
1167 {"decimal", "percent", "currency", "unit"},
1168 {Style::DECIMAL, Style::PERCENT, Style::CURRENCY, Style::UNIT},
1169 Style::DECIMAL);
1171 Style style = maybe_style.FromJust();
1172
1173 // 4. Set intlObj.[[Style]] to style.
1174
1175 // 5. Let currency be ? GetOption(options, "currency", "string", undefined,
1176 // undefined).
1177 std::unique_ptr<char[]> currency_cstr;
1178 const std::vector<const char*> empty_values = {};
1179 Maybe<bool> found_currency = GetStringOption(
1180 isolate, options, "currency", empty_values, service, &currency_cstr);
1182
1183 std::string currency;
1184 // 6. If currency is not undefined, then
1185 if (found_currency.FromJust()) {
1186 DCHECK_NOT_NULL(currency_cstr.get());
1187 currency = currency_cstr.get();
1188 // 6. a. If the result of IsWellFormedCurrencyCode(currency) is false,
1189 // throw a RangeError exception.
1190 if (!IsWellFormedCurrencyCode(currency)) {
1192 isolate,
1193 NewRangeError(MessageTemplate::kInvalid,
1194 factory->NewStringFromStaticChars("currency code"),
1195 factory->NewStringFromAsciiChecked(currency.c_str())));
1196 }
1197 } else {
1198 // 7. If style is "currency" and currency is undefined, throw a TypeError
1199 // exception.
1200 if (style == Style::CURRENCY) {
1201 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCurrencyCode));
1202 }
1203 }
1204 // 8. Let currencyDisplay be ? GetOption(options, "currencyDisplay",
1205 // "string", « "code", "symbol", "name", "narrowSymbol" », "symbol").
1206 Maybe<CurrencyDisplay> maybe_currency_display =
1208 isolate, options, "currencyDisplay", service,
1209 {"code", "symbol", "name", "narrowSymbol"},
1210 {CurrencyDisplay::CODE, CurrencyDisplay::SYMBOL,
1211 CurrencyDisplay::NAME, CurrencyDisplay::NARROW_SYMBOL},
1212 CurrencyDisplay::SYMBOL);
1213 MAYBE_RETURN(maybe_currency_display, MaybeDirectHandle<JSNumberFormat>());
1214 CurrencyDisplay currency_display = maybe_currency_display.FromJust();
1215
1216 CurrencySign currency_sign = CurrencySign::STANDARD;
1217 // 9. Let currencySign be ? GetOption(options, "currencySign", "string", «
1218 // "standard", "accounting" », "standard").
1220 isolate, options, "currencySign", service, {"standard", "accounting"},
1221 {CurrencySign::STANDARD, CurrencySign::ACCOUNTING},
1222 CurrencySign::STANDARD);
1223 MAYBE_RETURN(maybe_currency_sign, MaybeDirectHandle<JSNumberFormat>());
1224 currency_sign = maybe_currency_sign.FromJust();
1225
1226 // 10. Let unit be ? GetOption(options, "unit", "string", undefined,
1227 // undefined).
1228 std::unique_ptr<char[]> unit_cstr;
1229 Maybe<bool> found_unit = GetStringOption(isolate, options, "unit",
1230 empty_values, service, &unit_cstr);
1232
1233 std::pair<icu::MeasureUnit, icu::MeasureUnit> unit_pair;
1234 // 11. If unit is not undefined, then
1235 if (found_unit.FromJust()) {
1236 DCHECK_NOT_NULL(unit_cstr.get());
1237 std::string unit = unit_cstr.get();
1238 // 11.a If the result of IsWellFormedUnitIdentifier(unit) is false, throw a
1239 // RangeError exception.
1241 IsWellFormedUnitIdentifier(isolate, unit);
1242 if (maybe_wellformed_unit.IsNothing()) {
1244 isolate,
1245 NewRangeError(MessageTemplate::kInvalidUnit,
1246 factory->NewStringFromAsciiChecked(service),
1247 factory->NewStringFromAsciiChecked(unit.c_str())));
1248 }
1249 unit_pair = maybe_wellformed_unit.FromJust();
1250 } else {
1251 // 12. If style is "unit" and unit is undefined, throw a TypeError
1252 // exception.
1253 if (style == Style::UNIT) {
1254 THROW_NEW_ERROR(isolate,
1255 NewTypeError(MessageTemplate::kInvalidUnit,
1256 factory->NewStringFromAsciiChecked(service),
1257 factory->empty_string()));
1258 }
1259 }
1260
1261 // 13. Let unitDisplay be ? GetOption(options, "unitDisplay", "string", «
1262 // "short", "narrow", "long" », "short").
1264 isolate, options, "unitDisplay", service, {"short", "narrow", "long"},
1265 {UnitDisplay::SHORT, UnitDisplay::NARROW, UnitDisplay::LONG},
1266 UnitDisplay::SHORT);
1267 MAYBE_RETURN(maybe_unit_display, MaybeDirectHandle<JSNumberFormat>());
1268 UnitDisplay unit_display = maybe_unit_display.FromJust();
1269
1270 // 14. If style is "currency", then
1271 icu::UnicodeString currency_ustr;
1272 if (style == Style::CURRENCY) {
1273 // 14.a. If currency is undefined, throw a TypeError exception.
1274 if (!found_currency.FromJust()) {
1275 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCurrencyCode));
1276 }
1277 // 14.a. Let currency be the result of converting currency to upper case as
1278 // specified in 6.1
1279 std::transform(currency.begin(), currency.end(), currency.begin(), toupper);
1280 currency_ustr = currency.c_str();
1281
1282 // 14.b. Set numberFormat.[[Currency]] to currency.
1283 if (!currency_ustr.isEmpty()) {
1284 DirectHandle<String> currency_string;
1285 ASSIGN_RETURN_ON_EXCEPTION(isolate, currency_string,
1286 Intl::ToString(isolate, currency_ustr));
1287
1288 settings =
1289 settings.unit(icu::CurrencyUnit(currency_ustr.getBuffer(), status));
1290 DCHECK(U_SUCCESS(status));
1291 // 14.c Set intlObj.[[CurrencyDisplay]] to currencyDisplay.
1292 // The default unitWidth is SHORT in ICU and that mapped from
1293 // Symbol so we can skip the setting for optimization.
1294 if (currency_display != CurrencyDisplay::SYMBOL) {
1295 settings = settings.unitWidth(ToUNumberUnitWidth(currency_display));
1296 }
1297 DCHECK(U_SUCCESS(status));
1298 }
1299 }
1300
1301 // 15. If style is "unit", then
1302 if (style == Style::UNIT) {
1303 // Track newer style "unit".
1305
1306 icu::MeasureUnit none = icu::MeasureUnit();
1307 // 13.b Set intlObj.[[Unit]] to unit.
1308 if (unit_pair.first != AVOID_AMBIGUOUS_OP_WARNING(none)) {
1309 settings = settings.unit(unit_pair.first);
1310 }
1311 if (unit_pair.second != AVOID_AMBIGUOUS_OP_WARNING(none)) {
1312 settings = settings.perUnit(unit_pair.second);
1313 }
1314
1315 // The default unitWidth is SHORT in ICU and that mapped from
1316 // Symbol so we can skip the setting for optimization.
1317 if (unit_display != UnitDisplay::SHORT) {
1318 settings = settings.unitWidth(ToUNumberUnitWidth(unit_display));
1319 }
1320 }
1321
1322 // === End of SetNumberFormatUnitOptions
1323
1324 if (style == Style::PERCENT) {
1325 settings = settings.unit(icu::MeasureUnit::getPercent())
1326 .scale(icu::number::Scale::powerOfTen(2));
1327 }
1328
1329 Notation notation = Notation::STANDARD;
1330 // xx. Let notation be ? GetOption(options, "notation", "string", «
1331 // "standard", "scientific", "engineering", "compact" », "standard").
1333 isolate, notation,
1335 isolate, options, "notation", service,
1336 {"standard", "scientific", "engineering", "compact"},
1337 {Notation::STANDARD, Notation::SCIENTIFIC, Notation::ENGINEERING,
1338 Notation::COMPACT},
1339 Notation::STANDARD),
1341 // xx. Set numberFormat.[[Notation]] to notation.
1342
1343 // xx. If style is *"currency"* and *"notation"* is *"standard"*, then
1344 int mnfd_default, mxfd_default;
1345 if (style == Style::CURRENCY && notation == Notation::STANDARD) {
1346 // b. Let cDigits be CurrencyDigits(currency).
1347 int c_digits = CurrencyDigits(currency_ustr);
1348 // c. Let mnfdDefault be cDigits.
1349 // d. Let mxfdDefault be cDigits.
1350 mnfd_default = c_digits;
1351 mxfd_default = c_digits;
1352 // 17. Else,
1353 } else {
1354 // a. Let mnfdDefault be 0.
1355 mnfd_default = 0;
1356 // b. If style is "percent", then
1357 if (style == Style::PERCENT) {
1358 // i. Let mxfdDefault be 0.
1359 mxfd_default = 0;
1360 } else {
1361 // c. Else,
1362 // i. Let mxfdDefault be 3.
1363 mxfd_default = 3;
1364 }
1365 }
1366
1367 // 23. Perform ? SetNumberFormatDigitOptions(numberFormat, options,
1368 // mnfdDefault, mxfdDefault).
1369 Maybe<Intl::NumberFormatDigitOptions> maybe_digit_options =
1370 Intl::SetNumberFormatDigitOptions(isolate, options, mnfd_default,
1371 mxfd_default,
1372 notation == Notation::COMPACT, service);
1373 MAYBE_RETURN(maybe_digit_options, DirectHandle<JSNumberFormat>());
1374 Intl::NumberFormatDigitOptions digit_options = maybe_digit_options.FromJust();
1375
1376 // 13. If roundingIncrement is not 1, set mxfdDefault to mnfdDefault.
1377 if (digit_options.rounding_increment != 1) {
1378 mxfd_default = mnfd_default;
1379 }
1380 // 14. Set intlObj.[[RoundingIncrement]] to roundingIncrement.
1381
1382 // 15. Set intlObj.[[RoundingMode]] to roundingMode.
1383
1384 // 16. Set intlObj.[[TrailingZeroDisplay]] to trailingZeroDisplay.
1385 settings = SetDigitOptionsToFormatter(settings, digit_options);
1386
1387 // 28. Let compactDisplay be ? GetOption(options, "compactDisplay",
1388 // "string", « "short", "long" », "short").
1390 isolate, options, "compactDisplay", service, {"short", "long"},
1391 {CompactDisplay::SHORT, CompactDisplay::LONG}, CompactDisplay::SHORT);
1392 MAYBE_RETURN(maybe_compact_display, MaybeDirectHandle<JSNumberFormat>());
1393 CompactDisplay compact_display = maybe_compact_display.FromJust();
1394
1395 // The default notation in ICU is Simple, which mapped from STANDARD
1396 // so we can skip setting it.
1397 if (notation != Notation::STANDARD) {
1398 settings = settings.notation(ToICUNotation(notation, compact_display));
1399 }
1400
1401 // 28. Let defaultUseGrouping be "auto".
1402 UseGrouping default_use_grouping = UseGrouping::AUTO;
1403
1404 // 29. If notation is "compact", then
1405 if (notation == Notation::COMPACT) {
1406 // a. Set numberFormat.[[CompactDisplay]] to compactDisplay.
1407 // Done in above together
1408 // b. Set defaultUseGrouping to "min2".
1409 default_use_grouping = UseGrouping::MIN2;
1410 }
1411
1412 // 30. Let useGrouping be ? GetStringOrBooleanOption(options, "useGrouping",
1413 // « "min2", "auto", "always" », "always", false, defaultUseGrouping).
1415 isolate, options, "useGrouping", service, {"min2", "auto", "always"},
1416 {UseGrouping::MIN2, UseGrouping::AUTO, UseGrouping::ALWAYS},
1417 UseGrouping::ALWAYS, // trueValue
1418 UseGrouping::OFF, // falseValue
1419 default_use_grouping); // fallbackValue
1420 MAYBE_RETURN(maybe_use_grouping, MaybeDirectHandle<JSNumberFormat>());
1421 UseGrouping use_grouping = maybe_use_grouping.FromJust();
1422 // 31. Set numberFormat.[[UseGrouping]] to useGrouping.
1423 if (use_grouping != UseGrouping::AUTO) {
1424 settings = settings.grouping(ToUNumberGroupingStrategy(use_grouping));
1425 }
1426
1427 // 32. Let signDisplay be ? GetOption(options, "signDisplay", "string", «
1428 // "auto", "never", "always", "exceptZero", "negative" », "auto").
1429 Maybe<SignDisplay> maybe_sign_display = Nothing<SignDisplay>();
1430 maybe_sign_display = GetStringOption<SignDisplay>(
1431 isolate, options, "signDisplay", service,
1432 {"auto", "never", "always", "exceptZero", "negative"},
1433 {SignDisplay::AUTO, SignDisplay::NEVER, SignDisplay::ALWAYS,
1434 SignDisplay::EXCEPT_ZERO, SignDisplay::NEGATIVE},
1435 SignDisplay::AUTO);
1436 MAYBE_RETURN(maybe_sign_display, MaybeDirectHandle<JSNumberFormat>());
1437 SignDisplay sign_display = maybe_sign_display.FromJust();
1438
1439 // 33. Set numberFormat.[[SignDisplay]] to signDisplay.
1440 // The default sign in ICU is UNUM_SIGN_AUTO which is mapped from
1441 // SignDisplay::AUTO and CurrencySign::STANDARD so we can skip setting
1442 // under that values for optimization.
1443 if (sign_display != SignDisplay::AUTO ||
1444 currency_sign != CurrencySign::STANDARD) {
1445 settings = settings.sign(ToUNumberSignDisplay(sign_display, currency_sign));
1446 }
1447
1448 // 25. Let dataLocaleData be localeData.[[<dataLocale>]].
1449 //
1450 // 26. Let patterns be dataLocaleData.[[patterns]].
1451 //
1452 // 27. Assert: patterns is a record (see 11.3.3).
1453 //
1454 // 28. Let stylePatterns be patterns.[[<style>]].
1455 //
1456 // 29. Set numberFormat.[[PositivePattern]] to
1457 // stylePatterns.[[positivePattern]].
1458 //
1459 // 30. Set numberFormat.[[NegativePattern]] to
1460 // stylePatterns.[[negativePattern]].
1461 //
1462 icu::number::LocalizedNumberFormatter fmt = settings.locale(icu_locale);
1463
1465 managed_number_formatter =
1467 isolate, 0,
1468 std::make_shared<icu::number::LocalizedNumberFormatter>(fmt));
1469
1470 // Now all properties are ready, so we can allocate the result object.
1472 isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
1474 number_format->set_locale(*locale_str);
1475
1476 number_format->set_icu_number_formatter(*managed_number_formatter);
1477 number_format->set_bound_format(*factory->undefined_value());
1478
1479 // 31. Return numberFormat.
1480 return number_format;
1481}
1482
1483namespace {
1484
1485icu::number::FormattedNumber FormatDecimalString(
1486 Isolate* isolate,
1487 const icu::number::LocalizedNumberFormatter& number_format,
1488 Handle<String> string, UErrorCode& status) {
1489 string = String::Flatten(isolate, string);
1491 const String::FlatContent& flat = string->GetFlatContent(no_gc);
1492 int32_t length = static_cast<int32_t>(string->length());
1493 if (flat.IsOneByte()) {
1494 const char* char_buffer =
1495 reinterpret_cast<const char*>(flat.ToOneByteVector().begin());
1496 return number_format.formatDecimal({char_buffer, length}, status);
1497 }
1498 return number_format.formatDecimal({string->ToCString().get(), length},
1499 status);
1500}
1501
1502} // namespace
1503
1505
1508 if (IsNumber(*value_)) {
1509 return isolate->factory()->NumberToString(value_);
1510 }
1511 if (IsBigInt(*value_)) {
1512 return BigInt::ToString(isolate, Cast<BigInt>(value_));
1513 }
1514 DCHECK(IsString(*value_));
1515 return Cast<String>(value_);
1516}
1517
1518namespace {
1520 Isolate* isolate,
1521 const icu::number::LocalizedNumberFormatter& number_format,
1522 Handle<Object> numeric_obj) {
1523 icu::number::FormattedNumber formatted;
1524 // If it is BigInt, handle it differently.
1525 UErrorCode status = U_ZERO_ERROR;
1526 if (IsBigInt(*numeric_obj)) {
1527 auto big_int = Cast<BigInt>(numeric_obj);
1528 Handle<String> big_int_string;
1529 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, big_int_string,
1530 BigInt::ToString(isolate, big_int),
1532 big_int_string = String::Flatten(isolate, big_int_string);
1534 const String::FlatContent& flat = big_int_string->GetFlatContent(no_gc);
1535 int32_t length = static_cast<int32_t>(big_int_string->length());
1536 DCHECK(flat.IsOneByte());
1537 const char* char_buffer =
1538 reinterpret_cast<const char*>(flat.ToOneByteVector().begin());
1539 formatted = number_format.formatDecimal({char_buffer, length}, status);
1540 } else {
1541 if (IsString(*numeric_obj)) {
1542 // TODO(ftang) Correct the handling of string after the resolution of
1543 // https://github.com/tc39/proposal-intl-numberformat-v3/pull/82
1544 DirectHandle<String> string =
1545 String::Flatten(isolate, Cast<String>(numeric_obj));
1547 const String::FlatContent& flat = string->GetFlatContent(no_gc);
1548 int32_t length = static_cast<int32_t>(string->length());
1549 if (flat.IsOneByte()) {
1550 const char* char_buffer =
1551 reinterpret_cast<const char*>(flat.ToOneByteVector().begin());
1552 formatted = number_format.formatDecimal({char_buffer, length}, status);
1553 } else {
1554 // We may have two bytes string such as "漢 123456789".substring(2)
1555 // The value will be "123456789" only in ASCII range, but encoded
1556 // in two bytes string.
1557 // ICU accepts UTF8 string, so if the source is two-byte encoded,
1558 // copy into a UTF8 string via ToCString.
1559 formatted = number_format.formatDecimal(
1560 {string->ToCString().get(), length}, status);
1561 }
1562 } else {
1563 double number = IsNaN(*numeric_obj)
1564 ? std::numeric_limits<double>::quiet_NaN()
1565 : Object::NumberValue(*numeric_obj);
1566 formatted = number_format.formatDouble(number, status);
1567 }
1568 }
1569 if (U_FAILURE(status)) {
1570 // This happen because of icu data trimming trim out "unit".
1571 // See https://bugs.chromium.org/p/v8/issues/detail?id=8641
1573 NewTypeError(MessageTemplate::kIcuError),
1575 }
1576 return Just(std::move(formatted));
1577}
1578
1579} // namespace
1580
1582 Isolate* isolate,
1583 const icu::number::LocalizedNumberFormatter& number_format,
1584 const IntlMathematicalValue& x) {
1585 if (IsString(*x.value_)) {
1587 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, string, x.ToString(isolate),
1589 UErrorCode status = U_ZERO_ERROR;
1590 icu::number::FormattedNumber result =
1591 FormatDecimalString(isolate, number_format, string, status);
1592 if (U_FAILURE(status)) {
1594 NewTypeError(MessageTemplate::kIcuError),
1596 }
1597 return Just(std::move(result));
1598 }
1599 CHECK(IsNumber(*x.value_) || IsBigInt(*x.value_));
1600 return IcuFormatNumber(isolate, number_format, x.value_);
1601}
1602
1604 Isolate* isolate,
1605 const icu::number::LocalizedNumberRangeFormatter& number_range_format,
1607 icu::Formattable x_formatable;
1609 isolate, x_formatable, x.ToFormattable(isolate),
1611
1612 icu::Formattable y_formatable;
1614 isolate, y_formatable, y.ToFormattable(isolate),
1616
1617 UErrorCode status = U_ZERO_ERROR;
1618 icu::number::FormattedNumberRange result =
1619 number_range_format.formatFormattableRange(x_formatable, y_formatable,
1620 status);
1621 if (U_FAILURE(status)) {
1623 NewTypeError(MessageTemplate::kIcuError),
1625 }
1626
1627 return Just(std::move(result));
1628}
1629
1630namespace {
1631// Return the index of the end of leading white space or line terminator
1632// and the index of the start of trailing white space or line terminator.
1633template <typename Char>
1634std::pair<int, int> FindLeadingAndTrailingWhiteSpaceOrLineTerminator(
1636 size_t leading_end = 0;
1637
1638 // Find the length of leading StrWhiteSpaceChar.
1639 while (leading_end < src.size() &&
1641 static_cast<uint16_t>(src.at(leading_end)))) {
1642 leading_end++;
1643 }
1644 size_t trailing_start = src.size();
1645 // Find the start of the trailing StrWhiteSpaceChar
1646 while (trailing_start > leading_end &&
1648 static_cast<uint16_t>(src.at(trailing_start - 1)))) {
1649 trailing_start--;
1650 }
1651 return std::make_pair(leading_end, trailing_start);
1652}
1653
1654Handle<String> TrimWhiteSpaceOrLineTerminator(Isolate* isolate,
1655 Handle<String> string) {
1656 string = String::Flatten(isolate, string);
1657 std::pair<int, uint32_t> whitespace_offsets;
1658 {
1660 String::FlatContent flat = string->GetFlatContent(no_gc);
1661 if (flat.IsOneByte()) {
1662 whitespace_offsets = FindLeadingAndTrailingWhiteSpaceOrLineTerminator(
1663 flat.ToOneByteVector());
1664 } else {
1665 whitespace_offsets =
1666 FindLeadingAndTrailingWhiteSpaceOrLineTerminator(flat.ToUC16Vector());
1667 }
1668 }
1669 if (whitespace_offsets.first == 0 &&
1670 string->length() == whitespace_offsets.second) {
1671 return string;
1672 }
1673 return isolate->factory()->NewSubString(string, whitespace_offsets.first,
1674 whitespace_offsets.second);
1675}
1676
1677} // namespace
1678
1679// #sec-tointlmathematicalvalue
1681 Handle<Object> value) {
1682 Factory* factory = isolate->factory();
1683 // 1. Let primValue be ? ToPrimitive(value, number).
1684 Handle<Object> prim_value;
1685 if (IsJSReceiver(*value)) {
1687 isolate, prim_value,
1691 } else {
1692 prim_value = value;
1693 }
1695 // 2. If Type(primValue) is BigInt, return the mathematical value of
1696 // primValue.
1697 if (IsBigInt(*prim_value)) {
1698 result.value_ = prim_value;
1699 result.approx_ = Cast<BigInt>(prim_value)->AsInt64();
1700 return Just(result);
1701 }
1702 if (IsOddball(*prim_value)) {
1703 prim_value = Oddball::ToNumber(isolate, Cast<Oddball>(prim_value));
1704 }
1705 if (IsNumber(*prim_value)) {
1706 result.value_ = prim_value;
1707 result.approx_ = Object::NumberValue(*prim_value);
1708 return Just(result);
1709 }
1710 if (!IsString(*prim_value)) {
1711 // No need to convert from Number to String, just call ToNumber.
1713 Object::ToNumber(isolate, prim_value),
1715 result.approx_ = Object::NumberValue(*result.value_);
1716 return Just(result);
1717 }
1718 Handle<String> string = Cast<String>(prim_value);
1719
1720 string = TrimWhiteSpaceOrLineTerminator(isolate, string);
1721 if (string->length() == 0) {
1722 result.value_ = handle(Smi::zero(), isolate);
1723 result.approx_ = 0;
1724 return Just(result);
1725 }
1726 // We may have a NonDecimalIntegerLiteral:
1727 if (2 < string->length() && string->Get(0) == '0') {
1728 uint16_t ch = string->Get(1);
1729 if (ch == 'b' || ch == 'B' || ch == 'o' || ch == 'O' || ch == 'x' ||
1730 ch == 'X') {
1731 result.approx_ =
1732 StringToDouble(isolate, string, ALLOW_NON_DECIMAL_PREFIX, 0);
1733 // If approx is within the precision, just return as Number.
1734 if (result.approx_ < kMaxSafeInteger) {
1735 result.value_ = isolate->factory()->NewNumber(result.approx_);
1736 return Just(result);
1737 }
1738 // Otherwise return the BigInt
1739 MaybeHandle<BigInt> maybe_bigint = StringToBigInt(isolate, string);
1740 // If the parsing of BigInt fail, return nan
1741 if (maybe_bigint.is_null()) {
1742 isolate->clear_exception();
1743 result.value_ = factory->nan_value();
1744 return Just(result);
1745 }
1746 result.value_ = maybe_bigint.ToHandleChecked();
1747 return Just(result);
1748 }
1749 }
1750 // If it does not fit StrDecimalLiteral StrWhiteSpace_opt, StringToDouble will
1751 // parse it as NaN, in that case, return NaN.
1752 result.approx_ = StringToDouble(isolate, string, NO_CONVERSION_FLAG, 0);
1753 if (std::isnan(result.approx_)) {
1754 result.value_ = factory->nan_value();
1755 return Just(result);
1756 }
1757 // Handle Infinity / +Infinity / -Infinity
1758 if (!std::isfinite(result.approx_)) {
1759 if (result.approx_ < 0) {
1760 result.value_ = factory->minus_infinity_value();
1761 } else {
1762 result.value_ = factory->infinity_value();
1763 }
1764 return Just(result);
1765 }
1766 // At this point, str is for sure fit
1767 // "StrNumericLiteral StrWhiteSpace_opt" excluding "(+|-)?Infinity"
1768 result.value_ = string;
1769
1770 return Just(result);
1771}
1772
1774 Isolate* isolate) const {
1775 if (IsNumber(*value_)) {
1776 return Just(icu::Formattable(approx_));
1777 }
1779 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, string, ToString(isolate),
1781 UErrorCode status = U_ZERO_ERROR;
1782 {
1784 const String::FlatContent& flat = string->GetFlatContent(no_gc);
1785 int32_t length = static_cast<int32_t>(string->length());
1786 if (flat.IsOneByte()) {
1787 icu::Formattable result(
1788 {reinterpret_cast<const char*>(flat.ToOneByteVector().begin()),
1789 length},
1790 status);
1791 if (U_SUCCESS(status)) return Just(result);
1792 } else {
1793 icu::Formattable result({string->ToCString().get(), length}, status);
1794 if (U_SUCCESS(status)) return Just(result);
1795 }
1796 }
1798 NewTypeError(MessageTemplate::kIcuError),
1800}
1801
1802namespace {
1803bool cmp_NumberFormatSpan(const NumberFormatSpan& a,
1804 const NumberFormatSpan& b) {
1805 // Regions that start earlier should be encountered earlier.
1806 if (a.begin_pos < b.begin_pos) return true;
1807 if (a.begin_pos > b.begin_pos) return false;
1808 // For regions that start in the same place, regions that last longer should
1809 // be encountered earlier.
1810 if (a.end_pos < b.end_pos) return false;
1811 if (a.end_pos > b.end_pos) return true;
1812 // For regions that are exactly the same, one of them must be the "literal"
1813 // backdrop we added, which has a field_id of -1, so consider higher field_ids
1814 // to be later.
1815 return a.field_id < b.field_id;
1816}
1817
1818} // namespace
1819
1820// Flattens a list of possibly-overlapping "regions" to a list of
1821// non-overlapping "parts". At least one of the input regions must span the
1822// entire space of possible indexes. The regions parameter will sorted in-place
1823// according to some criteria; this is done for performance to avoid copying the
1824// input.
1825std::vector<NumberFormatSpan> FlattenRegionsToParts(
1826 std::vector<NumberFormatSpan>* regions) {
1827 // The intention of this algorithm is that it's used to translate ICU "fields"
1828 // to JavaScript "parts" of a formatted string. Each ICU field and JavaScript
1829 // part has an integer field_id, which corresponds to something like "grouping
1830 // separator", "fraction", or "percent sign", and has a begin and end
1831 // position. Here's a diagram of:
1832
1833 // var nf = new Intl.NumberFormat(['de'], {style:'currency',currency:'EUR'});
1834 // nf.formatToParts(123456.78);
1835
1836 // : 6
1837 // input regions: 0000000211 7
1838 // ('-' means -1): ------------
1839 // formatted string: "123.456,78 €"
1840 // output parts: 0006000211-7
1841
1842 // To illustrate the requirements of this algorithm, here's a contrived and
1843 // convoluted example of inputs and expected outputs:
1844
1845 // : 4
1846 // : 22 33 3
1847 // : 11111 22
1848 // input regions: 0000000 111
1849 // : ------------
1850 // formatted string: "abcdefghijkl"
1851 // output parts: 0221340--231
1852 // (The characters in the formatted string are irrelevant to this function.)
1853
1854 // We arrange the overlapping input regions like a mountain range where
1855 // smaller regions are "on top" of larger regions, and we output a birds-eye
1856 // view of the mountains, so that smaller regions take priority over larger
1857 // regions.
1858 std::sort(regions->begin(), regions->end(), cmp_NumberFormatSpan);
1859 std::vector<size_t> overlapping_region_index_stack;
1860 // At least one item in regions must be a region spanning the entire string.
1861 // Due to the sorting above, the first item in the vector will be one of them.
1862 overlapping_region_index_stack.push_back(0);
1863 NumberFormatSpan top_region = regions->at(0);
1864 size_t region_iterator = 1;
1865 int32_t entire_size = top_region.end_pos;
1866
1867 std::vector<NumberFormatSpan> out_parts;
1868
1869 // The "climber" is a cursor that advances from left to right climbing "up"
1870 // and "down" the mountains. Whenever the climber moves to the right, that
1871 // represents an item of output.
1872 int32_t climber = 0;
1873 while (climber < entire_size) {
1874 int32_t next_region_begin_pos;
1875 if (region_iterator < regions->size()) {
1876 next_region_begin_pos = regions->at(region_iterator).begin_pos;
1877 } else {
1878 // finish off the rest of the input by proceeding to the end.
1879 next_region_begin_pos = entire_size;
1880 }
1881
1882 if (climber < next_region_begin_pos) {
1883 while (top_region.end_pos < next_region_begin_pos) {
1884 if (climber < top_region.end_pos) {
1885 // step down
1886 out_parts.push_back(NumberFormatSpan(top_region.field_id, climber,
1887 top_region.end_pos));
1888 climber = top_region.end_pos;
1889 } else {
1890 // drop down
1891 }
1892 overlapping_region_index_stack.pop_back();
1893 top_region = regions->at(overlapping_region_index_stack.back());
1894 }
1895 if (climber < next_region_begin_pos) {
1896 // cross a plateau/mesa/valley
1897 out_parts.push_back(NumberFormatSpan(top_region.field_id, climber,
1898 next_region_begin_pos));
1899 climber = next_region_begin_pos;
1900 }
1901 }
1902 if (region_iterator < regions->size()) {
1903 overlapping_region_index_stack.push_back(region_iterator++);
1904 top_region = regions->at(overlapping_region_index_stack.back());
1905 }
1906 }
1907 return out_parts;
1908}
1909
1910namespace {
1911Maybe<int> ConstructParts(Isolate* isolate,
1912 const icu::FormattedValue& formatted,
1913 DirectHandle<JSArray> result, int start_index,
1914 bool style_is_unit, bool is_nan, bool output_source,
1915 bool output_unit, DirectHandle<String> unit) {
1916 UErrorCode status = U_ZERO_ERROR;
1917 icu::UnicodeString formatted_text = formatted.toString(status);
1918 if (U_FAILURE(status)) {
1920 isolate, NewTypeError(MessageTemplate::kIcuError), Nothing<int>());
1921 }
1922 int32_t length = formatted_text.length();
1923 int index = start_index;
1924 if (length == 0) return Just(index);
1925
1926 std::vector<NumberFormatSpan> regions;
1927 // Add a "literal" backdrop for the entire string. This will be used if no
1928 // other region covers some part of the formatted string. It's possible
1929 // there's another field with exactly the same begin and end as this backdrop,
1930 // in which case the backdrop's field_id of -1 will give it lower priority.
1931 regions.push_back(NumberFormatSpan(-1, 0, formatted_text.length()));
1932 Intl::FormatRangeSourceTracker tracker;
1933 {
1934 icu::ConstrainedFieldPosition cfpos;
1935 while (formatted.nextPosition(cfpos, status)) {
1936 int32_t category = cfpos.getCategory();
1937 int32_t field = cfpos.getField();
1938 int32_t start = cfpos.getStart();
1939 int32_t limit = cfpos.getLimit();
1940 if (category == UFIELD_CATEGORY_NUMBER_RANGE_SPAN) {
1941 DCHECK_LE(field, 2);
1942 tracker.Add(field, start, limit);
1943 } else {
1944 regions.push_back(NumberFormatSpan(field, start, limit));
1945 }
1946 }
1947 }
1948
1949 std::vector<NumberFormatSpan> parts = FlattenRegionsToParts(&regions);
1950
1951 for (auto it = parts.begin(); it < parts.end(); it++) {
1952 NumberFormatSpan part = *it;
1953 DirectHandle<String> field_type_string =
1954 isolate->factory()->literal_string();
1955 if (part.field_id != -1) {
1956 if (style_is_unit && static_cast<UNumberFormatFields>(part.field_id) ==
1957 UNUM_PERCENT_FIELD) {
1958 // Special case when style is unit.
1959 field_type_string = isolate->factory()->unit_string();
1960 } else {
1961 field_type_string =
1962 Intl::NumberFieldToType(isolate, part, formatted_text, is_nan);
1963 }
1964 }
1965 DirectHandle<String> substring;
1967 isolate, substring,
1968 Intl::ToString(isolate, formatted_text, part.begin_pos, part.end_pos),
1969 Nothing<int>());
1970
1971 if (output_source) {
1973 isolate, result, index, field_type_string, substring,
1974 isolate->factory()->source_string(),
1975 Intl::SourceString(isolate,
1976 tracker.GetSource(part.begin_pos, part.end_pos)));
1977 } else {
1978 if (output_unit) {
1979 Intl::AddElement(isolate, result, index, field_type_string, substring,
1980 isolate->factory()->unit_string(), unit);
1981 } else {
1982 Intl::AddElement(isolate, result, index, field_type_string, substring);
1983 }
1984 }
1985 ++index;
1986 }
1988 return Just(index);
1989}
1990
1991} // namespace
1992
1994 const icu::FormattedValue& formatted,
1996 int start_index, DirectHandle<String> unit) {
1997 return ConstructParts(isolate, formatted, result, start_index, true, false,
1998 false, true, unit);
1999}
2000
2001namespace {
2002
2003// #sec-partitionnumberrangepattern
2004template <typename T, MaybeDirectHandle<T> (*F)(
2005 Isolate*, const icu::FormattedValue&,
2006 const icu::number::LocalizedNumberFormatter&, bool)>
2007MaybeDirectHandle<T> PartitionNumberRangePattern(
2008 Isolate* isolate, DirectHandle<JSNumberFormat> number_format,
2009 Handle<Object> start, Handle<Object> end, const char* func_name) {
2010 Factory* factory = isolate->factory();
2011 // 4. Let x be ? ToIntlMathematicalValue(start).
2014 isolate, x, IntlMathematicalValue::From(isolate, start),
2015 DirectHandle<T>());
2016
2017 // 5. Let y be ? ToIntlMathematicalValue(end).
2020 isolate, y, IntlMathematicalValue::From(isolate, end), DirectHandle<T>());
2021
2022 // 1. If x is not-a-number or y is not-a-number, throw a RangeError exception.
2023 if (x.IsNaN()) {
2025 isolate,
2026 NewRangeError(MessageTemplate::kInvalid,
2027 factory->NewStringFromStaticChars("start"), start),
2029 }
2030 if (y.IsNaN()) {
2032 isolate,
2033 NewRangeError(MessageTemplate::kInvalid,
2034 factory->NewStringFromStaticChars("end"), end),
2035 MaybeDirectHandle<T>());
2036 }
2037
2038 Maybe<icu::number::LocalizedNumberRangeFormatter> maybe_range_formatter =
2040 isolate, number_format->locale(),
2041 *number_format->icu_number_formatter()->raw());
2042 MAYBE_RETURN(maybe_range_formatter, MaybeDirectHandle<T>());
2043
2044 icu::number::LocalizedNumberRangeFormatter nrfmt =
2045 maybe_range_formatter.FromJust();
2046
2047 Maybe<icu::number::FormattedNumberRange> maybe_formatted =
2048 IntlMathematicalValue::FormatRange(isolate, nrfmt, x, y);
2049 MAYBE_RETURN(maybe_formatted, DirectHandle<T>());
2050 icu::number::FormattedNumberRange formatted =
2051 std::move(maybe_formatted).FromJust();
2052
2053 return F(isolate, formatted, *(number_format->icu_number_formatter()->raw()),
2054 false /* is_nan */);
2055}
2056
2057MaybeDirectHandle<String> FormatToString(
2058 Isolate* isolate, const icu::FormattedValue& formatted,
2059 const icu::number::LocalizedNumberFormatter&, bool) {
2060 UErrorCode status = U_ZERO_ERROR;
2061 icu::UnicodeString result = formatted.toString(status);
2062 if (U_FAILURE(status)) {
2063 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
2064 }
2065 return Intl::ToString(isolate, result);
2066}
2067
2068MaybeDirectHandle<JSArray> FormatToJSArray(
2069 Isolate* isolate, const icu::FormattedValue& formatted,
2070 const icu::number::LocalizedNumberFormatter& nfmt, bool is_nan,
2071 bool output_source) {
2072 UErrorCode status = U_ZERO_ERROR;
2073 bool is_unit = Style::UNIT == StyleFromSkeleton(nfmt.toSkeleton(status));
2074 CHECK(U_SUCCESS(status));
2075
2076 Factory* factory = isolate->factory();
2077 DirectHandle<JSArray> result = factory->NewJSArray(0);
2078
2079 int format_to_parts;
2081 isolate, format_to_parts,
2082 ConstructParts(isolate, formatted, result, 0, is_unit, is_nan,
2083 output_source, false, DirectHandle<String>()),
2084 DirectHandle<JSArray>());
2085 USE(format_to_parts);
2086
2087 return result;
2088}
2089
2090MaybeDirectHandle<JSArray> FormatRangeToJSArray(
2091 Isolate* isolate, const icu::FormattedValue& formatted,
2092 const icu::number::LocalizedNumberFormatter& nfmt, bool is_nan) {
2093 return FormatToJSArray(isolate, formatted, nfmt, is_nan, true);
2094}
2095
2096} // namespace
2097
2098Maybe<icu::number::LocalizedNumberRangeFormatter>
2100 Isolate* isolate, Tagged<String> locale,
2101 const icu::number::LocalizedNumberFormatter& number_formatter) {
2102 UErrorCode status = U_ZERO_ERROR;
2103 UParseError perror;
2104 icu::number::LocalizedNumberRangeFormatter range_formatter =
2105 icu::number::UnlocalizedNumberRangeFormatter()
2106 .numberFormatterBoth(icu::number::NumberFormatter::forSkeleton(
2107 number_formatter.toSkeleton(status), perror, status))
2108 .locale(
2109 icu::Locale::forLanguageTag(locale->ToCString().get(), status));
2110 if (U_FAILURE(status)) {
2112 isolate, NewTypeError(MessageTemplate::kIcuError),
2114 }
2115 return Just(range_formatter);
2116}
2117
2119 Isolate* isolate,
2120 const icu::number::LocalizedNumberFormatter& number_format,
2121 Handle<Object> numeric_obj) {
2123 IcuFormatNumber(isolate, number_format, numeric_obj);
2124 MAYBE_RETURN(maybe_format, DirectHandle<String>());
2125 icu::number::FormattedNumber formatted = std::move(maybe_format).FromJust();
2126
2127 return FormatToString(isolate, formatted, number_format, IsNaN(*numeric_obj));
2128}
2129
2131 Isolate* isolate, DirectHandle<JSNumberFormat> number_format,
2132 Handle<Object> value) {
2133 icu::number::LocalizedNumberFormatter* fmt =
2134 number_format->icu_number_formatter()->raw();
2135 CHECK_NOT_NULL(fmt);
2136
2137 // 4. Let x be ? ToIntlMathematicalValue(value).
2140 isolate, x, IntlMathematicalValue::From(isolate, value),
2142
2143 // 5. Return FormatNumeric(nf, x).
2144 Maybe<icu::number::FormattedNumber> maybe_formatted =
2146 MAYBE_RETURN(maybe_formatted, DirectHandle<String>());
2147 icu::number::FormattedNumber formatted =
2148 std::move(maybe_formatted).FromJust();
2149
2150 return FormatToString(isolate, formatted, *fmt, x.IsNaN());
2151}
2152
2154 Isolate* isolate, DirectHandle<JSNumberFormat> number_format,
2155 Handle<Object> numeric_obj) {
2156 icu::number::LocalizedNumberFormatter* fmt =
2157 number_format->icu_number_formatter()->raw();
2158 DCHECK_NOT_NULL(fmt);
2161 isolate, value, IntlMathematicalValue::From(isolate, numeric_obj),
2163
2164 Maybe<icu::number::FormattedNumber> maybe_formatted =
2165 IntlMathematicalValue::FormatNumeric(isolate, *fmt, value);
2166 MAYBE_RETURN(maybe_formatted, DirectHandle<JSArray>());
2167 icu::number::FormattedNumber formatted =
2168 std::move(maybe_formatted).FromJust();
2169
2170 return FormatToJSArray(isolate, formatted, *fmt, value.IsNaN(), false);
2171}
2172
2173// #sec-number-format-functions
2174
2176 Isolate* isolate, DirectHandle<JSNumberFormat> number_format,
2177 Handle<Object> x_obj, Handle<Object> y_obj) {
2178 return PartitionNumberRangePattern<String, FormatToString>(
2179 isolate, number_format, x_obj, y_obj,
2180 "Intl.NumberFormat.prototype.formatRange");
2181}
2182
2184 Isolate* isolate, DirectHandle<JSNumberFormat> number_format,
2185 Handle<Object> x_obj, Handle<Object> y_obj) {
2186 return PartitionNumberRangePattern<JSArray, FormatRangeToJSArray>(
2187 isolate, number_format, x_obj, y_obj,
2188 "Intl.NumberFormat.prototype.formatRangeToParts");
2189}
2190
2191namespace {
2192
2193struct CheckNumberElements {
2194 static const char* key() { return "NumberElements"; }
2195 static const char* path() { return nullptr; }
2196};
2197
2198} // namespace
2199
2200const std::set<std::string>& JSNumberFormat::GetAvailableLocales() {
2202 available_locales = LAZY_INSTANCE_INITIALIZER;
2203 return available_locales.Pointer()->Get();
2204}
2205
2206} // namespace internal
2207} // namespace v8
@ kNumberFormatStyleUnit
Definition v8-isolate.h:554
V8_INLINE T FromJust() const &
Definition v8-maybe.h:64
V8_INLINE bool IsNothing() const
Definition v8-maybe.h:35
static MaybeHandle< String > ToString(Isolate *isolate, DirectHandle< BigInt > bigint, int radix=10, ShouldThrow should_throw=kThrowOnError)
Definition bigint.cc:835
Handle< Number > NewNumberFromInt(int32_t value)
Handle< String > NewStringFromAsciiChecked(const char *str, AllocationType allocation=AllocationType::kYoung)
Handle< JSObject > NewJSObject(DirectHandle< JSFunction > constructor, AllocationType allocation=AllocationType::kYoung, NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:2985
Handle< String > NewStringFromStaticChars(const char(&str)[N], AllocationType allocation=AllocationType::kYoung)
Definition factory-inl.h:79
Maybe< icu::Formattable > ToFormattable(Isolate *isolate) const
static Maybe< icu::number::FormattedNumberRange > FormatRange(Isolate *isolate, const icu::number::LocalizedNumberRangeFormatter &number_range_format, const IntlMathematicalValue &x, const IntlMathematicalValue &y)
static V8_EXPORT_PRIVATE Maybe< IntlMathematicalValue > From(Isolate *isolate, Handle< Object > value)
MaybeHandle< String > ToString(Isolate *isolate) const
V8_EXPORT_PRIVATE bool IsNaN() const
static Maybe< icu::number::FormattedNumber > FormatNumeric(Isolate *isolate, const icu::number::LocalizedNumberFormatter &number_format, const IntlMathematicalValue &x)
static void AddElement(Isolate *isolate, DirectHandle< JSArray > array, int index, DirectHandle< String > field_type_string, DirectHandle< String > value)
static std::string GetNumberingSystem(const icu::Locale &icu_locale)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< Object > LegacyUnwrapReceiver(Isolate *isolate, DirectHandle< JSReceiver > receiver, DirectHandle< JSFunction > constructor, bool has_initialized_slot)
static V8_WARN_UNUSED_RESULT MaybeHandle< String > ToString(Isolate *isolate, const icu::UnicodeString &string)
static Maybe< int > AddNumberElements(Isolate *isolate, const icu::FormattedValue &formatted, DirectHandle< JSArray > result, int start_index, DirectHandle< String > unit)
static Maybe< std::vector< std::string > > CanonicalizeLocaleList(Isolate *isolate, DirectHandle< Object > locales, bool only_return_one_result=false)
static Maybe< ResolvedLocale > ResolveLocale(Isolate *isolate, const std::set< std::string > &available_locales, const std::vector< std::string > &requested_locales, MatcherOption options, const std::set< std::string > &relevant_extension_keys)
static std::set< std::string > SanctionedSimpleUnits()
static V8_WARN_UNUSED_RESULT Maybe< MatcherOption > GetLocaleMatcher(Isolate *isolate, DirectHandle< JSReceiver > options, const char *method_name)
static Maybe< std::string > ToLanguageTag(const icu::Locale &locale)
static V8_WARN_UNUSED_RESULT Maybe< NumberFormatDigitOptions > SetNumberFormatDigitOptions(Isolate *isolate, DirectHandle< JSReceiver > options, int mnfd_default, int mxfd_default, bool notation_is_compact, const char *service)
static bool IsValidNumberingSystem(const std::string &value)
static DirectHandle< String > SourceString(Isolate *isolate, FormatRangeSource source)
static DirectHandle< String > NumberFieldToType(Isolate *isolate, const NumberFormatSpan &part, const icu::UnicodeString &text, bool is_nan)
static V8_EXPORT_PRIVATE const std::set< std::string > & GetAvailableLocales()
static int32_t MinimumIntegerDigitsFromSkeleton(const icu::UnicodeString &skeleton)
static DirectHandle< String > TrailingZeroDisplayString(Isolate *isolate, const icu::UnicodeString &skeleton)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > NumberFormatFunction(Isolate *isolate, DirectHandle< JSNumberFormat > number_format, Handle< Object > numeric_obj)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > FormatNumeric(Isolate *isolate, const icu::number::LocalizedNumberFormatter &number_format, Handle< Object > numeric_obj)
static DirectHandle< Object > RoundingIncrement(Isolate *isolate, const icu::UnicodeString &skeleton)
static DirectHandle< JSObject > ResolvedOptions(Isolate *isolate, DirectHandle< JSNumberFormat > number_format)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSNumberFormat > New(Isolate *isolate, DirectHandle< Map > map, DirectHandle< Object > locales, DirectHandle< Object > options, const char *service)
static const icu::UnicodeString NumberingSystemFromSkeleton(const icu::UnicodeString &skeleton)
static V8_WARN_UNUSED_RESULT Maybe< icu::number::LocalizedNumberRangeFormatter > GetRangeFormatter(Isolate *isolate, Tagged< String > locale, const icu::number::LocalizedNumberFormatter &number_formatter)
static icu::number::UnlocalizedNumberFormatter SetDigitOptionsToFormatter(const icu::number::UnlocalizedNumberFormatter &settings, const Intl::NumberFormatDigitOptions &digit_options)
static DirectHandle< String > RoundingPriorityString(Isolate *isolate, const icu::UnicodeString &skeleton)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSNumberFormat > UnwrapNumberFormat(Isolate *isolate, DirectHandle< JSReceiver > format_holder)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSArray > FormatToParts(Isolate *isolate, DirectHandle< JSNumberFormat > number_format, Handle< Object > numeric_obj)
static bool SignificantDigitsFromSkeleton(const icu::UnicodeString &skeleton, int32_t *minimum, int32_t *maximum)
static bool FractionDigitsFromSkeleton(const icu::UnicodeString &skeleton, int32_t *minimum, int32_t *maximum)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSArray > FormatNumericRangeToParts(Isolate *isolate, DirectHandle< JSNumberFormat > number_format, Handle< Object > x, Handle< Object > y)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > FormatNumericRange(Isolate *isolate, DirectHandle< JSNumberFormat > number_format, Handle< Object > x, Handle< Object > y)
static DirectHandle< String > RoundingModeString(Isolate *isolate, const icu::UnicodeString &skeleton)
static void ValidateElements(Tagged< JSObject > object)
static V8_WARN_UNUSED_RESULT HandleType< Object >::MaybeType ToPrimitive(Isolate *isolate, HandleType< JSReceiver > receiver, ToPrimitiveHint hint=ToPrimitiveHint::kDefault)
static V8_WARN_UNUSED_RESULT Maybe< bool > CreateDataProperty(Isolate *isolate, DirectHandle< JSReceiver > object, DirectHandle< Name > key, DirectHandle< Object > value, Maybe< ShouldThrow > should_throw)
static DirectHandle< Managed< CppType > > From(Isolate *isolate, size_t estimated_size, std::shared_ptr< CppType > shared_ptr, AllocationType allocation_type=AllocationType::kYoung)
Definition managed-inl.h:27
V8_INLINE Handle< T > ToHandleChecked() const
static V8_WARN_UNUSED_RESULT HandleType< Number >::MaybeType ToNumber(Isolate *isolate, HandleType< T > input)
static double NumberValue(Tagged< Number > obj)
static V8_WARN_UNUSED_RESULT Handle< Number > ToNumber(Isolate *isolate, DirectHandle< Oddball > input)
Definition oddball-inl.h:54
static constexpr Tagged< Smi > zero()
Definition smi.h:99
base::Vector< const uint8_t > ToOneByteVector() const
Definition string.h:139
static V8_INLINE HandleType< String > Flatten(Isolate *isolate, HandleType< T > string, AllocationType allocation=AllocationType::kYoung)
const MapRef map_
int start
int end
#define THROW_NEW_ERROR_RETURN_VALUE(isolate, call, value)
Definition isolate.h:300
#define ASSIGN_RETURN_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:291
#define THROW_NEW_ERROR(isolate, call)
Definition isolate.h:307
#define ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)
Definition isolate.h:276
#define MAYBE_RETURN(call, value)
Definition isolate.h:408
#define MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)
Definition isolate.h:440
std::map< const std::string, const std::string > map
icu::number::FormattedNumber formatted
JSDurationFormat::FieldStyle style
#define AVOID_AMBIGUOUS_OP_WARNING(x)
DirectHandle< JSReceiver > options
Precision precision
double increment
RoundingMode rounding_mode
ZoneVector< RpoNumber > & result
#define LAZY_INSTANCE_INITIALIZER
int x
std::priority_queue< BigUnit > units[CompilationTier::kNumTiers]
int r
Definition mul-fft.cc:298
int int32_t
Definition unicode.cc:40
constexpr bool IsInRange(T value, U lower_limit, U higher_limit)
Definition bounds.h:20
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
constexpr double kMaxSafeInteger
Definition globals.h:1985
bool IsNaN(Tagged< Object > obj)
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
bool IsNumber(Tagged< Object > obj)
bool IsWhiteSpaceOrLineTerminator(base::uc32 c)
V8_EXPORT_PRIVATE std::vector< NumberFormatSpan > FlattenRegionsToParts(std::vector< NumberFormatSpan > *regions)
constexpr bool IsDecimalDigit(base::uc32 c)
@ ALLOW_NON_DECIMAL_PREFIX
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in allocation gc speed threshold for starting incremental marking via a task in percent of available threshold for starting incremental marking immediately in percent of available Use a single schedule for determining a marking schedule between JS and C objects schedules the minor GC task with kUserVisible priority max worker number of concurrent for NumberOfWorkerThreads start background threads that allocate memory concurrent_array_buffer_sweeping use parallel threads to clear weak refs in the atomic pause trace progress of the incremental marking trace object counts and memory usage report a tick only when allocated zone memory changes by this amount TracingFlags::gc_stats TracingFlags::gc_stats track native contexts that are expected to be garbage collected verify heap pointers before and after GC memory reducer runs GC with ReduceMemoryFootprint flag Maximum number of memory reducer GCs scheduled Old gen GC speed is computed directly from gc tracer counters Perform compaction on full GCs based on V8 s default heuristics Perform compaction on every full GC Perform code space compaction when finalizing a full GC with stack Stress GC compaction to flush out bugs with moving objects flush of baseline code when it has not been executed recently Use time base code flushing instead of age Use a progress bar to scan large objects in increments when incremental marking is active force incremental marking for small heaps and run it more often force marking at random points between and force scavenge at random points between and reclaim otherwise unreachable unmodified wrapper objects when possible less compaction in non memory reducing mode use high priority threads for concurrent Marking Test mode only flag It allows an unit test to select evacuation candidates use incremental marking for CppHeap cppheap_concurrent_marking c value for membalancer A special constant to balance between memory and space tradeoff The smaller the more memory it uses enable use of SSE4 instructions if available enable use of AVX VNNI instructions if available enable use of POPCNT instruction if available force all emitted branches to be in long enable use of partial constant none
Definition flags.cc:2422
return value
Definition map-inl.h:893
MaybeDirectHandle< JSReceiver > CoerceOptionsToObject(Isolate *isolate, DirectHandle< Object > options, const char *method_name)
constexpr int AsciiAlphaToLower(base::uc32 c)
static V8_WARN_UNUSED_RESULT Maybe< T > GetStringOrBooleanOption(Isolate *isolate, DirectHandle< JSReceiver > options, const char *property, const char *method, const std::vector< const char * > &str_values, const std::vector< T > &enum_values, T true_value, T false_value, T fallback_value)
Maybe< bool > GetStringOption(Isolate *isolate, DirectHandle< JSReceiver > options, const char *property, const std::vector< const char * > &values, const char *method_name, std::unique_ptr< char[]> *result)
double StringToDouble(const char *str, ConversionFlag flags, double empty_string_val)
template const char * string
MaybeHandle< BigInt > StringToBigInt(Isolate *isolate, DirectHandle< String > string)
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
char16_t UChar
Definition string-16.h:22
Maybe< T > Nothing()
Definition v8-maybe.h:112
Maybe< T > Just(const T &t)
Definition v8-maybe.h:117
uint32_t substring
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define CHECK_NOT_NULL(val)
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define USE(...)
Definition macros.h:293
typename LazyStaticInstance< T, CreateTrait, InitOnceTrait, DestroyTrait >::type type
#define NAME(feat,...)
long LONG