diff --git a/core/engine/src/builtins/intl/collator/mod.rs b/core/engine/src/builtins/intl/collator/mod.rs index a6520efeb7..3394c34ff9 100644 --- a/core/engine/src/builtins/intl/collator/mod.rs +++ b/core/engine/src/builtins/intl/collator/mod.rs @@ -17,8 +17,8 @@ use crate::{ OrdinaryObject, }, context::{ + icu::IntlProvider, intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, - BoaProvider, }, js_string, native_function::NativeFunction, @@ -79,7 +79,7 @@ impl Service for Collator { type LocaleOptions = CollatorLocaleOptions; - fn resolve(locale: &mut Locale, options: &mut Self::LocaleOptions, provider: &BoaProvider) { + fn resolve(locale: &mut Locale, options: &mut Self::LocaleOptions, provider: &IntlProvider) { let collation = options .collation .take() @@ -274,8 +274,11 @@ impl BuiltInConstructor for Collator { // 18. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]]. // 19. Let r be ResolveLocale(%Collator%.[[AvailableLocales]], requestedLocales, opt, relevantExtensionKeys, localeData). - let mut locale = - resolve_locale::(&requested_locales, &mut intl_options, context.icu()); + let mut locale = resolve_locale::( + &requested_locales, + &mut intl_options, + context.intl_provider(), + ); let collator_locale = { // `collator_locale` needs to be different from the resolved locale because ECMA402 doesn't @@ -332,7 +335,7 @@ impl BuiltInConstructor for Collator { .unzip(); let collator = - NativeCollator::try_new_unstable(context.icu().provider(), &collator_locale, { + NativeCollator::try_new_unstable(context.intl_provider(), &collator_locale, { let mut options = icu_collator::CollatorOptions::new(); options.strength = strength; options.case_level = case_level; diff --git a/core/engine/src/builtins/intl/list_format/mod.rs b/core/engine/src/builtins/intl/list_format/mod.rs index f126706818..55c2105322 100644 --- a/core/engine/src/builtins/intl/list_format/mod.rs +++ b/core/engine/src/builtins/intl/list_format/mod.rs @@ -126,7 +126,7 @@ impl BuiltInConstructor for ListFormat { matcher, ..Default::default() }, - context.icu(), + context.intl_provider(), ); // 11. Let type be ? GetOption(options, "type", string, « "conjunction", "disjunction", "unit" », "conjunction"). @@ -144,17 +144,17 @@ impl BuiltInConstructor for ListFormat { let data_locale = DataLocale::from(&locale); let formatter = match typ { ListFormatType::Conjunction => ListFormatter::try_new_and_with_length_unstable( - context.icu().provider(), + context.intl_provider(), &data_locale, style, ), ListFormatType::Disjunction => ListFormatter::try_new_or_with_length_unstable( - context.icu().provider(), + context.intl_provider(), &data_locale, style, ), ListFormatType::Unit => ListFormatter::try_new_unit_with_length_unstable( - context.icu().provider(), + context.intl_provider(), &data_locale, style, ), diff --git a/core/engine/src/builtins/intl/locale/mod.rs b/core/engine/src/builtins/intl/locale/mod.rs index 15fd7f1b66..8f5a288e66 100644 --- a/core/engine/src/builtins/intl/locale/mod.rs +++ b/core/engine/src/builtins/intl/locale/mod.rs @@ -249,7 +249,10 @@ impl BuiltInConstructor for Locale { let region = get_option(options, utf16!("region"), context)?; // 10. Set tag to ! CanonicalizeUnicodeLocaleId(tag). - context.icu().locale_canonicalizer().canonicalize(&mut tag); + context + .intl_provider() + .locale_canonicalizer() + .canonicalize(&mut tag); // Skipping some boilerplate since this is easier to do using the `Locale` type, but putting the // spec for completion. @@ -280,7 +283,10 @@ impl BuiltInConstructor for Locale { } // 17. Return ! CanonicalizeUnicodeLocaleId(tag). - context.icu().locale_canonicalizer().canonicalize(&mut tag); + context + .intl_provider() + .locale_canonicalizer() + .canonicalize(&mut tag); } // 12. Let opt be a new Record. @@ -363,7 +369,10 @@ impl BuiltInConstructor for Locale { tag.extensions.unicode.keywords.set(key!("nu"), nu); } - context.icu().locale_canonicalizer().canonicalize(&mut tag); + context + .intl_provider() + .locale_canonicalizer() + .canonicalize(&mut tag); // 6. Let locale be ? OrdinaryCreateFromConstructor(NewTarget, "%Locale.prototype%", internalSlotsList). let prototype = @@ -403,7 +412,7 @@ impl Locale { .clone(); // 3. Let maximal be the result of the Add Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set maximal to loc.[[Locale]]. - context.icu().locale_expander().maximize(&mut loc); + context.intl_provider().locale_expander().maximize(&mut loc); // 4. Return ! Construct(%Locale%, maximal). let prototype = context.intrinsics().constructors().locale().prototype(); @@ -439,7 +448,7 @@ impl Locale { .clone(); // 3. Let minimal be the result of the Remove Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set minimal to loc.[[Locale]]. - context.icu().locale_expander().minimize(&mut loc); + context.intl_provider().locale_expander().minimize(&mut loc); // 4. Return ! Construct(%Locale%, minimal). let prototype = context.intrinsics().constructors().locale().prototype(); diff --git a/core/engine/src/builtins/intl/locale/tests.rs b/core/engine/src/builtins/intl/locale/tests.rs index 7c200786e1..969c9cb456 100644 --- a/core/engine/src/builtins/intl/locale/tests.rs +++ b/core/engine/src/builtins/intl/locale/tests.rs @@ -15,7 +15,7 @@ use crate::{ options::{IntlOptions, LocaleMatcher}, Service, }, - context::icu::{BoaProvider, Icu, StaticProviderAdapter}, + context::icu::IntlProvider, }; #[derive(Debug)] @@ -30,7 +30,7 @@ impl Service for TestService { type LocaleOptions = TestOptions; - fn resolve(locale: &mut Locale, options: &mut Self::LocaleOptions, provider: &BoaProvider) { + fn resolve(locale: &mut Locale, options: &mut Self::LocaleOptions, provider: &IntlProvider) { let loc_hc = locale .extensions .unicode @@ -73,11 +73,8 @@ impl Service for TestService { #[test] fn locale_resolution() { - let icu = Icu::new(BoaProvider::Buffer(Box::new(StaticProviderAdapter( - boa_icu_provider::buffer(), - )))) - .unwrap(); - let mut default = default_locale(icu.locale_canonicalizer()); + let provider = IntlProvider::try_new_with_buffer_provider(boa_icu_provider::buffer()).unwrap(); + let mut default = default_locale(provider.locale_canonicalizer()); default .extensions .unicode @@ -91,7 +88,7 @@ fn locale_resolution() { hc: Some(HourCycle::H11), }, }; - let locale = resolve_locale::(&[], &mut options, &icu); + let locale = resolve_locale::(&[], &mut options, &provider); assert_eq!(locale, default); // test best fit @@ -102,10 +99,10 @@ fn locale_resolution() { }, }; - let locale = resolve_locale::(&[], &mut options, &icu); + let locale = resolve_locale::(&[], &mut options, &provider); let best = best_locale_for_provider::<::LangMarker>( default.id.clone(), - icu.provider(), + &provider, ) .unwrap(); let mut best = Locale::from(best); @@ -118,6 +115,6 @@ fn locale_resolution() { service_options: TestOptions { hc: None }, }; - let locale = resolve_locale::(&[locale!("es-AR")], &mut options, &icu); + let locale = resolve_locale::(&[locale!("es-AR")], &mut options, &provider); assert_eq!(locale, "es-u-hc-h23".parse().unwrap()); } diff --git a/core/engine/src/builtins/intl/locale/utils.rs b/core/engine/src/builtins/intl/locale/utils.rs index 16194b07f8..423908cd46 100644 --- a/core/engine/src/builtins/intl/locale/utils.rs +++ b/core/engine/src/builtins/intl/locale/utils.rs @@ -7,7 +7,7 @@ use crate::{ options::get_option, Array, }, - context::{icu::Icu, BoaProvider}, + context::icu::IntlProvider, js_string, object::JsObject, string::utf16, @@ -138,7 +138,10 @@ pub(crate) fn canonicalize_locale_list( }; // vi. Let canonicalizedTag be CanonicalizeUnicodeLocaleId(tag). - context.icu().locale_canonicalizer().canonicalize(&mut tag); + context + .intl_provider() + .locale_canonicalizer() + .canonicalize(&mut tag); // vii. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen. seen.insert(tag); @@ -314,9 +317,12 @@ pub(crate) fn best_locale_for_provider( /// in order to see if a certain [`Locale`] is supported. /// /// [spec]: https://tc39.es/ecma402/#sec-lookupmatcher -fn lookup_matcher(requested_locales: &[Locale], icu: &Icu) -> Locale +fn lookup_matcher( + requested_locales: &[Locale], + provider: &IntlProvider, +) -> Locale where - BoaProvider: DataProvider, + IntlProvider: DataProvider, { // 1. Let result be a new Record. // 2. For each element locale of requestedLocales, do @@ -329,7 +335,7 @@ where locale.extensions.private.clear(); // b. Let availableLocale be ! BestAvailableLocale(availableLocales, noExtensionsLocale). - let available_locale = best_available_locale::(id, icu.provider()); + let available_locale = best_available_locale::(id, provider); // c. If availableLocale is not undefined, then if let Some(available_locale) = available_locale { @@ -349,7 +355,7 @@ where // 3. Let defLocale be ! DefaultLocale(). // 4. Set result.[[locale]] to defLocale. // 5. Return result. - default_locale(icu.locale_canonicalizer()) + default_locale(provider.locale_canonicalizer()) } /// Abstract operation [`BestFitMatcher ( availableLocales, requestedLocales )`][spec] @@ -361,22 +367,25 @@ where /// produced by the `LookupMatcher` abstract operation. /// /// [spec]: https://tc39.es/ecma402/#sec-bestfitmatcher -fn best_fit_matcher(requested_locales: &[Locale], icu: &Icu) -> Locale +fn best_fit_matcher( + requested_locales: &[Locale], + provider: &IntlProvider, +) -> Locale where - BoaProvider: DataProvider, + IntlProvider: DataProvider, { for mut locale in requested_locales .iter() .cloned() .chain(std::iter::once_with(|| { - default_locale(icu.locale_canonicalizer()) + default_locale(provider.locale_canonicalizer()) })) { let id = std::mem::take(&mut locale.id); locale.extensions.transform.clear(); locale.extensions.private.clear(); - if let Some(available) = best_locale_for_provider(id, icu.provider()) { + if let Some(available) = best_locale_for_provider(id, provider) { locale.id = available; return locale; @@ -399,11 +408,11 @@ where pub(in crate::builtins::intl) fn resolve_locale( requested_locales: &[Locale], options: &mut IntlOptions, - icu: &Icu, + provider: &IntlProvider, ) -> Locale where S: Service, - BoaProvider: DataProvider, + IntlProvider: DataProvider, { // 1. Let matcher be options.[[localeMatcher]]. // 2. If matcher is "lookup", then @@ -412,9 +421,9 @@ where // a. Let r be ! BestFitMatcher(availableLocales, requestedLocales). // 4. Let foundLocale be r.[[locale]]. let mut found_locale = if options.matcher == LocaleMatcher::Lookup { - lookup_matcher::(requested_locales, icu) + lookup_matcher::(requested_locales, provider) } else { - best_fit_matcher::(requested_locales, icu) + best_fit_matcher::(requested_locales, provider) }; // From here, the spec differs significantly from the implementation, @@ -469,12 +478,10 @@ where // 11. Set result.[[locale]] to foundLocale. // 12. Return result. - S::resolve( - &mut found_locale, - &mut options.service_options, - icu.provider(), - ); - icu.locale_canonicalizer().canonicalize(&mut found_locale); + S::resolve(&mut found_locale, &mut options.service_options, provider); + provider + .locale_canonicalizer() + .canonicalize(&mut found_locale); found_locale } @@ -493,7 +500,7 @@ where /// [spec]: https://tc39.es/ecma402/#sec-lookupsupportedlocales fn lookup_supported_locales( requested_locales: &[Locale], - provider: &impl DataProvider, + provider: &(impl DataProvider + ?Sized), ) -> Vec { // 1. Let subset be a new empty List. // 2. For each element locale of requestedLocales, do @@ -517,7 +524,7 @@ fn lookup_supported_locales( /// [spec]: https://tc39.es/ecma402/#sec-bestfitsupportedlocales fn best_fit_supported_locales( requested_locales: &[Locale], - provider: &impl DataProvider, + provider: &(impl DataProvider + ?Sized), ) -> Vec { requested_locales .iter() @@ -538,7 +545,7 @@ pub(in crate::builtins::intl) fn supported_locales( context: &mut Context, ) -> JsResult where - BoaProvider: DataProvider, + IntlProvider: DataProvider, { // 1. Set options to ? CoerceOptionsToObject(options). let options = coerce_options_to_object(options, context)?; @@ -550,12 +557,12 @@ where // 4. Else, // a. Let supportedLocales be LookupSupportedLocales(availableLocales, requestedLocales). LocaleMatcher::Lookup => { - lookup_supported_locales(requested_locales, context.icu().provider()) + lookup_supported_locales(requested_locales, context.intl_provider()) } // 3. If matcher is "best fit", then // a. Let supportedLocales be BestFitSupportedLocales(availableLocales, requestedLocales). LocaleMatcher::BestFit => { - best_fit_supported_locales(requested_locales, context.icu().provider()) + best_fit_supported_locales(requested_locales, context.intl_provider()) } }; @@ -600,7 +607,7 @@ mod tests { builtins::intl::locale::utils::{ best_available_locale, best_fit_matcher, default_locale, lookup_matcher, }, - context::icu::{BoaProvider, Icu, StaticProviderAdapter}, + context::icu::IntlProvider, }; #[test] @@ -626,10 +633,7 @@ mod tests { #[test] fn lookup_match() { - let icu = Icu::new(BoaProvider::Buffer(Box::new(StaticProviderAdapter( - boa_icu_provider::buffer(), - )))) - .unwrap(); + let icu = IntlProvider::try_new_with_buffer_provider(boa_icu_provider::buffer()).unwrap(); // requested: [] diff --git a/core/engine/src/builtins/intl/mod.rs b/core/engine/src/builtins/intl/mod.rs index 92c85e98c2..1a29e37110 100644 --- a/core/engine/src/builtins/intl/mod.rs +++ b/core/engine/src/builtins/intl/mod.rs @@ -15,7 +15,7 @@ use crate::{ builtins::{Array, BuiltInBuilder, BuiltInObject, IntrinsicObject}, - context::{intrinsics::Intrinsics, BoaProvider}, + context::{icu::IntlProvider, intrinsics::Intrinsics}, js_string, object::JsObject, property::Attribute, @@ -172,7 +172,7 @@ trait Service { fn resolve( _locale: &mut icu_locid::Locale, _options: &mut Self::LocaleOptions, - _provider: &BoaProvider, + _provider: &IntlProvider, ) { } } diff --git a/core/engine/src/builtins/intl/plural_rules/mod.rs b/core/engine/src/builtins/intl/plural_rules/mod.rs index 02aa1cf9a2..fa14b352cb 100644 --- a/core/engine/src/builtins/intl/plural_rules/mod.rs +++ b/core/engine/src/builtins/intl/plural_rules/mod.rs @@ -143,16 +143,16 @@ impl BuiltInConstructor for PluralRules { matcher, ..Default::default() }, - context.icu(), + context.intl_provider(), ); let native = match rule_type { PluralRuleType::Cardinal => PluralRulesWithRanges::try_new_cardinal_unstable( - context.icu().provider(), + context.intl_provider(), &DataLocale::from(&locale), ), PluralRuleType::Ordinal => PluralRulesWithRanges::try_new_ordinal_unstable( - context.icu().provider(), + context.intl_provider(), &DataLocale::from(&locale), ), _ => { diff --git a/core/engine/src/builtins/intl/segmenter/mod.rs b/core/engine/src/builtins/intl/segmenter/mod.rs index 279c305274..2ff7f03e56 100644 --- a/core/engine/src/builtins/intl/segmenter/mod.rs +++ b/core/engine/src/builtins/intl/segmenter/mod.rs @@ -146,7 +146,7 @@ impl BuiltInConstructor for Segmenter { matcher, ..Default::default() }, - context.icu(), + context.intl_provider(), ); // 12. Let granularity be ? GetOption(options, "granularity", string, « "grapheme", "word", "sentence" », "grapheme"). @@ -155,14 +155,14 @@ impl BuiltInConstructor for Segmenter { let native = match granularity { Granularity::Grapheme => { - GraphemeClusterSegmenter::try_new_unstable(context.icu().provider()) + GraphemeClusterSegmenter::try_new_unstable(context.intl_provider()) .map(|s| NativeSegmenter::Grapheme(Box::new(s))) } - Granularity::Word => WordSegmenter::try_new_auto_unstable(context.icu().provider()) + Granularity::Word => WordSegmenter::try_new_auto_unstable(context.intl_provider()) .map(|s| NativeSegmenter::Word(Box::new(s))), - Granularity::Sentence => SentenceSegmenter::try_new_unstable(context.icu().provider()) + Granularity::Sentence => SentenceSegmenter::try_new_unstable(context.intl_provider()) .map(|s| NativeSegmenter::Sentence(Box::new(s))), } .map_err(|err| JsNativeError::typ().with_message(err.to_string()))?; diff --git a/core/engine/src/builtins/string/mod.rs b/core/engine/src/builtins/string/mod.rs index 46777d6d0e..ec888839be 100644 --- a/core/engine/src/builtins/string/mod.rs +++ b/core/engine/src/builtins/string/mod.rs @@ -1754,16 +1754,16 @@ impl String { .next() // 3. Else, // a. Let requestedLocale be ! DefaultLocale(). - .unwrap_or_else(|| default_locale(context.icu().locale_canonicalizer())) + .unwrap_or_else(|| default_locale(context.intl_provider().locale_canonicalizer())) .id; // 4. Let noExtensionsLocale be the String value that is requestedLocale with any Unicode locale extension sequences (6.2.1) removed. // 5. Let availableLocales be a List with language tags that includes the languages for which the Unicode Character Database contains language sensitive case mappings. Implementations may add additional language tags if they support case mapping for additional locales. // 6. Let locale be ! BestAvailableLocale(availableLocales, noExtensionsLocale). // 7. If locale is undefined, set locale to "und". - let lang = best_available_locale::(lang, context.icu().provider()) + let lang = best_available_locale::(lang, context.intl_provider()) .unwrap_or(LanguageIdentifier::UND); - let casemapper = context.icu().case_mapper(); + let casemapper = context.intl_provider().case_mapper(); // 8. Let codePoints be StringToCodePoints(S). let result = string.map_valid_segments(|segment| { @@ -2143,7 +2143,7 @@ impl String { } #[cfg(feature = "intl")] { - context.icu().string_normalizers() + context.intl_provider().string_normalizers() } }; diff --git a/core/engine/src/context/icu.rs b/core/engine/src/context/icu.rs index f2307e7c5a..0be4f9416a 100644 --- a/core/engine/src/context/icu.rs +++ b/core/engine/src/context/icu.rs @@ -14,42 +14,10 @@ use zerofrom::ZeroFrom; use crate::builtins::string::StringNormalizers; -/// ICU4X data provider used in boa. -/// -/// Providers can be either [`BufferProvider`]s or [`AnyProvider`]s. -/// -/// The [`icu_provider`] documentation has more information about data providers. -pub enum BoaProvider { - /// A [`BufferProvider`] data provider. - Buffer(Box), - /// An [`AnyProvider`] data provider. +/// A [`DataProvider`] that can be either a [`BufferProvider`] or an [`AnyProvider`]. +enum ErasedProvider { Any(Box), -} - -impl Debug for BoaProvider { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Buffer(_) => f.debug_tuple("Buffer").field(&"..").finish(), - Self::Any(_) => f.debug_tuple("Any").field(&"..").finish(), - } - } -} - -// This blanket implementation mirrors the `DataProvider` implementations of `BufferProvider` and -// `AnyProvider`, which allows us to use `unstable` constructors in a stable way. -impl DataProvider for BoaProvider -where - M: KeyedDataMarker + 'static, - for<'de> YokeTraitHack<>::Output>: Deserialize<'de>, - for<'a> YokeTraitHack<>::Output>: Clone, - M::Yokeable: ZeroFrom<'static, M::Yokeable> + MaybeSendSync, -{ - fn load(&self, req: DataRequest<'_>) -> Result, DataError> { - match self { - BoaProvider::Buffer(provider) => provider.as_deserializing().load(req), - BoaProvider::Any(provider) => provider.as_downcasting().load(req), - } - } + Buffer(Box), } /// Error thrown when the engine cannot initialize the ICU tools from a data provider. @@ -66,46 +34,82 @@ pub enum IcuError { CaseMap(#[from] DataError), } -/// Collection of tools initialized from a [`DataProvider`] that are used for the functionality of -/// `Intl`. -pub(crate) struct Icu { - provider: BoaProvider, +/// Custom [`DataProvider`] for `Intl` that caches some utilities. +pub(crate) struct IntlProvider { + inner_provider: ErasedProvider, locale_canonicalizer: LocaleCanonicalizer, locale_expander: LocaleExpander, string_normalizers: StringNormalizers, case_mapper: CaseMapper, } -impl Debug for Icu { +impl DataProvider for IntlProvider +where + M: KeyedDataMarker + 'static, + for<'de> YokeTraitHack<>::Output>: Deserialize<'de> + Clone, + M::Yokeable: ZeroFrom<'static, M::Yokeable> + MaybeSendSync, +{ + fn load(&self, req: DataRequest<'_>) -> Result, DataError> { + match &self.inner_provider { + ErasedProvider::Any(any) => any.as_downcasting().load(req), + ErasedProvider::Buffer(buffer) => buffer.as_deserializing().load(req), + } + } +} + +impl Debug for IntlProvider { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Icu") - .field("provider", &self.provider) .field("locale_canonicalizer", &self.locale_canonicalizer) .field("locale_expander", &self.locale_expander) .field("string_normalizers", &self.string_normalizers) .field("string_normalizercase_mapper", &self.case_mapper) - .finish() + .finish_non_exhaustive() } } -impl Icu { - /// Creates a new [`Icu`] from a valid [`BoaProvider`] +impl IntlProvider { + /// Creates a new [`IntlProvider`] from a [`BufferProvider`]. + /// + /// # Errors + /// + /// Returns an error if any of the tools required cannot be constructed. + pub(crate) fn try_new_with_buffer_provider( + provider: (impl BufferProvider + 'static), + ) -> Result { + Ok(Self { + locale_canonicalizer: LocaleCanonicalizer::try_new_with_buffer_provider(&provider)?, + locale_expander: LocaleExpander::try_new_with_buffer_provider(&provider)?, + string_normalizers: StringNormalizers { + nfc: ComposingNormalizer::try_new_nfc_with_buffer_provider(&provider)?, + nfkc: ComposingNormalizer::try_new_nfkc_with_buffer_provider(&provider)?, + nfd: DecomposingNormalizer::try_new_nfd_with_buffer_provider(&provider)?, + nfkd: DecomposingNormalizer::try_new_nfkd_with_buffer_provider(&provider)?, + }, + case_mapper: CaseMapper::try_new_with_buffer_provider(&provider)?, + inner_provider: ErasedProvider::Buffer(Box::new(provider)), + }) + } + + /// Creates a new [`IntlProvider`] from an [`AnyProvider`]. /// /// # Errors /// /// Returns an error if any of the tools required cannot be constructed. - pub(crate) fn new(provider: BoaProvider) -> Result { + pub(crate) fn try_new_with_any_provider( + provider: (impl AnyProvider + 'static), + ) -> Result { Ok(Self { - locale_canonicalizer: LocaleCanonicalizer::try_new_unstable(&provider)?, - locale_expander: LocaleExpander::try_new_extended_unstable(&provider)?, + locale_canonicalizer: LocaleCanonicalizer::try_new_with_any_provider(&provider)?, + locale_expander: LocaleExpander::try_new_extended_with_any_provider(&provider)?, string_normalizers: StringNormalizers { - nfc: ComposingNormalizer::try_new_nfc_unstable(&provider)?, - nfkc: ComposingNormalizer::try_new_nfkc_unstable(&provider)?, - nfd: DecomposingNormalizer::try_new_nfd_unstable(&provider)?, - nfkd: DecomposingNormalizer::try_new_nfkd_unstable(&provider)?, + nfc: ComposingNormalizer::try_new_nfc_with_any_provider(&provider)?, + nfkc: ComposingNormalizer::try_new_nfkc_with_any_provider(&provider)?, + nfd: DecomposingNormalizer::try_new_nfd_with_any_provider(&provider)?, + nfkd: DecomposingNormalizer::try_new_nfkd_with_any_provider(&provider)?, }, - case_mapper: CaseMapper::try_new_unstable(&provider)?, - provider, + case_mapper: CaseMapper::try_new_with_any_provider(&provider)?, + inner_provider: ErasedProvider::Any(Box::new(provider)), }) } @@ -128,34 +132,4 @@ impl Icu { pub(crate) const fn case_mapper(&self) -> &CaseMapper { &self.case_mapper } - - /// Gets the inner icu data provider - pub(crate) const fn provider(&self) -> &BoaProvider { - &self.provider - } -} - -/// Adapter to allow creating a `Box` from -/// a &'static impl Provider. -#[derive(Debug)] -pub(crate) struct StaticProviderAdapter(pub(crate) &'static T); - -impl BufferProvider for StaticProviderAdapter { - fn load_buffer( - &self, - key: icu_provider::DataKey, - req: DataRequest<'_>, - ) -> Result, DataError> { - self.0.load_buffer(key, req) - } -} - -impl AnyProvider for StaticProviderAdapter { - fn load_any( - &self, - key: icu_provider::DataKey, - req: DataRequest<'_>, - ) -> Result { - self.0.load_any(key, req) - } } diff --git a/core/engine/src/context/mod.rs b/core/engine/src/context/mod.rs index 07cc6ef947..c5a9cce945 100644 --- a/core/engine/src/context/mod.rs +++ b/core/engine/src/context/mod.rs @@ -6,8 +6,10 @@ pub(crate) mod icu; pub mod intrinsics; pub use hooks::{DefaultHooks, HostHooks}; + #[cfg(feature = "intl")] -pub use icu::{BoaProvider, IcuError}; +pub use icu::IcuError; + use intrinsics::Intrinsics; #[cfg(not(feature = "intl"))] @@ -102,9 +104,9 @@ pub struct Context { can_block: bool, - /// ICU related utilities + /// Intl data provider. #[cfg(feature = "intl")] - icu: icu::Icu, + intl_provider: icu::IntlProvider, host_hooks: &'static dyn HostHooks, @@ -134,7 +136,7 @@ impl std::fmt::Debug for Context { .field("optimizer_options", &self.optimizer_options); #[cfg(feature = "intl")] - debug.field("icu", &self.icu); + debug.field("intl_provider", &self.intl_provider); debug.finish_non_exhaustive() } @@ -837,10 +839,10 @@ impl Context { ContextCleanupGuard::new(self, cleanup) } - /// Get the ICU related utilities + /// Get the Intl data provider. #[cfg(feature = "intl")] - pub(crate) const fn icu(&self) -> &icu::Icu { - &self.icu + pub(crate) const fn intl_provider(&self) -> &icu::IntlProvider { + &self.intl_provider } } @@ -848,13 +850,6 @@ impl Context { /// /// This builder allows custom initialization of the [`Interner`] within /// the context. -/// Additionally, if the `intl` feature is enabled, [`ContextBuilder`] becomes -/// the only way to create a new [`Context`], since now it requires a -/// valid data provider for the `Intl` functionality. -#[cfg_attr( - feature = "intl", - doc = "The required data in a valid provider is specified in [`BoaProvider`]" -)] #[derive(Default)] pub struct ContextBuilder { interner: Option, @@ -863,7 +858,7 @@ pub struct ContextBuilder { module_loader: Option>, can_block: bool, #[cfg(feature = "intl")] - icu: Option, + icu: Option, #[cfg(feature = "fuzz")] instructions_remaining: usize, } @@ -917,7 +912,30 @@ impl ContextBuilder { self } - /// Provides an icu data provider to the [`Context`]. + /// Provides a [`BufferProvider`] data provider to the [`Context`]. + /// + /// This function is only available if the `intl` feature is enabled. + /// + /// # Errors + /// + /// This returns `Err` if the provided provider doesn't have the required locale information + /// to construct both a [`LocaleCanonicalizer`] and a [`LocaleExpander`]. Note that this doesn't + /// mean that the provider will successfully construct all `Intl` services; that check is made + /// until the creation of an instance of a service. + /// + /// [`LocaleCanonicalizer`]: icu_locid_transform::LocaleCanonicalizer + /// [`LocaleExpander`]: icu_locid_transform::LocaleExpander + /// [`BufferProvider`]: icu_provider::BufferProvider + #[cfg(feature = "intl")] + pub fn icu_buffer_provider( + mut self, + provider: T, + ) -> Result { + self.icu = Some(icu::IntlProvider::try_new_with_buffer_provider(provider)?); + Ok(self) + } + + /// Provides an [`AnyProvider`] data provider to the [`Context`]. /// /// This function is only available if the `intl` feature is enabled. /// @@ -930,9 +948,13 @@ impl ContextBuilder { /// /// [`LocaleCanonicalizer`]: icu_locid_transform::LocaleCanonicalizer /// [`LocaleExpander`]: icu_locid_transform::LocaleExpander + /// [`AnyProvider`]: icu_provider::AnyProvider #[cfg(feature = "intl")] - pub fn icu_provider(mut self, provider: BoaProvider) -> Result { - self.icu = Some(icu::Icu::new(provider)?); + pub fn icu_any_provider( + mut self, + provider: T, + ) -> Result { + self.icu = Some(icu::IntlProvider::try_new_with_any_provider(provider)?); Ok(self) } @@ -1027,11 +1049,9 @@ impl ContextBuilder { vm, strict: false, #[cfg(feature = "intl")] - icu: self.icu.unwrap_or_else(|| { - let provider = BoaProvider::Buffer(Box::new(icu::StaticProviderAdapter( - boa_icu_provider::buffer(), - ))); - icu::Icu::new(provider).expect("Failed to initialize default icu data.") + intl_provider: self.icu.unwrap_or_else(|| { + icu::IntlProvider::try_new_with_buffer_provider(boa_icu_provider::buffer()) + .expect("Failed to initialize default icu data.") }), #[cfg(feature = "fuzz")] instructions_remaining: self.instructions_remaining,