NumberFormat.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. /*
  2. * Copyright (c) 2021, Tim Flynn <trflynn89@pm.me>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/Runtime/GlobalObject.h>
  7. #include <LibJS/Runtime/Intl/AbstractOperations.h>
  8. #include <LibJS/Runtime/Intl/NumberFormat.h>
  9. #include <LibUnicode/CurrencyCode.h>
  10. #include <LibUnicode/Locale.h>
  11. namespace JS::Intl {
  12. Vector<StringView> const& NumberFormat::relevant_extension_keys()
  13. {
  14. // 15.3.3 Internal slots, https://tc39.es/ecma402/#sec-intl.numberformat-internal-slots
  15. // The value of the [[RelevantExtensionKeys]] internal slot is « "nu" ».
  16. static Vector<StringView> relevant_extension_keys { "nu"sv };
  17. return relevant_extension_keys;
  18. }
  19. // 15 NumberFormat Objects, https://tc39.es/ecma402/#numberformat-objects
  20. NumberFormat::NumberFormat(Object& prototype)
  21. : Object(prototype)
  22. {
  23. }
  24. void NumberFormat::set_style(StringView style)
  25. {
  26. if (style == "decimal"sv)
  27. m_style = Style::Decimal;
  28. else if (style == "percent"sv)
  29. m_style = Style::Percent;
  30. else if (style == "currency"sv)
  31. m_style = Style::Currency;
  32. else if (style == "unit"sv)
  33. m_style = Style::Unit;
  34. else
  35. VERIFY_NOT_REACHED();
  36. }
  37. StringView NumberFormat::style_string() const
  38. {
  39. switch (m_style) {
  40. case Style::Decimal:
  41. return "decimal"sv;
  42. case Style::Percent:
  43. return "percent"sv;
  44. case Style::Currency:
  45. return "currency"sv;
  46. case Style::Unit:
  47. return "unit"sv;
  48. default:
  49. VERIFY_NOT_REACHED();
  50. }
  51. }
  52. void NumberFormat::set_currency_display(StringView currency_display)
  53. {
  54. if (currency_display == "code"sv)
  55. m_currency_display = CurrencyDisplay::Code;
  56. else if (currency_display == "symbol"sv)
  57. m_currency_display = CurrencyDisplay::Symbol;
  58. else if (currency_display == "narrowSymbol"sv)
  59. m_currency_display = CurrencyDisplay::NarrowSymbol;
  60. else if (currency_display == "name"sv)
  61. m_currency_display = CurrencyDisplay::Name;
  62. else
  63. VERIFY_NOT_REACHED();
  64. }
  65. StringView NumberFormat::currency_display_string() const
  66. {
  67. VERIFY(m_currency_display.has_value());
  68. switch (*m_currency_display) {
  69. case CurrencyDisplay::Code:
  70. return "code"sv;
  71. case CurrencyDisplay::Symbol:
  72. return "symbol"sv;
  73. case CurrencyDisplay::NarrowSymbol:
  74. return "narrowSymbol"sv;
  75. case CurrencyDisplay::Name:
  76. return "name"sv;
  77. default:
  78. VERIFY_NOT_REACHED();
  79. }
  80. }
  81. void NumberFormat::set_currency_sign(StringView currency_sign)
  82. {
  83. if (currency_sign == "standard"sv)
  84. m_currency_sign = CurrencySign::Standard;
  85. else if (currency_sign == "accounting"sv)
  86. m_currency_sign = CurrencySign::Accounting;
  87. else
  88. VERIFY_NOT_REACHED();
  89. }
  90. StringView NumberFormat::currency_sign_string() const
  91. {
  92. VERIFY(m_currency_sign.has_value());
  93. switch (*m_currency_sign) {
  94. case CurrencySign::Standard:
  95. return "standard"sv;
  96. case CurrencySign::Accounting:
  97. return "accounting"sv;
  98. default:
  99. VERIFY_NOT_REACHED();
  100. }
  101. }
  102. void NumberFormat::set_unit_display(StringView unit_display)
  103. {
  104. if (unit_display == "short"sv)
  105. m_unit_display = UnitDisplay::Short;
  106. else if (unit_display == "narrow"sv)
  107. m_unit_display = UnitDisplay::Narrow;
  108. else if (unit_display == "long"sv)
  109. m_unit_display = UnitDisplay::Long;
  110. else
  111. VERIFY_NOT_REACHED();
  112. }
  113. StringView NumberFormat::unit_display_string() const
  114. {
  115. VERIFY(m_unit_display.has_value());
  116. switch (*m_unit_display) {
  117. case UnitDisplay::Short:
  118. return "short"sv;
  119. case UnitDisplay::Narrow:
  120. return "narrow"sv;
  121. case UnitDisplay::Long:
  122. return "long"sv;
  123. default:
  124. VERIFY_NOT_REACHED();
  125. }
  126. }
  127. StringView NumberFormat::rounding_type_string() const
  128. {
  129. switch (m_rounding_type) {
  130. case RoundingType::SignificantDigits:
  131. return "significantDigits"sv;
  132. case RoundingType::FractionDigits:
  133. return "fractionDigits"sv;
  134. case RoundingType::CompactRounding:
  135. return "compactRounding"sv;
  136. default:
  137. VERIFY_NOT_REACHED();
  138. }
  139. }
  140. void NumberFormat::set_notation(StringView notation)
  141. {
  142. if (notation == "standard"sv)
  143. m_notation = Notation::Standard;
  144. else if (notation == "scientific"sv)
  145. m_notation = Notation::Scientific;
  146. else if (notation == "engineering"sv)
  147. m_notation = Notation::Engineering;
  148. else if (notation == "compact"sv)
  149. m_notation = Notation::Compact;
  150. else
  151. VERIFY_NOT_REACHED();
  152. }
  153. StringView NumberFormat::notation_string() const
  154. {
  155. switch (m_notation) {
  156. case Notation::Standard:
  157. return "standard"sv;
  158. case Notation::Scientific:
  159. return "scientific"sv;
  160. case Notation::Engineering:
  161. return "engineering"sv;
  162. case Notation::Compact:
  163. return "compact"sv;
  164. default:
  165. VERIFY_NOT_REACHED();
  166. }
  167. }
  168. void NumberFormat::set_compact_display(StringView compact_display)
  169. {
  170. if (compact_display == "short"sv)
  171. m_compact_display = CompactDisplay::Short;
  172. else if (compact_display == "long"sv)
  173. m_compact_display = CompactDisplay::Long;
  174. else
  175. VERIFY_NOT_REACHED();
  176. }
  177. StringView NumberFormat::compact_display_string() const
  178. {
  179. VERIFY(m_compact_display.has_value());
  180. switch (*m_compact_display) {
  181. case CompactDisplay::Short:
  182. return "short"sv;
  183. case CompactDisplay::Long:
  184. return "long"sv;
  185. default:
  186. VERIFY_NOT_REACHED();
  187. }
  188. }
  189. void NumberFormat::set_sign_display(StringView sign_display)
  190. {
  191. if (sign_display == "auto"sv)
  192. m_sign_display = SignDisplay::Auto;
  193. else if (sign_display == "never"sv)
  194. m_sign_display = SignDisplay::Never;
  195. else if (sign_display == "always"sv)
  196. m_sign_display = SignDisplay::Always;
  197. else if (sign_display == "exceptZero"sv)
  198. m_sign_display = SignDisplay::ExceptZero;
  199. else
  200. VERIFY_NOT_REACHED();
  201. }
  202. StringView NumberFormat::sign_display_string() const
  203. {
  204. switch (m_sign_display) {
  205. case SignDisplay::Auto:
  206. return "auto"sv;
  207. case SignDisplay::Never:
  208. return "never"sv;
  209. case SignDisplay::Always:
  210. return "always"sv;
  211. case SignDisplay::ExceptZero:
  212. return "exceptZero"sv;
  213. default:
  214. VERIFY_NOT_REACHED();
  215. }
  216. }
  217. // 15.1.1 SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault, notation ), https://tc39.es/ecma402/#sec-setnfdigitoptions
  218. void set_number_format_digit_options(GlobalObject& global_object, NumberFormat& intl_object, Object const& options, int default_min_fraction_digits, int default_max_fraction_digits, NumberFormat::Notation notation)
  219. {
  220. auto& vm = global_object.vm();
  221. // 1. Assert: Type(intlObj) is Object.
  222. // 2. Assert: Type(options) is Object.
  223. // 3. Assert: Type(mnfdDefault) is Number.
  224. // 4. Assert: Type(mxfdDefault) is Number.
  225. // 5. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21, 1).
  226. auto min_integer_digits = get_number_option(global_object, options, vm.names.minimumIntegerDigits, 1, 21, 1);
  227. if (vm.exception())
  228. return;
  229. // 6. Let mnfd be ? Get(options, "minimumFractionDigits").
  230. auto min_fraction_digits = options.get(vm.names.minimumFractionDigits);
  231. if (vm.exception())
  232. return;
  233. // 7. Let mxfd be ? Get(options, "maximumFractionDigits").
  234. auto max_fraction_digits = options.get(vm.names.maximumFractionDigits);
  235. if (vm.exception())
  236. return;
  237. // 8. Let mnsd be ? Get(options, "minimumSignificantDigits").
  238. auto min_significant_digits = options.get(vm.names.minimumSignificantDigits);
  239. if (vm.exception())
  240. return;
  241. // 9. Let mxsd be ? Get(options, "maximumSignificantDigits").
  242. auto max_significant_digits = options.get(vm.names.maximumSignificantDigits);
  243. if (vm.exception())
  244. return;
  245. // 10. Set intlObj.[[MinimumIntegerDigits]] to mnid.
  246. intl_object.set_min_integer_digits(*min_integer_digits);
  247. // 11. If mnsd is not undefined or mxsd is not undefined, then
  248. if (!min_significant_digits.is_undefined() || !max_significant_digits.is_undefined()) {
  249. // a. Set intlObj.[[RoundingType]] to significantDigits.
  250. intl_object.set_rounding_type(NumberFormat::RoundingType::SignificantDigits);
  251. // b. Let mnsd be ? DefaultNumberOption(mnsd, 1, 21, 1).
  252. auto min_digits = default_number_option(global_object, min_significant_digits, 1, 21, 1);
  253. if (vm.exception())
  254. return;
  255. // c. Let mxsd be ? DefaultNumberOption(mxsd, mnsd, 21, 21).
  256. auto max_digits = default_number_option(global_object, max_significant_digits, *min_digits, 21, 21);
  257. if (vm.exception())
  258. return;
  259. // d. Set intlObj.[[MinimumSignificantDigits]] to mnsd.
  260. intl_object.set_min_significant_digits(*min_digits);
  261. // e. Set intlObj.[[MaximumSignificantDigits]] to mxsd.
  262. intl_object.set_max_significant_digits(*max_digits);
  263. }
  264. // 12. Else if mnfd is not undefined or mxfd is not undefined, then
  265. else if (!min_fraction_digits.is_undefined() || !max_fraction_digits.is_undefined()) {
  266. // a. Set intlObj.[[RoundingType]] to fractionDigits.
  267. intl_object.set_rounding_type(NumberFormat::RoundingType::FractionDigits);
  268. // b. Let mnfd be ? DefaultNumberOption(mnfd, 0, 20, undefined).
  269. auto min_digits = default_number_option(global_object, min_fraction_digits, 0, 20, {});
  270. if (vm.exception())
  271. return;
  272. // c. Let mxfd be ? DefaultNumberOption(mxfd, 0, 20, undefined).
  273. auto max_digits = default_number_option(global_object, max_fraction_digits, 0, 20, {});
  274. if (vm.exception())
  275. return;
  276. // d. If mnfd is undefined, set mnfd to min(mnfdDefault, mxfd).
  277. if (!min_digits.has_value()) {
  278. min_digits = min(default_min_fraction_digits, *max_digits);
  279. }
  280. // e. Else if mxfd is undefined, set mxfd to max(mxfdDefault, mnfd).
  281. else if (!max_digits.has_value()) {
  282. max_digits = max(default_max_fraction_digits, *min_digits);
  283. }
  284. // f. Else if mnfd is greater than mxfd, throw a RangeError exception.
  285. else if (*min_digits > *max_digits) {
  286. vm.throw_exception<RangeError>(global_object, ErrorType::IntlMinimumExceedsMaximum, *min_digits, *max_digits);
  287. return;
  288. }
  289. // g. Set intlObj.[[MinimumFractionDigits]] to mnfd.
  290. intl_object.set_min_fraction_digits(*min_digits);
  291. // h. Set intlObj.[[MaximumFractionDigits]] to mxfd.
  292. intl_object.set_max_fraction_digits(*max_digits);
  293. }
  294. // 13. Else if notation is "compact", then
  295. else if (notation == NumberFormat::Notation::Compact) {
  296. // a. Set intlObj.[[RoundingType]] to compactRounding.
  297. intl_object.set_rounding_type(NumberFormat::RoundingType::CompactRounding);
  298. }
  299. // 14. Else,
  300. else {
  301. // a. Set intlObj.[[RoundingType]] to fractionDigits.
  302. intl_object.set_rounding_type(NumberFormat::RoundingType::FractionDigits);
  303. // b. Set intlObj.[[MinimumFractionDigits]] to mnfdDefault.
  304. intl_object.set_min_fraction_digits(default_min_fraction_digits);
  305. // c. Set intlObj.[[MaximumFractionDigits]] to mxfdDefault.
  306. intl_object.set_max_fraction_digits(default_max_fraction_digits);
  307. }
  308. }
  309. // 15.1.2 InitializeNumberFormat ( numberFormat, locales, options ), https://tc39.es/ecma402/#sec-initializenumberformat
  310. NumberFormat* initialize_number_format(GlobalObject& global_object, NumberFormat& number_format, Value locales_value, Value options_value)
  311. {
  312. auto& vm = global_object.vm();
  313. // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
  314. auto requested_locales = TRY_OR_DISCARD(canonicalize_locale_list(global_object, locales_value));
  315. // 2. Set options to ? CoerceOptionsToObject(options).
  316. auto* options = TRY_OR_DISCARD(coerce_options_to_object(global_object, options_value));
  317. // 3. Let opt be a new Record.
  318. LocaleOptions opt {};
  319. // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit").
  320. auto matcher = TRY_OR_DISCARD(get_option(global_object, *options, vm.names.localeMatcher, Value::Type::String, { "lookup"sv, "best fit"sv }, "best fit"sv));
  321. // 5. Set opt.[[localeMatcher]] to matcher.
  322. opt.locale_matcher = matcher;
  323. // 6. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined).
  324. auto numbering_system = TRY_OR_DISCARD(get_option(global_object, *options, vm.names.numberingSystem, Value::Type::String, {}, Empty {}));
  325. // 7. If numberingSystem is not undefined, then
  326. if (!numbering_system.is_undefined()) {
  327. // a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
  328. if (!Unicode::is_type_identifier(numbering_system.as_string().string())) {
  329. vm.throw_exception<RangeError>(global_object, ErrorType::OptionIsNotValidValue, numbering_system, "numberingSystem"sv);
  330. return {};
  331. }
  332. // 8. Set opt.[[nu]] to numberingSystem.
  333. opt.nu = numbering_system.as_string().string();
  334. }
  335. // 9. Let localeData be %NumberFormat%.[[LocaleData]].
  336. // 10. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]], localeData).
  337. auto result = resolve_locale(requested_locales, opt, NumberFormat::relevant_extension_keys());
  338. // 11. Set numberFormat.[[Locale]] to r.[[locale]].
  339. number_format.set_locale(move(result.locale));
  340. // 12. Set numberFormat.[[DataLocale]] to r.[[dataLocale]].
  341. number_format.set_data_locale(move(result.data_locale));
  342. // 13. Set numberFormat.[[NumberingSystem]] to r.[[nu]].
  343. number_format.set_numbering_system(result.nu.release_value());
  344. // 14. Perform ? SetNumberFormatUnitOptions(numberFormat, options).
  345. set_number_format_unit_options(global_object, number_format, *options);
  346. if (vm.exception())
  347. return {};
  348. // 15. Let style be numberFormat.[[Style]].
  349. auto style = number_format.style();
  350. int default_min_fraction_digits = 0;
  351. int default_max_fraction_digits = 0;
  352. // 16. If style is "currency", then
  353. if (style == NumberFormat::Style::Currency) {
  354. // a. Let currency be numberFormat.[[Currency]].
  355. auto const& currency = number_format.currency();
  356. // b. Let cDigits be CurrencyDigits(currency).
  357. int digits = currency_digits(currency);
  358. // c. Let mnfdDefault be cDigits.
  359. default_min_fraction_digits = digits;
  360. // d. Let mxfdDefault be cDigits.
  361. default_max_fraction_digits = digits;
  362. }
  363. // 17. Else,
  364. else {
  365. // a. Let mnfdDefault be 0.
  366. default_min_fraction_digits = 0;
  367. // b. If style is "percent", then
  368. // i. Let mxfdDefault be 0.
  369. // c. Else,
  370. // i. Let mxfdDefault be 3.
  371. default_max_fraction_digits = style == NumberFormat::Style::Percent ? 0 : 3;
  372. }
  373. // 18. Let notation be ? GetOption(options, "notation", "string", « "standard", "scientific", "engineering", "compact" », "standard").
  374. auto notation = TRY_OR_DISCARD(get_option(global_object, *options, vm.names.notation, Value::Type::String, { "standard"sv, "scientific"sv, "engineering"sv, "compact"sv }, "standard"sv));
  375. // 19. Set numberFormat.[[Notation]] to notation.
  376. number_format.set_notation(notation.as_string().string());
  377. // 20. Perform ? SetNumberFormatDigitOptions(numberFormat, options, mnfdDefault, mxfdDefault, notation).
  378. set_number_format_digit_options(global_object, number_format, *options, default_min_fraction_digits, default_max_fraction_digits, number_format.notation());
  379. if (vm.exception())
  380. return {};
  381. // 21. Let compactDisplay be ? GetOption(options, "compactDisplay", "string", « "short", "long" », "short").
  382. auto compact_display = TRY_OR_DISCARD(get_option(global_object, *options, vm.names.compactDisplay, Value::Type::String, { "short"sv, "long"sv }, "short"sv));
  383. // 22. If notation is "compact", then
  384. if (number_format.notation() == NumberFormat::Notation::Compact) {
  385. // a. Set numberFormat.[[CompactDisplay]] to compactDisplay.
  386. number_format.set_compact_display(compact_display.as_string().string());
  387. }
  388. // 23. Let useGrouping be ? GetOption(options, "useGrouping", "boolean", undefined, true).
  389. auto use_grouping = TRY_OR_DISCARD(get_option(global_object, *options, vm.names.useGrouping, Value::Type::Boolean, {}, true));
  390. // 24. Set numberFormat.[[UseGrouping]] to useGrouping.
  391. number_format.set_use_grouping(use_grouping.as_bool());
  392. // 25. Let signDisplay be ? GetOption(options, "signDisplay", "string", « "auto", "never", "always", "exceptZero" », "auto").
  393. auto sign_display = TRY_OR_DISCARD(get_option(global_object, *options, vm.names.signDisplay, Value::Type::String, { "auto"sv, "never"sv, "always"sv, "exceptZero"sv }, "auto"sv));
  394. // 26. Set numberFormat.[[SignDisplay]] to signDisplay.
  395. number_format.set_sign_display(sign_display.as_string().string());
  396. // 27. Return numberFormat.
  397. return &number_format;
  398. }
  399. // 15.1.3 CurrencyDigits ( currency ), https://tc39.es/ecma402/#sec-currencydigits
  400. int currency_digits(StringView currency)
  401. {
  402. // 1. If the ISO 4217 currency and funds code list contains currency as an alphabetic code, return the minor
  403. // unit value corresponding to the currency from the list; otherwise, return 2.
  404. if (auto currency_code = Unicode::get_currency_code(currency); currency_code.has_value())
  405. return currency_code->minor_unit.value_or(2);
  406. return 2;
  407. }
  408. // 15.1.13 SetNumberFormatUnitOptions ( intlObj, options ), https://tc39.es/ecma402/#sec-setnumberformatunitoptions
  409. void set_number_format_unit_options(GlobalObject& global_object, NumberFormat& intl_object, Object const& options)
  410. {
  411. auto& vm = global_object.vm();
  412. // 1. Assert: Type(intlObj) is Object.
  413. // 2. Assert: Type(options) is Object.
  414. // 3. Let style be ? GetOption(options, "style", "string", « "decimal", "percent", "currency", "unit" », "decimal").
  415. auto style_or_error = get_option(global_object, options, vm.names.style, Value::Type::String, { "decimal"sv, "percent"sv, "currency"sv, "unit"sv }, "decimal"sv);
  416. if (style_or_error.is_error())
  417. return;
  418. auto style = style_or_error.release_value();
  419. // 4. Set intlObj.[[Style]] to style.
  420. intl_object.set_style(style.as_string().string());
  421. // 5. Let currency be ? GetOption(options, "currency", "string", undefined, undefined).
  422. auto currency_or_error = get_option(global_object, options, vm.names.currency, Value::Type::String, {}, Empty {});
  423. if (currency_or_error.is_error())
  424. return;
  425. auto currency = currency_or_error.release_value();
  426. // 6. If currency is undefined, then
  427. if (currency.is_undefined()) {
  428. // a. If style is "currency", throw a TypeError exception.
  429. if (intl_object.style() == NumberFormat::Style::Currency) {
  430. vm.throw_exception<TypeError>(global_object, ErrorType::IntlOptionUndefined, "currency"sv, "style"sv, style);
  431. return;
  432. }
  433. }
  434. // 7. Else,
  435. // a. If the result of IsWellFormedCurrencyCode(currency) is false, throw a RangeError exception.
  436. else if (!is_well_formed_currency_code(currency.as_string().string())) {
  437. vm.throw_exception<RangeError>(global_object, ErrorType::OptionIsNotValidValue, currency, "currency"sv);
  438. return;
  439. }
  440. // 8. Let currencyDisplay be ? GetOption(options, "currencyDisplay", "string", « "code", "symbol", "narrowSymbol", "name" », "symbol").
  441. auto currency_display_or_error = get_option(global_object, options, vm.names.currencyDisplay, Value::Type::String, { "code"sv, "symbol"sv, "narrowSymbol"sv, "name"sv }, "symbol"sv);
  442. if (currency_display_or_error.is_error())
  443. return;
  444. auto currency_display = currency_display_or_error.release_value();
  445. // 9. Let currencySign be ? GetOption(options, "currencySign", "string", « "standard", "accounting" », "standard").
  446. auto currency_sign_or_error = get_option(global_object, options, vm.names.currencySign, Value::Type::String, { "standard"sv, "accounting"sv }, "standard"sv);
  447. if (currency_sign_or_error.is_error())
  448. return;
  449. auto currency_sign = currency_sign_or_error.release_value();
  450. // 10. Let unit be ? GetOption(options, "unit", "string", undefined, undefined).
  451. auto unit_or_error = get_option(global_object, options, vm.names.unit, Value::Type::String, {}, Empty {});
  452. if (unit_or_error.is_error())
  453. return;
  454. auto unit = unit_or_error.release_value();
  455. // 11. If unit is undefined, then
  456. if (unit.is_undefined()) {
  457. // a. If style is "unit", throw a TypeError exception.
  458. if (intl_object.style() == NumberFormat::Style::Unit) {
  459. vm.throw_exception<TypeError>(global_object, ErrorType::IntlOptionUndefined, "unit"sv, "style"sv, style);
  460. return;
  461. }
  462. }
  463. // 12. Else,
  464. // a. If the result of IsWellFormedUnitIdentifier(unit) is false, throw a RangeError exception.
  465. else if (!is_well_formed_unit_identifier(unit.as_string().string())) {
  466. vm.throw_exception<RangeError>(global_object, ErrorType::OptionIsNotValidValue, unit, "unit"sv);
  467. return;
  468. }
  469. // 13. Let unitDisplay be ? GetOption(options, "unitDisplay", "string", « "short", "narrow", "long" », "short").
  470. auto unit_display_or_error = get_option(global_object, options, vm.names.unitDisplay, Value::Type::String, { "short"sv, "narrow"sv, "long"sv }, "short"sv);
  471. if (unit_display_or_error.is_error())
  472. return;
  473. auto unit_display = unit_display_or_error.release_value();
  474. // 14. If style is "currency", then
  475. if (intl_object.style() == NumberFormat::Style::Currency) {
  476. // a. Let currency be the result of converting currency to upper case as specified in 6.1.
  477. // b. Set intlObj.[[Currency]] to currency.
  478. intl_object.set_currency(currency.as_string().string().to_uppercase());
  479. // c. Set intlObj.[[CurrencyDisplay]] to currencyDisplay.
  480. intl_object.set_currency_display(currency_display.as_string().string());
  481. // d. Set intlObj.[[CurrencySign]] to currencySign.
  482. intl_object.set_currency_sign(currency_sign.as_string().string());
  483. }
  484. // 15. If style is "unit", then
  485. if (intl_object.style() == NumberFormat::Style::Unit) {
  486. // a. Set intlObj.[[Unit]] to unit.
  487. intl_object.set_unit(unit.as_string().string());
  488. // b. Set intlObj.[[UnitDisplay]] to unitDisplay.
  489. intl_object.set_unit_display(unit_display.as_string().string());
  490. }
  491. }
  492. }