Browse Source

Implement prototype of `Intl` built-in (#1622)

<!---
Thank you for contributing to Boa! Please fill out the template below, and remove or add any
information as you feel neccesary.
--->

This pull request is related to #1180.

It changes the following:

- Creates the `Intl` global
- Adds the `Intl.getCanonicalLocales` method

At the moment it does not actually use ICU4X behind the scenes; `Intl.getCanonicalLocales` simply acts as if all the locales passed are canonical locales. This will not be the case in the final PR.


Co-authored-by: RageKnify <RageKnify@gmail.com>
pull/1686/head
hle0 3 years ago
parent
commit
da29677323
  1. 141
      boa/src/builtins/intl/mod.rs
  2. 3
      boa/src/builtins/mod.rs

141
boa/src/builtins/intl/mod.rs

@ -0,0 +1,141 @@
//! This module implements the global `Intl` object.
//!
//! `Intl` is a built-in object that has properties and methods for i18n. It's not a function object.
//!
//! More information:
//! - [ECMAScript reference][spec]
//!
//! [spec]: https://tc39.es/ecma402/#intl-object
use indexmap::IndexSet;
use crate::{
builtins::{Array, BuiltIn, JsArgs},
object::ObjectInitializer,
property::Attribute,
symbol::WellKnownSymbols,
BoaProfiler, Context, JsResult, JsString, JsValue,
};
/// JavaScript `Intl` object.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Intl;
impl BuiltIn for Intl {
const NAME: &'static str = "Intl";
const ATTRIBUTE: Attribute = Attribute::WRITABLE
.union(Attribute::NON_ENUMERABLE)
.union(Attribute::CONFIGURABLE);
fn init(context: &mut Context) -> JsValue {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let string_tag = WellKnownSymbols::to_string_tag();
let object = ObjectInitializer::new(context)
.function(Self::get_canonical_locales, "getCanonicalLocales", 1)
.property(
string_tag,
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build();
object.into()
}
}
impl Intl {
fn canonicalize_locale(locale: &str) -> JsString {
JsString::new(locale)
}
fn canonicalize_locale_list(
args: &[JsValue],
context: &mut Context,
) -> JsResult<Vec<JsString>> {
// https://tc39.es/ecma402/#sec-canonicalizelocalelist
// 1. If locales is undefined, then
let locales = args.get_or_undefined(0);
if locales.is_undefined() {
// a. Return a new empty List.
return Ok(Vec::new());
}
let locales = &args[0];
// 2. Let seen be a new empty List.
let mut seen = IndexSet::new();
// 3. If Type(locales) is String or Type(locales) is Object and locales has an [[InitializedLocale]] internal slot, then
// TODO: check if Type(locales) is object and handle the internal slots
let o = if locales.is_string() {
// a. Let O be CreateArrayFromList(« locales »).
Array::create_array_from_list([locales.clone()], context)
} else {
// 4. Else,
// a. Let O be ? ToObject(locales).
locales.to_object(context)?
};
// 5. Let len be ? ToLength(? Get(O, "length")).
let len = o.length_of_array_like(context)?;
// 6 Let k be 0.
// 7. Repeat, while k < len,
for k in 0..len {
// a. Let Pk be ToString(k).
// b. Let kPresent be ? HasProperty(O, Pk).
let k_present = o.has_property(k, context)?;
// c. If kPresent is true, then
if k_present {
// i. Let kValue be ? Get(O, Pk).
let k_value = o.get(k, context)?;
// ii. If Type(kValue) is not String or Object, throw a TypeError exception.
if !(k_value.is_object() || k_value.is_string()) {
return Err(context
.throw_type_error("locale should be a String or Object")
.unwrap_err());
}
// iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then
// TODO: handle checks for InitializedLocale internal slot (there should be an if statement here)
// 1. Let tag be kValue.[[Locale]].
// iv. Else,
// 1. Let tag be ? ToString(kValue).
let tag = k_value.to_string(context)?;
// v. If IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception.
// TODO: implement `IsStructurallyValidLanguageTag`
// vi. Let canonicalizedTag be CanonicalizeUnicodeLocaleId(tag).
seen.insert(Self::canonicalize_locale(&tag));
// vii. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen.
}
// d. Increase k by 1.
}
// 8. Return seen.
Ok(seen.into_iter().collect::<Vec<JsString>>())
}
/// Returns an array containing the canonical locale names.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN docs][mdn]
///
/// [spec]: https://tc39.es/ecma402/#sec-intl.getcanonicallocales
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales
pub(crate) fn get_canonical_locales(
_: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Let ll be ? CanonicalizeLocaleList(locales).
let ll = Self::canonicalize_locale_list(args, context)?;
// 2. Return CreateArrayFromList(ll).
Ok(JsValue::Object(Array::create_array_from_list(
ll.into_iter().map(|x| x.into()),
context,
)))
}
}

3
boa/src/builtins/mod.rs

@ -12,6 +12,7 @@ pub mod error;
pub mod function; pub mod function;
pub mod global_this; pub mod global_this;
pub mod infinity; pub mod infinity;
pub mod intl;
pub mod intrinsics; pub mod intrinsics;
pub mod iterable; pub mod iterable;
pub mod json; pub mod json;
@ -39,6 +40,7 @@ pub(crate) use self::{
function::BuiltInFunctionObject, function::BuiltInFunctionObject,
global_this::GlobalThis, global_this::GlobalThis,
infinity::Infinity, infinity::Infinity,
intl::Intl,
json::Json, json::Json,
map::map_iterator::MapIterator, map::map_iterator::MapIterator,
map::Map, map::Map,
@ -124,6 +126,7 @@ pub fn init(context: &mut Context) {
BuiltInFunctionObject, BuiltInFunctionObject,
BuiltInObjectObject, BuiltInObjectObject,
Math, Math,
Intl,
Json, Json,
Array, Array,
Proxy, Proxy,

Loading…
Cancel
Save