Browse Source

Simplify `Icu` API (#3503)

* Simplify `Icu` API

* Fix docs

* Document internals

* Simplify inner API
pull/3511/head
José Julián Espina 11 months ago committed by GitHub
parent
commit
52b39fabd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      core/engine/src/builtins/intl/collator/mod.rs
  2. 8
      core/engine/src/builtins/intl/list_format/mod.rs
  3. 19
      core/engine/src/builtins/intl/locale/mod.rs
  4. 19
      core/engine/src/builtins/intl/locale/tests.rs
  5. 64
      core/engine/src/builtins/intl/locale/utils.rs
  6. 4
      core/engine/src/builtins/intl/mod.rs
  7. 6
      core/engine/src/builtins/intl/plural_rules/mod.rs
  8. 8
      core/engine/src/builtins/intl/segmenter/mod.rs
  9. 8
      core/engine/src/builtins/string/mod.rs
  10. 140
      core/engine/src/context/icu.rs
  11. 66
      core/engine/src/context/mod.rs

13
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::<Self>(&requested_locales, &mut intl_options, context.icu());
let mut locale = resolve_locale::<Self>(
&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;

8
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,
),

19
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();

19
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::<TestService>(&[], &mut options, &icu);
let locale = resolve_locale::<TestService>(&[], &mut options, &provider);
assert_eq!(locale, default);
// test best fit
@ -102,10 +99,10 @@ fn locale_resolution() {
},
};
let locale = resolve_locale::<TestService>(&[], &mut options, &icu);
let locale = resolve_locale::<TestService>(&[], &mut options, &provider);
let best = best_locale_for_provider::<<TestService as Service>::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::<TestService>(&[locale!("es-AR")], &mut options, &icu);
let locale = resolve_locale::<TestService>(&[locale!("es-AR")], &mut options, &provider);
assert_eq!(locale, "es-u-hc-h23".parse().unwrap());
}

64
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<M: KeyedDataMarker>(
/// in order to see if a certain [`Locale`] is supported.
///
/// [spec]: https://tc39.es/ecma402/#sec-lookupmatcher
fn lookup_matcher<M: KeyedDataMarker>(requested_locales: &[Locale], icu: &Icu) -> Locale
fn lookup_matcher<M: KeyedDataMarker>(
requested_locales: &[Locale],
provider: &IntlProvider,
) -> Locale
where
BoaProvider: DataProvider<M>,
IntlProvider: DataProvider<M>,
{
// 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::<M>(id, icu.provider());
let available_locale = best_available_locale::<M>(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<M: KeyedDataMarker>(requested_locales: &[Locale], icu: &Icu) -> Locale
fn best_fit_matcher<M: KeyedDataMarker>(
requested_locales: &[Locale],
provider: &IntlProvider,
) -> Locale
where
BoaProvider: DataProvider<M>,
IntlProvider: DataProvider<M>,
{
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<S>(
requested_locales: &[Locale],
options: &mut IntlOptions<S::LocaleOptions>,
icu: &Icu,
provider: &IntlProvider,
) -> Locale
where
S: Service,
BoaProvider: DataProvider<S::LangMarker>,
IntlProvider: DataProvider<S::LangMarker>,
{
// 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::<S::LangMarker>(requested_locales, icu)
lookup_matcher::<S::LangMarker>(requested_locales, provider)
} else {
best_fit_matcher::<S::LangMarker>(requested_locales, icu)
best_fit_matcher::<S::LangMarker>(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<M: KeyedDataMarker>(
requested_locales: &[Locale],
provider: &impl DataProvider<M>,
provider: &(impl DataProvider<M> + ?Sized),
) -> Vec<Locale> {
// 1. Let subset be a new empty List.
// 2. For each element locale of requestedLocales, do
@ -517,7 +524,7 @@ fn lookup_supported_locales<M: KeyedDataMarker>(
/// [spec]: https://tc39.es/ecma402/#sec-bestfitsupportedlocales
fn best_fit_supported_locales<M: KeyedDataMarker>(
requested_locales: &[Locale],
provider: &impl DataProvider<M>,
provider: &(impl DataProvider<M> + ?Sized),
) -> Vec<Locale> {
requested_locales
.iter()
@ -538,7 +545,7 @@ pub(in crate::builtins::intl) fn supported_locales<M: KeyedDataMarker>(
context: &mut Context,
) -> JsResult<JsObject>
where
BoaProvider: DataProvider<M>,
IntlProvider: DataProvider<M>,
{
// 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: []

4
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,
) {
}
}

6
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),
),
_ => {

8
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()))?;

8
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::<CaseMapV1Marker>(lang, context.icu().provider())
let lang = best_available_locale::<CaseMapV1Marker>(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()
}
};

140
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<dyn BufferProvider>),
/// An [`AnyProvider`] data provider.
/// A [`DataProvider`] that can be either a [`BufferProvider`] or an [`AnyProvider`].
enum ErasedProvider {
Any(Box<dyn AnyProvider>),
}
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<M> DataProvider<M> for BoaProvider
where
M: KeyedDataMarker + 'static,
for<'de> YokeTraitHack<<M::Yokeable as Yokeable<'de>>::Output>: Deserialize<'de>,
for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
M::Yokeable: ZeroFrom<'static, M::Yokeable> + MaybeSendSync,
{
fn load(&self, req: DataRequest<'_>) -> Result<DataResponse<M>, DataError> {
match self {
BoaProvider::Buffer(provider) => provider.as_deserializing().load(req),
BoaProvider::Any(provider) => provider.as_downcasting().load(req),
}
}
Buffer(Box<dyn BufferProvider>),
}
/// 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<M> DataProvider<M> for IntlProvider
where
M: KeyedDataMarker + 'static,
for<'de> YokeTraitHack<<M::Yokeable as Yokeable<'de>>::Output>: Deserialize<'de> + Clone,
M::Yokeable: ZeroFrom<'static, M::Yokeable> + MaybeSendSync,
{
fn load(&self, req: DataRequest<'_>) -> Result<DataResponse<M>, 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<IntlProvider, IcuError> {
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<Icu, IcuError> {
pub(crate) fn try_new_with_any_provider(
provider: (impl AnyProvider + 'static),
) -> Result<IntlProvider, IcuError> {
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<dyn Provider>` from
/// a &'static impl Provider.
#[derive(Debug)]
pub(crate) struct StaticProviderAdapter<T: 'static>(pub(crate) &'static T);
impl<T: BufferProvider> BufferProvider for StaticProviderAdapter<T> {
fn load_buffer(
&self,
key: icu_provider::DataKey,
req: DataRequest<'_>,
) -> Result<DataResponse<icu_provider::BufferMarker>, DataError> {
self.0.load_buffer(key, req)
}
}
impl<T: AnyProvider> AnyProvider for StaticProviderAdapter<T> {
fn load_any(
&self,
key: icu_provider::DataKey,
req: DataRequest<'_>,
) -> Result<icu_provider::AnyResponse, DataError> {
self.0.load_any(key, req)
}
}

66
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<Interner>,
@ -863,7 +858,7 @@ pub struct ContextBuilder {
module_loader: Option<Rc<dyn ModuleLoader>>,
can_block: bool,
#[cfg(feature = "intl")]
icu: Option<icu::Icu>,
icu: Option<icu::IntlProvider>,
#[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<T: icu_provider::BufferProvider + 'static>(
mut self,
provider: T,
) -> Result<Self, IcuError> {
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, IcuError> {
self.icu = Some(icu::Icu::new(provider)?);
pub fn icu_any_provider<T: icu_provider::AnyProvider + 'static>(
mut self,
provider: T,
) -> Result<Self, IcuError> {
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,

Loading…
Cancel
Save