NumberFormat.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  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 = canonicalize_locale_list(global_object, locales_value);
  315. if (vm.exception())
  316. return {};
  317. // 2. Set options to ? CoerceOptionsToObject(options).
  318. auto* options = coerce_options_to_object(global_object, options_value);
  319. if (vm.exception())
  320. return {};
  321. // 3. Let opt be a new Record.
  322. LocaleOptions opt {};
  323. // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit").
  324. auto matcher = get_option(global_object, *options, vm.names.localeMatcher, Value::Type::String, { "lookup"sv, "best fit"sv }, "best fit"sv);
  325. if (vm.exception())
  326. return {};
  327. // 5. Set opt.[[localeMatcher]] to matcher.
  328. opt.locale_matcher = matcher;
  329. // 6. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined).
  330. auto numbering_system = get_option(global_object, *options, vm.names.numberingSystem, Value::Type::String, {}, Empty {});
  331. if (vm.exception())
  332. return {};
  333. // 7. If numberingSystem is not undefined, then
  334. if (!numbering_system.is_undefined()) {
  335. // a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
  336. if (!Unicode::is_type_identifier(numbering_system.as_string().string())) {
  337. vm.throw_exception<RangeError>(global_object, ErrorType::OptionIsNotValidValue, numbering_system, "numberingSystem"sv);
  338. return {};
  339. }
  340. // 8. Set opt.[[nu]] to numberingSystem.
  341. opt.nu = numbering_system.as_string().string();
  342. }
  343. // 9. Let localeData be %NumberFormat%.[[LocaleData]].
  344. // 10. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]], localeData).
  345. auto result = resolve_locale(requested_locales, opt, NumberFormat::relevant_extension_keys());
  346. // 11. Set numberFormat.[[Locale]] to r.[[locale]].
  347. number_format.set_locale(move(result.locale));
  348. // 12. Set numberFormat.[[DataLocale]] to r.[[dataLocale]].
  349. number_format.set_data_locale(move(result.data_locale));
  350. // 13. Set numberFormat.[[NumberingSystem]] to r.[[nu]].
  351. number_format.set_numbering_system(result.nu.release_value());
  352. // 14. Perform ? SetNumberFormatUnitOptions(numberFormat, options).
  353. set_number_format_unit_options(global_object, number_format, *options);
  354. if (vm.exception())
  355. return {};
  356. // 15. Let style be numberFormat.[[Style]].
  357. auto style = number_format.style();
  358. int default_min_fraction_digits = 0;
  359. int default_max_fraction_digits = 0;
  360. // 16. If style is "currency", then
  361. if (style == NumberFormat::Style::Currency) {
  362. // a. Let currency be numberFormat.[[Currency]].
  363. auto const& currency = number_format.currency();
  364. // b. Let cDigits be CurrencyDigits(currency).
  365. int digits = currency_digits(currency);
  366. // c. Let mnfdDefault be cDigits.
  367. default_min_fraction_digits = digits;
  368. // d. Let mxfdDefault be cDigits.
  369. default_max_fraction_digits = digits;
  370. }
  371. // 17. Else,
  372. else {
  373. // a. Let mnfdDefault be 0.
  374. default_min_fraction_digits = 0;
  375. // b. If style is "percent", then
  376. // i. Let mxfdDefault be 0.
  377. // c. Else,
  378. // i. Let mxfdDefault be 3.
  379. default_max_fraction_digits = style == NumberFormat::Style::Percent ? 0 : 3;
  380. }
  381. // 18. Let notation be ? GetOption(options, "notation", "string", « "standard", "scientific", "engineering", "compact" », "standard").
  382. auto notation = get_option(global_object, *options, vm.names.notation, Value::Type::String, { "standard"sv, "scientific"sv, "engineering"sv, "compact"sv }, "standard"sv);
  383. if (vm.exception())
  384. return {};
  385. // 19. Set numberFormat.[[Notation]] to notation.
  386. number_format.set_notation(notation.as_string().string());
  387. // 20. Perform ? SetNumberFormatDigitOptions(numberFormat, options, mnfdDefault, mxfdDefault, notation).
  388. set_number_format_digit_options(global_object, number_format, *options, default_min_fraction_digits, default_max_fraction_digits, number_format.notation());
  389. if (vm.exception())
  390. return {};
  391. // 21. Let compactDisplay be ? GetOption(options, "compactDisplay", "string", « "short", "long" », "short").
  392. auto compact_display = get_option(global_object, *options, vm.names.compactDisplay, Value::Type::String, { "short"sv, "long"sv }, "short"sv);
  393. if (vm.exception())
  394. return {};
  395. // 22. If notation is "compact", then
  396. if (number_format.notation() == NumberFormat::Notation::Compact) {
  397. // a. Set numberFormat.[[CompactDisplay]] to compactDisplay.
  398. number_format.set_compact_display(compact_display.as_string().string());
  399. }
  400. // 23. Let useGrouping be ? GetOption(options, "useGrouping", "boolean", undefined, true).
  401. auto use_grouping = get_option(global_object, *options, vm.names.useGrouping, Value::Type::Boolean, {}, true);
  402. if (vm.exception())
  403. return {};
  404. // 24. Set numberFormat.[[UseGrouping]] to useGrouping.
  405. number_format.set_use_grouping(use_grouping.as_bool());
  406. // 25. Let signDisplay be ? GetOption(options, "signDisplay", "string", « "auto", "never", "always", "exceptZero" », "auto").
  407. auto sign_display = get_option(global_object, *options, vm.names.signDisplay, Value::Type::String, { "auto"sv, "never"sv, "always"sv, "exceptZero"sv }, "auto"sv);
  408. if (vm.exception())
  409. return {};
  410. // 26. Set numberFormat.[[SignDisplay]] to signDisplay.
  411. number_format.set_sign_display(sign_display.as_string().string());
  412. // 27. Return numberFormat.
  413. return &number_format;
  414. }
  415. // 15.1.3 CurrencyDigits ( currency ), https://tc39.es/ecma402/#sec-currencydigits
  416. int currency_digits(StringView currency)
  417. {
  418. // 1. If the ISO 4217 currency and funds code list contains currency as an alphabetic code, return the minor
  419. // unit value corresponding to the currency from the list; otherwise, return 2.
  420. if (auto currency_code = Unicode::get_currency_code(currency); currency_code.has_value())
  421. return currency_code->minor_unit.value_or(2);
  422. return 2;
  423. }
  424. // 15.1.13 SetNumberFormatUnitOptions ( intlObj, options ), https://tc39.es/ecma402/#sec-setnumberformatunitoptions
  425. void set_number_format_unit_options(GlobalObject& global_object, NumberFormat& intl_object, Object const& options)
  426. {
  427. auto& vm = global_object.vm();
  428. // 1. Assert: Type(intlObj) is Object.
  429. // 2. Assert: Type(options) is Object.
  430. // 3. Let style be ? GetOption(options, "style", "string", « "decimal", "percent", "currency", "unit" », "decimal").
  431. auto style = get_option(global_object, options, vm.names.style, Value::Type::String, { "decimal"sv, "percent"sv, "currency"sv, "unit"sv }, "decimal"sv);
  432. if (vm.exception())
  433. return;
  434. // 4. Set intlObj.[[Style]] to style.
  435. intl_object.set_style(style.as_string().string());
  436. // 5. Let currency be ? GetOption(options, "currency", "string", undefined, undefined).
  437. auto currency = get_option(global_object, options, vm.names.currency, Value::Type::String, {}, Empty {});
  438. if (vm.exception())
  439. return;
  440. // 6. If currency is undefined, then
  441. if (currency.is_undefined()) {
  442. // a. If style is "currency", throw a TypeError exception.
  443. if (intl_object.style() == NumberFormat::Style::Currency) {
  444. vm.throw_exception<TypeError>(global_object, ErrorType::IntlOptionUndefined, "currency"sv, "style"sv, style);
  445. return;
  446. }
  447. }
  448. // 7. Else,
  449. // a. If the result of IsWellFormedCurrencyCode(currency) is false, throw a RangeError exception.
  450. else if (!is_well_formed_currency_code(currency.as_string().string())) {
  451. vm.throw_exception<RangeError>(global_object, ErrorType::OptionIsNotValidValue, currency, "currency"sv);
  452. return;
  453. }
  454. // 8. Let currencyDisplay be ? GetOption(options, "currencyDisplay", "string", « "code", "symbol", "narrowSymbol", "name" », "symbol").
  455. auto currency_display = get_option(global_object, options, vm.names.currencyDisplay, Value::Type::String, { "code"sv, "symbol"sv, "narrowSymbol"sv, "name"sv }, "symbol"sv);
  456. if (vm.exception())
  457. return;
  458. // 9. Let currencySign be ? GetOption(options, "currencySign", "string", « "standard", "accounting" », "standard").
  459. auto currency_sign = get_option(global_object, options, vm.names.currencySign, Value::Type::String, { "standard"sv, "accounting"sv }, "standard"sv);
  460. if (vm.exception())
  461. return;
  462. // 10. Let unit be ? GetOption(options, "unit", "string", undefined, undefined).
  463. auto unit = get_option(global_object, options, vm.names.unit, Value::Type::String, {}, Empty {});
  464. if (vm.exception())
  465. return;
  466. // 11. If unit is undefined, then
  467. if (unit.is_undefined()) {
  468. // a. If style is "unit", throw a TypeError exception.
  469. if (intl_object.style() == NumberFormat::Style::Unit) {
  470. vm.throw_exception<TypeError>(global_object, ErrorType::IntlOptionUndefined, "unit"sv, "style"sv, style);
  471. return;
  472. }
  473. }
  474. // 12. Else,
  475. // a. If the result of IsWellFormedUnitIdentifier(unit) is false, throw a RangeError exception.
  476. else if (!is_well_formed_unit_identifier(unit.as_string().string())) {
  477. vm.throw_exception<RangeError>(global_object, ErrorType::OptionIsNotValidValue, unit, "unit"sv);
  478. return;
  479. }
  480. // 13. Let unitDisplay be ? GetOption(options, "unitDisplay", "string", « "short", "narrow", "long" », "short").
  481. auto unit_display = get_option(global_object, options, vm.names.unitDisplay, Value::Type::String, { "short"sv, "narrow"sv, "long"sv }, "short"sv);
  482. if (vm.exception())
  483. return;
  484. // 14. If style is "currency", then
  485. if (intl_object.style() == NumberFormat::Style::Currency) {
  486. // a. Let currency be the result of converting currency to upper case as specified in 6.1.
  487. // b. Set intlObj.[[Currency]] to currency.
  488. intl_object.set_currency(currency.as_string().string().to_uppercase());
  489. // c. Set intlObj.[[CurrencyDisplay]] to currencyDisplay.
  490. intl_object.set_currency_display(currency_display.as_string().string());
  491. // d. Set intlObj.[[CurrencySign]] to currencySign.
  492. intl_object.set_currency_sign(currency_sign.as_string().string());
  493. }
  494. // 15. If style is "unit", then
  495. if (intl_object.style() == NumberFormat::Style::Unit) {
  496. // a. Set intlObj.[[Unit]] to unit.
  497. intl_object.set_unit(unit.as_string().string());
  498. // b. Set intlObj.[[UnitDisplay]] to unitDisplay.
  499. intl_object.set_unit_display(unit_display.as_string().string());
  500. }
  501. }
  502. }