Browse Source

Transition `Intl` types to `NativeObject` API (#3491)

pull/3172/head
José Julián Espina 1 year ago committed by GitHub
parent
commit
eb2f33e74a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      Cargo.lock
  2. 2
      boa_engine/Cargo.toml
  3. 10
      boa_engine/src/builtins/intl/collator/mod.rs
  4. 6
      boa_engine/src/builtins/intl/date_time_format.rs
  5. 18
      boa_engine/src/builtins/intl/list_format/mod.rs
  6. 34
      boa_engine/src/builtins/intl/locale/mod.rs
  7. 4
      boa_engine/src/builtins/intl/locale/utils.rs
  8. 18
      boa_engine/src/builtins/intl/plural_rules/mod.rs
  9. 10
      boa_engine/src/builtins/intl/segmenter/iterator.rs
  10. 16
      boa_engine/src/builtins/intl/segmenter/mod.rs
  11. 15
      boa_engine/src/builtins/intl/segmenter/segments.rs
  12. 2
      boa_engine/src/builtins/string/mod.rs
  13. 273
      boa_engine/src/object/mod.rs
  14. 9
      boa_gc/Cargo.toml
  15. 21
      boa_gc/src/trace.rs

1
Cargo.lock generated

@ -517,6 +517,7 @@ dependencies = [
"boa_macros", "boa_macros",
"boa_profiler", "boa_profiler",
"hashbrown 0.14.3", "hashbrown 0.14.3",
"icu_locid",
"thin-vec", "thin-vec",
] ]

2
boa_engine/Cargo.toml

@ -57,7 +57,7 @@ js = ["dep:web-time"]
[dependencies] [dependencies]
boa_interner.workspace = true boa_interner.workspace = true
boa_gc = { workspace = true, features = [ "thinvec" ] } boa_gc = { workspace = true, features = [ "thin-vec", "icu" ] }
boa_profiler.workspace = true boa_profiler.workspace = true
boa_macros.workspace = true boa_macros.workspace = true
boa_ast.workspace = true boa_ast.workspace = true

10
boa_engine/src/builtins/intl/collator/mod.rs

@ -42,7 +42,7 @@ mod options;
pub(crate) use options::*; pub(crate) use options::*;
#[derive(Debug)] #[derive(Debug)]
pub struct Collator { pub(crate) struct Collator {
locale: Locale, locale: Locale,
collation: Value, collation: Value,
numeric: bool, numeric: bool,
@ -350,7 +350,7 @@ impl BuiltInConstructor for Collator {
let collator = JsObject::from_proto_and_data_with_shared_shape( let collator = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(), context.root_shape(),
prototype, prototype,
ObjectData::collator(Self { ObjectData::native_object(Self {
locale, locale,
collation, collation,
numeric, numeric,
@ -414,7 +414,7 @@ impl Collator {
})?; })?;
let collator_obj = this.clone(); let collator_obj = this.clone();
let mut collator = this.borrow_mut(); let mut collator = this.borrow_mut();
let collator = collator.as_collator_mut().ok_or_else(|| { let collator = collator.downcast_mut::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`resolvedOptions` can only be called on a `Collator` object") .with_message("`resolvedOptions` can only be called on a `Collator` object")
})?; })?;
@ -436,7 +436,7 @@ impl Collator {
// 2. Assert: Type(collator) is Object and collator has an [[InitializedCollator]] internal slot. // 2. Assert: Type(collator) is Object and collator has an [[InitializedCollator]] internal slot.
let collator = collator.borrow(); let collator = collator.borrow();
let collator = collator let collator = collator
.as_collator() .downcast_ref::<Self>()
.expect("checked above that the object was a collator object"); .expect("checked above that the object was a collator object");
// 3. If x is not provided, let x be undefined. // 3. If x is not provided, let x be undefined.
@ -483,7 +483,7 @@ impl Collator {
JsNativeError::typ() JsNativeError::typ()
.with_message("`resolvedOptions` can only be called on a `Collator` object") .with_message("`resolvedOptions` can only be called on a `Collator` object")
})?; })?;
let collator = collator.as_collator().ok_or_else(|| { let collator = collator.downcast_ref::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`resolvedOptions` can only be called on a `Collator` object") .with_message("`resolvedOptions` can only be called on a `Collator` object")
})?; })?;

6
boa_engine/src/builtins/intl/date_time_format.rs

@ -40,7 +40,7 @@ impl OptionType for HourCycle {
/// JavaScript `Intl.DateTimeFormat` object. /// JavaScript `Intl.DateTimeFormat` object.
#[derive(Debug, Clone, Trace, Finalize)] #[derive(Debug, Clone, Trace, Finalize)]
pub struct DateTimeFormat { pub(crate) struct DateTimeFormat {
initialized_date_time_format: bool, initialized_date_time_format: bool,
locale: JsString, locale: JsString,
calendar: JsString, calendar: JsString,
@ -123,7 +123,7 @@ impl BuiltInConstructor for DateTimeFormat {
let date_time_format = JsObject::from_proto_and_data_with_shared_shape( let date_time_format = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(), context.root_shape(),
prototype, prototype,
ObjectData::date_time_format(Box::new(Self { ObjectData::native_object(Self {
initialized_date_time_format: true, initialized_date_time_format: true,
locale: js_string!("en-US"), locale: js_string!("en-US"),
calendar: js_string!("gregory"), calendar: js_string!("gregory"),
@ -143,7 +143,7 @@ impl BuiltInConstructor for DateTimeFormat {
hour_cycle: js_string!("h24"), hour_cycle: js_string!("h24"),
pattern: js_string!("{hour}:{minute}"), pattern: js_string!("{hour}:{minute}"),
bound_format: js_string!("undefined"), bound_format: js_string!("undefined"),
})), }),
); );
// TODO 3. Perform ? InitializeDateTimeFormat(dateTimeFormat, locales, options). // TODO 3. Perform ? InitializeDateTimeFormat(dateTimeFormat, locales, options).

18
boa_engine/src/builtins/intl/list_format/mod.rs

@ -1,5 +1,6 @@
use std::fmt::Write; use std::fmt::Write;
use boa_gc::{empty_trace, Finalize, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use icu_list::{provider::AndListV1Marker, ListFormatter, ListLength}; use icu_list::{provider::AndListV1Marker, ListFormatter, ListLength};
use icu_locid::Locale; use icu_locid::Locale;
@ -29,14 +30,19 @@ use super::{
mod options; mod options;
pub(crate) use options::*; pub(crate) use options::*;
#[derive(Debug)] #[derive(Debug, Finalize)]
pub struct ListFormat { pub(crate) struct ListFormat {
locale: Locale, locale: Locale,
typ: ListFormatType, typ: ListFormatType,
style: ListLength, style: ListLength,
native: ListFormatter, native: ListFormatter,
} }
// SAFETY: `ListFormat` doesn't contain traceable data.
unsafe impl Trace for ListFormat {
empty_trace!();
}
impl Service for ListFormat { impl Service for ListFormat {
type LangMarker = AndListV1Marker; type LangMarker = AndListV1Marker;
@ -164,7 +170,7 @@ impl BuiltInConstructor for ListFormat {
let list_format = JsObject::from_proto_and_data_with_shared_shape( let list_format = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(), context.root_shape(),
prototype, prototype,
ObjectData::list_format(Self { ObjectData::native_object(Self {
locale, locale,
typ, typ,
style, style,
@ -221,7 +227,7 @@ impl ListFormat {
JsNativeError::typ() JsNativeError::typ()
.with_message("`format` can only be called on a `ListFormat` object") .with_message("`format` can only be called on a `ListFormat` object")
})?; })?;
let lf = lf.as_list_format().ok_or_else(|| { let lf = lf.downcast_ref::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`format` can only be called on a `ListFormat` object") .with_message("`format` can only be called on a `ListFormat` object")
})?; })?;
@ -339,7 +345,7 @@ impl ListFormat {
JsNativeError::typ() JsNativeError::typ()
.with_message("`formatToParts` can only be called on a `ListFormat` object") .with_message("`formatToParts` can only be called on a `ListFormat` object")
})?; })?;
let lf = lf.as_list_format().ok_or_else(|| { let lf = lf.downcast_ref::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`formatToParts` can only be called on a `ListFormat` object") .with_message("`formatToParts` can only be called on a `ListFormat` object")
})?; })?;
@ -413,7 +419,7 @@ impl ListFormat {
JsNativeError::typ() JsNativeError::typ()
.with_message("`resolvedOptions` can only be called on a `ListFormat` object") .with_message("`resolvedOptions` can only be called on a `ListFormat` object")
})?; })?;
let lf = lf.as_list_format().ok_or_else(|| { let lf = lf.downcast_ref::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`resolvedOptions` can only be called on a `ListFormat` object") .with_message("`resolvedOptions` can only be called on a `ListFormat` object")
})?; })?;

34
boa_engine/src/builtins/intl/locale/mod.rs

@ -206,7 +206,7 @@ impl BuiltInConstructor for Locale {
let mut tag = if let Some(tag) = tag let mut tag = if let Some(tag) = tag
.as_object() .as_object()
.and_then(|obj| obj.borrow().as_locale().cloned()) .and_then(|obj| obj.borrow().downcast_ref::<icu_locid::Locale>().cloned())
{ {
// a. Let tag be tag.[[Locale]]. // a. Let tag be tag.[[Locale]].
tag tag
@ -371,7 +371,7 @@ impl BuiltInConstructor for Locale {
let locale = JsObject::from_proto_and_data_with_shared_shape( let locale = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(), context.root_shape(),
prototype, prototype,
ObjectData::locale(tag), ObjectData::native_object(tag),
); );
// 37. Return locale. // 37. Return locale.
@ -398,7 +398,7 @@ impl Locale {
JsNativeError::typ().with_message("`maximize` can only be called on a `Locale` object") JsNativeError::typ().with_message("`maximize` can only be called on a `Locale` object")
})?; })?;
let mut loc = loc let mut loc = loc
.as_locale() .downcast_ref::<icu_locid::Locale>()
.ok_or_else(|| { .ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`maximize` can only be called on a `Locale` object") .with_message("`maximize` can only be called on a `Locale` object")
@ -413,7 +413,7 @@ impl Locale {
Ok(JsObject::from_proto_and_data_with_shared_shape( Ok(JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(), context.root_shape(),
prototype, prototype,
ObjectData::locale(loc), ObjectData::native_object(loc),
) )
.into()) .into())
} }
@ -436,7 +436,7 @@ impl Locale {
JsNativeError::typ().with_message("`minimize` can only be called on a `Locale` object") JsNativeError::typ().with_message("`minimize` can only be called on a `Locale` object")
})?; })?;
let mut loc = loc let mut loc = loc
.as_locale() .downcast_ref::<icu_locid::Locale>()
.ok_or_else(|| { .ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`minimize` can only be called on a `Locale` object") .with_message("`minimize` can only be called on a `Locale` object")
@ -451,7 +451,7 @@ impl Locale {
Ok(JsObject::from_proto_and_data_with_shared_shape( Ok(JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(), context.root_shape(),
prototype, prototype,
ObjectData::locale(loc), ObjectData::native_object(loc),
) )
.into()) .into())
} }
@ -469,7 +469,7 @@ impl Locale {
let loc = this.as_object().map(JsObject::borrow).ok_or_else(|| { let loc = this.as_object().map(JsObject::borrow).ok_or_else(|| {
JsNativeError::typ().with_message("`toString` can only be called on a `Locale` object") JsNativeError::typ().with_message("`toString` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ().with_message("`toString` can only be called on a `Locale` object") JsNativeError::typ().with_message("`toString` can only be called on a `Locale` object")
})?; })?;
@ -491,7 +491,7 @@ impl Locale {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get baseName` can only be called on a `Locale` object") .with_message("`get baseName` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get baseName` can only be called on a `Locale` object") .with_message("`get baseName` can only be called on a `Locale` object")
})?; })?;
@ -515,7 +515,7 @@ impl Locale {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get calendar` can only be called on a `Locale` object") .with_message("`get calendar` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get calendar` can only be called on a `Locale` object") .with_message("`get calendar` can only be called on a `Locale` object")
})?; })?;
@ -544,7 +544,7 @@ impl Locale {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get caseFirst` can only be called on a `Locale` object") .with_message("`get caseFirst` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get caseFirst` can only be called on a `Locale` object") .with_message("`get caseFirst` can only be called on a `Locale` object")
})?; })?;
@ -573,7 +573,7 @@ impl Locale {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get collation` can only be called on a `Locale` object") .with_message("`get collation` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get collation` can only be called on a `Locale` object") .with_message("`get collation` can only be called on a `Locale` object")
})?; })?;
@ -602,7 +602,7 @@ impl Locale {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get hourCycle` can only be called on a `Locale` object") .with_message("`get hourCycle` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get hourCycle` can only be called on a `Locale` object") .with_message("`get hourCycle` can only be called on a `Locale` object")
})?; })?;
@ -631,7 +631,7 @@ impl Locale {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get numeric` can only be called on a `Locale` object") .with_message("`get numeric` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get numeric` can only be called on a `Locale` object") .with_message("`get numeric` can only be called on a `Locale` object")
})?; })?;
@ -668,7 +668,7 @@ impl Locale {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get numberingSystem` can only be called on a `Locale` object") .with_message("`get numberingSystem` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get numberingSystem` can only be called on a `Locale` object") .with_message("`get numberingSystem` can only be called on a `Locale` object")
})?; })?;
@ -697,7 +697,7 @@ impl Locale {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get language` can only be called on a `Locale` object") .with_message("`get language` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get language` can only be called on a `Locale` object") .with_message("`get language` can only be called on a `Locale` object")
})?; })?;
@ -722,7 +722,7 @@ impl Locale {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get script` can only be called on a `Locale` object") .with_message("`get script` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get script` can only be called on a `Locale` object") .with_message("`get script` can only be called on a `Locale` object")
})?; })?;
@ -752,7 +752,7 @@ impl Locale {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get region` can only be called on a `Locale` object") .with_message("`get region` can only be called on a `Locale` object")
})?; })?;
let loc = loc.as_locale().ok_or_else(|| { let loc = loc.downcast_ref::<icu_locid::Locale>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`get region` can only be called on a `Locale` object") .with_message("`get region` can only be called on a `Locale` object")
})?; })?;

4
boa_engine/src/builtins/intl/locale/utils.rs

@ -80,7 +80,7 @@ pub(crate) fn canonicalize_locale_list(
let o = if locales.is_string() let o = if locales.is_string()
|| locales || locales
.as_object() .as_object()
.map_or(false, |o| o.borrow().is_locale()) .map_or(false, |o| o.borrow().is::<Locale>())
{ {
// a. Let O be CreateArrayFromList(« locales »). // a. Let O be CreateArrayFromList(« locales »).
Array::create_array_from_list([locales.clone()], context) Array::create_array_from_list([locales.clone()], context)
@ -113,7 +113,7 @@ pub(crate) fn canonicalize_locale_list(
// iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then // iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then
let mut tag = if let Some(tag) = k_value let mut tag = if let Some(tag) = k_value
.as_object() .as_object()
.and_then(|obj| obj.borrow().as_locale().cloned()) .and_then(|obj| obj.borrow().downcast_ref::<Locale>().cloned())
{ {
// 1. Let tag be kValue.[[Locale]]. // 1. Let tag be kValue.[[Locale]].
tag tag

18
boa_engine/src/builtins/intl/plural_rules/mod.rs

@ -1,5 +1,6 @@
mod options; mod options;
use boa_gc::{empty_trace, Finalize, Trace};
use boa_macros::utf16; use boa_macros::utf16;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use fixed_decimal::FixedDecimal; use fixed_decimal::FixedDecimal;
@ -34,14 +35,19 @@ use super::{
Service, Service,
}; };
#[derive(Debug)] #[derive(Debug, Finalize)]
pub struct PluralRules { pub(crate) struct PluralRules {
locale: Locale, locale: Locale,
native: PluralRulesWithRanges<NativePluralRules>, native: PluralRulesWithRanges<NativePluralRules>,
rule_type: PluralRuleType, rule_type: PluralRuleType,
format_options: DigitFormatOptions, format_options: DigitFormatOptions,
} }
// SAFETY: `PluralRules` doesn't contain any traceable data.
unsafe impl Trace for PluralRules {
empty_trace!();
}
impl Service for PluralRules { impl Service for PluralRules {
type LangMarker = CardinalV1Marker; type LangMarker = CardinalV1Marker;
@ -164,7 +170,7 @@ impl BuiltInConstructor for PluralRules {
Ok(JsObject::from_proto_and_data_with_shared_shape( Ok(JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(), context.root_shape(),
proto, proto,
ObjectData::plural_rules(Self { ObjectData::native_object(Self {
locale, locale,
native, native,
rule_type, rule_type,
@ -192,7 +198,7 @@ impl PluralRules {
JsNativeError::typ() JsNativeError::typ()
.with_message("`select` can only be called on an `Intl.PluralRules` object") .with_message("`select` can only be called on an `Intl.PluralRules` object")
})?; })?;
let plural_rules = plural_rules.as_plural_rules().ok_or_else(|| { let plural_rules = plural_rules.downcast_ref::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`select` can only be called on an `Intl.PluralRules` object") .with_message("`select` can only be called on an `Intl.PluralRules` object")
})?; })?;
@ -219,7 +225,7 @@ impl PluralRules {
JsNativeError::typ() JsNativeError::typ()
.with_message("`select_range` can only be called on an `Intl.PluralRules` object") .with_message("`select_range` can only be called on an `Intl.PluralRules` object")
})?; })?;
let plural_rules = plural_rules.as_plural_rules().ok_or_else(|| { let plural_rules = plural_rules.downcast_ref::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`select_range` can only be called on an `Intl.PluralRules` object") .with_message("`select_range` can only be called on an `Intl.PluralRules` object")
})?; })?;
@ -314,7 +320,7 @@ impl PluralRules {
"`resolved_options` can only be called on an `Intl.PluralRules` object", "`resolved_options` can only be called on an `Intl.PluralRules` object",
) )
})?; })?;
let plural_rules = plural_rules.as_plural_rules().ok_or_else(|| { let plural_rules = plural_rules.downcast_ref::<Self>().ok_or_else(|| {
JsNativeError::typ().with_message( JsNativeError::typ().with_message(
"`resolved_options` can only be called on an `Intl.PluralRules` object", "`resolved_options` can only be called on an `Intl.PluralRules` object",
) )

10
boa_engine/src/builtins/intl/segmenter/iterator.rs

@ -14,7 +14,7 @@ use crate::{
Context, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, Context, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
}; };
use super::create_segment_data_object; use super::{create_segment_data_object, Segmenter};
pub(crate) enum NativeSegmentIterator<'l, 's> { pub(crate) enum NativeSegmentIterator<'l, 's> {
Grapheme(GraphemeClusterBreakIteratorUtf16<'l, 's>), Grapheme(GraphemeClusterBreakIteratorUtf16<'l, 's>),
@ -47,7 +47,7 @@ impl NativeSegmentIterator<'_, '_> {
} }
#[derive(Debug, Trace, Finalize)] #[derive(Debug, Trace, Finalize)]
pub struct SegmentIterator { pub(crate) struct SegmentIterator {
segmenter: JsObject, segmenter: JsObject,
string: JsString, string: JsString,
next_segment_index: usize, next_segment_index: usize,
@ -90,7 +90,7 @@ impl SegmentIterator {
.objects() .objects()
.iterator_prototypes() .iterator_prototypes()
.segment(), .segment(),
ObjectData::segment_iterator(Self { ObjectData::native_object(Self {
segmenter, segmenter,
string, string,
next_segment_index: 0, next_segment_index: 0,
@ -107,7 +107,7 @@ impl SegmentIterator {
JsNativeError::typ() JsNativeError::typ()
.with_message("`next` can only be called on a `Segment Iterator` object") .with_message("`next` can only be called on a `Segment Iterator` object")
})?; })?;
let iter = iter.as_segment_iterator_mut().ok_or_else(|| { let iter = iter.downcast_mut::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`next` can only be called on a `Segment Iterator` object") .with_message("`next` can only be called on a `Segment Iterator` object")
})?; })?;
@ -121,7 +121,7 @@ impl SegmentIterator {
// 3. Let segmenter be iterator.[[IteratingSegmenter]]. // 3. Let segmenter be iterator.[[IteratingSegmenter]].
let segmenter = iter.segmenter.borrow(); let segmenter = iter.segmenter.borrow();
let segmenter = segmenter let segmenter = segmenter
.as_segmenter() .downcast_ref::<Segmenter>()
.expect("segment iterator object should contain a segmenter"); .expect("segment iterator object should contain a segmenter");
let mut segments = segmenter.native.segment(string); let mut segments = segmenter.native.segment(string);
// the first elem is always 0. // the first elem is always 0.

16
boa_engine/src/builtins/intl/segmenter/mod.rs

@ -1,5 +1,6 @@
use std::ops::Range; use std::ops::Range;
use boa_gc::{empty_trace, Finalize, Trace};
use boa_macros::utf16; use boa_macros::utf16;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use icu_locid::Locale; use icu_locid::Locale;
@ -36,12 +37,17 @@ use super::{
Service, Service,
}; };
#[derive(Debug)] #[derive(Debug, Finalize)]
pub struct Segmenter { pub(crate) struct Segmenter {
locale: Locale, locale: Locale,
native: NativeSegmenter, native: NativeSegmenter,
} }
// SAFETY: `Segmenter` doesn't contain any traceable data.
unsafe impl Trace for Segmenter {
empty_trace!();
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum NativeSegmenter { pub(crate) enum NativeSegmenter {
Grapheme(Box<GraphemeClusterSegmenter>), Grapheme(Box<GraphemeClusterSegmenter>),
@ -177,7 +183,7 @@ impl BuiltInConstructor for Segmenter {
let segmenter = JsObject::from_proto_and_data_with_shared_shape( let segmenter = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(), context.root_shape(),
proto, proto,
ObjectData::segmenter(segmenter), ObjectData::native_object(segmenter),
); );
// 14. Return segmenter. // 14. Return segmenter.
@ -230,7 +236,7 @@ impl Segmenter {
JsNativeError::typ() JsNativeError::typ()
.with_message("`resolved_options` can only be called on an `Intl.Segmenter` object") .with_message("`resolved_options` can only be called on an `Intl.Segmenter` object")
})?; })?;
let segmenter = segmenter.as_segmenter().ok_or_else(|| { let segmenter = segmenter.downcast_ref::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`resolved_options` can only be called on an `Intl.Segmenter` object") .with_message("`resolved_options` can only be called on an `Intl.Segmenter` object")
})?; })?;
@ -268,7 +274,7 @@ impl Segmenter {
// 2. Perform ? RequireInternalSlot(segmenter, [[InitializedSegmenter]]). // 2. Perform ? RequireInternalSlot(segmenter, [[InitializedSegmenter]]).
let segmenter = this let segmenter = this
.as_object() .as_object()
.filter(|o| o.borrow().is_segmenter()) .filter(|o| o.borrow().is::<Self>())
.ok_or_else(|| { .ok_or_else(|| {
JsNativeError::typ().with_message( JsNativeError::typ().with_message(
"`resolved_options` can only be called on an `Intl.Segmenter` object", "`resolved_options` can only be called on an `Intl.Segmenter` object",

15
boa_engine/src/builtins/intl/segmenter/segments.rs

@ -11,10 +11,10 @@ use crate::{
Context, JsArgs, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, Context, JsArgs, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
}; };
use super::{create_segment_data_object, SegmentIterator}; use super::{create_segment_data_object, SegmentIterator, Segmenter};
#[derive(Debug, Trace, Finalize)] #[derive(Debug, Trace, Finalize)]
pub struct Segments { pub(crate) struct Segments {
segmenter: JsObject, segmenter: JsObject,
string: JsString, string: JsString,
} }
@ -44,9 +44,10 @@ impl Segments {
// 3. Set segments.[[SegmentsSegmenter]] to segmenter. // 3. Set segments.[[SegmentsSegmenter]] to segmenter.
// 4. Set segments.[[SegmentsString]] to string. // 4. Set segments.[[SegmentsString]] to string.
// 5. Return segments. // 5. Return segments.
JsObject::from_proto_and_data( JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
context.intrinsics().objects().segments_prototype(), context.intrinsics().objects().segments_prototype(),
ObjectData::segments(Self { segmenter, string }), ObjectData::native_object(Self { segmenter, string }),
) )
} }
@ -60,7 +61,7 @@ impl Segments {
JsNativeError::typ() JsNativeError::typ()
.with_message("`containing` can only be called on a `Segments` object") .with_message("`containing` can only be called on a `Segments` object")
})?; })?;
let segments = segments.as_segments().ok_or_else(|| { let segments = segments.downcast_ref::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`containing` can only be called on a `Segments` object") .with_message("`containing` can only be called on a `Segments` object")
})?; })?;
@ -68,7 +69,7 @@ impl Segments {
// 3. Let segmenter be segments.[[SegmentsSegmenter]]. // 3. Let segmenter be segments.[[SegmentsSegmenter]].
let segmenter = segments.segmenter.borrow(); let segmenter = segments.segmenter.borrow();
let segmenter = segmenter let segmenter = segmenter
.as_segmenter() .downcast_ref::<Segmenter>()
.expect("segments object should contain a segmenter"); .expect("segments object should contain a segmenter");
// 4. Let string be segments.[[SegmentsString]]. // 4. Let string be segments.[[SegmentsString]].
@ -115,7 +116,7 @@ impl Segments {
JsNativeError::typ() JsNativeError::typ()
.with_message("`containing` can only be called on a `Segments` object") .with_message("`containing` can only be called on a `Segments` object")
})?; })?;
let segments = segments.as_segments().ok_or_else(|| { let segments = segments.downcast_ref::<Self>().ok_or_else(|| {
JsNativeError::typ() JsNativeError::typ()
.with_message("`containing` can only be called on a `Segments` object") .with_message("`containing` can only be called on a `Segments` object")
})?; })?;

2
boa_engine/src/builtins/string/mod.rs

@ -1425,7 +1425,7 @@ impl String {
.map(JsObject::borrow) .map(JsObject::borrow)
.expect("constructor must return a JsObject"); .expect("constructor must return a JsObject");
let collator = collator let collator = collator
.as_collator() .downcast_ref::<crate::builtins::intl::Collator>()
.expect("constructor must return a `Collator` object") .expect("constructor must return a `Collator` object")
.collator(); .collator();

273
boa_engine/src/object/mod.rs

@ -30,14 +30,6 @@ use self::{
}, },
shape::Shape, shape::Shape,
}; };
#[cfg(feature = "intl")]
use crate::builtins::intl::{
collator::Collator,
date_time_format::DateTimeFormat,
list_format::ListFormat,
plural_rules::PluralRules,
segmenter::{SegmentIterator, Segmenter, Segments},
};
#[cfg(feature = "temporal")] #[cfg(feature = "temporal")]
use crate::builtins::temporal::{ use crate::builtins::temporal::{
Calendar, Duration, Instant, PlainDate, PlainDateTime, PlainMonthDay, PlainTime, Calendar, Duration, Instant, PlainDate, PlainDateTime, PlainMonthDay, PlainTime,
@ -128,8 +120,18 @@ pub trait NativeObject: Any + Trace {
/// Convert the Rust type which implements `NativeObject` to a `&mut dyn Any`. /// Convert the Rust type which implements `NativeObject` to a `&mut dyn Any`.
fn as_mut_any(&mut self) -> &mut dyn Any; fn as_mut_any(&mut self) -> &mut dyn Any;
/// Gets the type name of the value.
fn type_name_of_value(&self) -> &'static str {
fn name_of_val<T: ?Sized>(_val: &T) -> &'static str {
std::any::type_name::<T>()
}
name_of_val(self)
}
} }
// TODO: Use super trait casting in Rust 1.75
impl<T: Any + Trace> NativeObject for T { impl<T: Any + Trace> NativeObject for T {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
@ -140,6 +142,7 @@ impl<T: Any + Trace> NativeObject for T {
} }
} }
// TODO: Use super trait casting in Rust 1.75
impl dyn NativeObject { impl dyn NativeObject {
/// Returns `true` if the inner type is the same as `T`. /// Returns `true` if the inner type is the same as `T`.
#[inline] #[inline]
@ -409,37 +412,6 @@ pub enum ObjectKind {
/// The `ModuleNamespace` object kind. /// The `ModuleNamespace` object kind.
ModuleNamespace(ModuleNamespace), ModuleNamespace(ModuleNamespace),
/// The `Intl.Collator` object kind.
#[cfg(feature = "intl")]
Collator(Box<Collator>),
/// The `Intl.DateTimeFormat` object kind.
#[cfg(feature = "intl")]
DateTimeFormat(Box<DateTimeFormat>),
/// The `Intl.ListFormat` object kind.
#[cfg(feature = "intl")]
ListFormat(Box<ListFormat>),
/// The `Intl.Locale` object kind.
#[cfg(feature = "intl")]
Locale(Box<icu_locid::Locale>),
/// The `Intl.Segmenter` object kind.
#[cfg(feature = "intl")]
Segmenter(Segmenter),
/// The `Segments` object kind.
#[cfg(feature = "intl")]
Segments(Segments),
/// The `Segment Iterator` object kind.
#[cfg(feature = "intl")]
SegmentIterator(SegmentIterator),
/// The `PluralRules` object kind.
#[cfg(feature = "intl")]
PluralRules(Box<PluralRules>),
/// The `Temporal.Instant` object kind. /// The `Temporal.Instant` object kind.
#[cfg(feature = "temporal")] #[cfg(feature = "temporal")]
Instant(Instant), Instant(Instant),
@ -509,19 +481,6 @@ unsafe impl Trace for ObjectKind {
Self::WeakMap(wm) => mark(wm), Self::WeakMap(wm) => mark(wm),
Self::WeakSet(ws) => mark(ws), Self::WeakSet(ws) => mark(ws),
Self::ModuleNamespace(m) => mark(m), Self::ModuleNamespace(m) => mark(m),
#[cfg(feature = "intl")]
Self::DateTimeFormat(f) => mark(f),
#[cfg(feature = "intl")]
Self::Collator(co) => mark(co),
#[cfg(feature = "intl")]
Self::Segments(seg) => mark(seg),
#[cfg(feature = "intl")]
Self::SegmentIterator(it) => mark(it),
#[cfg(feature = "intl")]
Self::ListFormat(_)
| Self::Locale(_)
| Self::Segmenter(_)
| Self::PluralRules(_) => {}
Self::RegExp(_) Self::RegExp(_)
| Self::BigInt(_) | Self::BigInt(_)
| Self::Boolean(_) | Self::Boolean(_)
@ -907,86 +866,6 @@ impl ObjectData {
} }
} }
/// Create the `Collator` object data
#[cfg(feature = "intl")]
#[must_use]
pub fn collator(date_time_fmt: Collator) -> Self {
Self {
kind: ObjectKind::Collator(Box::new(date_time_fmt)),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
/// Create the `DateTimeFormat` object data
#[cfg(feature = "intl")]
#[must_use]
pub fn date_time_format(date_time_fmt: Box<DateTimeFormat>) -> Self {
Self {
kind: ObjectKind::DateTimeFormat(date_time_fmt),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
/// Create the `ListFormat` object data
#[cfg(feature = "intl")]
#[must_use]
pub fn list_format(list_format: ListFormat) -> Self {
Self {
kind: ObjectKind::ListFormat(Box::new(list_format)),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
/// Create the `Locale` object data
#[cfg(feature = "intl")]
#[must_use]
pub fn locale(locale: icu_locid::Locale) -> Self {
Self {
kind: ObjectKind::Locale(Box::new(locale)),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
/// Create the `Segmenter` object data
#[cfg(feature = "intl")]
#[must_use]
pub fn segmenter(segmenter: Segmenter) -> Self {
Self {
kind: ObjectKind::Segmenter(segmenter),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
/// Create the `Segments` object data
#[cfg(feature = "intl")]
#[must_use]
pub fn segments(segments: Segments) -> Self {
Self {
kind: ObjectKind::Segments(segments),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
/// Create the `SegmentIterator` object data
#[cfg(feature = "intl")]
#[must_use]
pub fn segment_iterator(segment_iterator: SegmentIterator) -> Self {
Self {
kind: ObjectKind::SegmentIterator(segment_iterator),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
/// Create the `PluralRules` object data
#[cfg(feature = "intl")]
#[must_use]
pub fn plural_rules(plural_rules: PluralRules) -> Self {
Self {
kind: ObjectKind::PluralRules(Box::new(plural_rules)),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
/// Create the `Instant` object data /// Create the `Instant` object data
#[cfg(feature = "temporal")] #[cfg(feature = "temporal")]
#[must_use] #[must_use]
@ -1119,7 +998,6 @@ impl Debug for ObjectKind {
Self::Date(_) => "Date", Self::Date(_) => "Date",
Self::Global => "Global", Self::Global => "Global",
Self::Arguments(_) => "Arguments", Self::Arguments(_) => "Arguments",
Self::NativeObject(_) => "NativeObject",
Self::IntegerIndexed(_) => "TypedArray", Self::IntegerIndexed(_) => "TypedArray",
Self::DataView(_) => "DataView", Self::DataView(_) => "DataView",
Self::Promise(_) => "Promise", Self::Promise(_) => "Promise",
@ -1127,22 +1005,7 @@ impl Debug for ObjectKind {
Self::WeakMap(_) => "WeakMap", Self::WeakMap(_) => "WeakMap",
Self::WeakSet(_) => "WeakSet", Self::WeakSet(_) => "WeakSet",
Self::ModuleNamespace(_) => "ModuleNamespace", Self::ModuleNamespace(_) => "ModuleNamespace",
#[cfg(feature = "intl")] Self::NativeObject(obj) => (**obj).type_name_of_value(),
Self::Collator(_) => "Collator",
#[cfg(feature = "intl")]
Self::DateTimeFormat(_) => "DateTimeFormat",
#[cfg(feature = "intl")]
Self::ListFormat(_) => "ListFormat",
#[cfg(feature = "intl")]
Self::Locale(_) => "Locale",
#[cfg(feature = "intl")]
Self::Segmenter(_) => "Segmenter",
#[cfg(feature = "intl")]
Self::Segments(_) => "Segments",
#[cfg(feature = "intl")]
Self::SegmentIterator(_) => "SegmentIterator",
#[cfg(feature = "intl")]
Self::PluralRules(_) => "PluralRules",
#[cfg(feature = "temporal")] #[cfg(feature = "temporal")]
Self::Instant(_) => "Instant", Self::Instant(_) => "Instant",
#[cfg(feature = "temporal")] #[cfg(feature = "temporal")]
@ -2045,118 +1908,6 @@ impl Object {
} }
} }
/// Gets the `Collator` data if the object is a `Collator`.
#[inline]
#[must_use]
#[cfg(feature = "intl")]
pub const fn as_collator(&self) -> Option<&Collator> {
match self.kind {
ObjectKind::Collator(ref collator) => Some(collator),
_ => None,
}
}
/// Gets a mutable reference to the `Collator` data if the object is a `Collator`.
#[inline]
#[cfg(feature = "intl")]
pub fn as_collator_mut(&mut self) -> Option<&mut Collator> {
match self.kind {
ObjectKind::Collator(ref mut collator) => Some(collator),
_ => None,
}
}
/// Checks if it is a `Locale` object.
#[inline]
#[must_use]
#[cfg(feature = "intl")]
pub const fn is_locale(&self) -> bool {
matches!(self.kind, ObjectKind::Locale(_))
}
/// Gets the `Locale` data if the object is a `Locale`.
#[inline]
#[must_use]
#[cfg(feature = "intl")]
pub const fn as_locale(&self) -> Option<&icu_locid::Locale> {
match self.kind {
ObjectKind::Locale(ref locale) => Some(locale),
_ => None,
}
}
/// Gets the `ListFormat` data if the object is a `ListFormat`.
#[inline]
#[must_use]
#[cfg(feature = "intl")]
pub const fn as_list_format(&self) -> Option<&ListFormat> {
match self.kind {
ObjectKind::ListFormat(ref lf) => Some(lf),
_ => None,
}
}
/// Checks if it is a `Segmenter` object.
#[inline]
#[must_use]
#[cfg(feature = "intl")]
pub const fn is_segmenter(&self) -> bool {
matches!(self.kind, ObjectKind::Segmenter(_))
}
/// Gets the `Segmenter` data if the object is a `Segmenter`.
#[inline]
#[must_use]
#[cfg(feature = "intl")]
pub const fn as_segmenter(&self) -> Option<&Segmenter> {
match self.kind {
ObjectKind::Segmenter(ref seg) => Some(seg),
_ => None,
}
}
/// Gets the `Segments` data if the object is a `Segments`.
#[inline]
#[must_use]
#[cfg(feature = "intl")]
pub const fn as_segments(&self) -> Option<&Segments> {
match self.kind {
ObjectKind::Segments(ref seg) => Some(seg),
_ => None,
}
}
/// Gets the `SegmentIterator` data if the object is a `SegmentIterator`.
#[inline]
#[cfg(feature = "intl")]
pub fn as_segment_iterator_mut(&mut self) -> Option<&mut SegmentIterator> {
match &mut self.kind {
ObjectKind::SegmentIterator(it) => Some(it),
_ => None,
}
}
/// Gets the `PluralRules` data if the object is a `PluralRules`.
#[inline]
#[must_use]
#[cfg(feature = "intl")]
pub const fn as_plural_rules(&self) -> Option<&PluralRules> {
match &self.kind {
ObjectKind::PluralRules(it) => Some(it),
_ => None,
}
}
/// Gets a mutable reference to the `PluralRules` data if the object is a `PluralRules`.
#[inline]
#[cfg(feature = "intl")]
pub fn as_plural_rules_mut(&mut self) -> Option<&mut PluralRules> {
match &mut self.kind {
ObjectKind::PluralRules(plural_rules) => Some(plural_rules),
_ => None,
}
}
/// Gets the `TimeZone` data if the object is a `Temporal.TimeZone`. /// Gets the `TimeZone` data if the object is a `Temporal.TimeZone`.
#[inline] #[inline]
#[must_use] #[must_use]

9
boa_gc/Cargo.toml

@ -11,15 +11,18 @@ repository.workspace = true
rust-version.workspace = true rust-version.workspace = true
[features] [features]
# Enable default implementatio of trace and finalize thin-vec crate # Enable default implementations of trace and finalize for the thin-vec crate
thinvec = ["thin-vec"] thin-vec = ["dep:thin-vec"]
# Enable default implementations of trace and finalize for some `ICU4X` types
icu = ["dep:icu_locid"]
[dependencies] [dependencies]
boa_profiler.workspace = true boa_profiler.workspace = true
boa_macros.workspace = true boa_macros.workspace = true
hashbrown = { workspace = true, features = ["ahash", "raw"] }
thin-vec = { workspace = true, optional = true } thin-vec = { workspace = true, optional = true }
hashbrown = { workspace = true, features = ["ahash", "raw"] } icu_locid = { workspace = true, optional = true }
[lints] [lints]
workspace = true workspace = true

21
boa_gc/src/trace.rs

@ -438,3 +438,24 @@ unsafe impl<T: Trace> Trace for Cell<Option<T>> {
} }
}); });
} }
#[cfg(feature = "icu")]
mod icu {
use icu_locid::{LanguageIdentifier, Locale};
use crate::{Finalize, Trace};
impl Finalize for LanguageIdentifier {}
// SAFETY: `LanguageIdentifier` doesn't have any traceable data.
unsafe impl Trace for LanguageIdentifier {
empty_trace!();
}
impl Finalize for Locale {}
// SAFETY: `LanguageIdentifier` doesn't have any traceable data.
unsafe impl Trace for Locale {
empty_trace!();
}
}

Loading…
Cancel
Save