From a68edde2b7b3c409a544d5e9858d58072c92e487 Mon Sep 17 00:00:00 2001 From: Norbert Garfield Date: Mon, 11 Apr 2022 17:02:16 +0000 Subject: [PATCH] Initial implementation for Intl.DateTimeFormat --- .../src/builtins/intl/date_time_format.rs | 114 ++++++++++++++++++ boa_engine/src/builtins/intl/mod.rs | 16 ++- boa_engine/src/context/intrinsics.rs | 7 ++ boa_engine/src/object/mod.rs | 11 ++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 boa_engine/src/builtins/intl/date_time_format.rs diff --git a/boa_engine/src/builtins/intl/date_time_format.rs b/boa_engine/src/builtins/intl/date_time_format.rs new file mode 100644 index 00000000000..7a49604e5df --- /dev/null +++ b/boa_engine/src/builtins/intl/date_time_format.rs @@ -0,0 +1,114 @@ +//! This module implements the global `Intl.DateTimeFormat` object. +//! +//! `Intl.DateTimeFormat` is a built-in object that has properties and methods for date and time i18n. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma402/#datetimeformat-objects + +use crate::{ + builtins::BuiltIn, + context::intrinsics::StandardConstructors, + object::internal_methods::get_prototype_from_constructor, + object::{JsObject, ObjectData, ObjectInitializer}, + property::Attribute, + symbol::WellKnownSymbols, + Context, JsResult, JsValue, +}; + +use boa_gc::{Finalize, Trace}; +use boa_profiler::Profiler; +use tap::{Conv, Pipe}; + +/// JavaScript `Intl.DateTimeFormat` object. +#[derive(Debug, Clone, Trace, Finalize)] +pub struct DateTimeFormat { + initialized_date_time_format: bool, + locale: String, + calendar: String, + numbering_system: String, + time_zone: String, + weekday: String, + era: String, + year: String, + month: String, + day: String, + day_period: String, + hour: String, + minute: String, + second: String, + fractional_second_digits: String, + time_zone_name: String, + hour_cycle: String, + pattern: String, + bound_format: String, +} + +impl BuiltIn for DateTimeFormat { + const NAME: &'static str = "Intl.DateTimeFormat"; + + fn init(context: &mut Context) -> Option { + let _timer = Profiler::global().start_event(Self::NAME, "init"); + + let string_tag = WellKnownSymbols::to_string_tag(); + ObjectInitializer::new(context) + .property( + string_tag, + Self::NAME, + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) + .build() + .conv::() + .pipe(Some) + } +} + +impl DateTimeFormat { + /// The `Intl.DateTimeFormat` constructor is the `%DateTimeFormat%` intrinsic object and a standard built-in property of the `Intl` object. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma402/#datetimeformat-objects + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat + pub(crate) fn constructor( + new_target: &JsValue, + _args: &[JsValue], + context: &mut Context, + ) -> JsResult { + // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget. + let prototype = get_prototype_from_constructor( + new_target, + StandardConstructors::date_time_format, + context, + )?; + // 2. Let dateTimeFormat be ? OrdinaryCreateFromConstructor(newTarget, "%DateTimeFormat.prototype%", « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[Weekday]], [[Era]], [[Year]], [[Month]], [[Day]], [[DayPeriod]], [[Hour]], [[Minute]], [[Second]], [[FractionalSecondDigits]], [[TimeZoneName]], [[HourCycle]], [[Pattern]], [[BoundFormat]] »). + let date_time_format = JsObject::from_proto_and_data( + prototype, + ObjectData::date_time_format(Box::new(Self { + initialized_date_time_format: true, + locale: "en-US".to_string(), + calendar: "gregory".to_string(), + numbering_system: "arab".to_string(), + time_zone: "UTC".to_string(), + weekday: "narrow".to_string(), + era: "narrow".to_string(), + year: "numeric".to_string(), + month: "narrow".to_string(), + day: "numeric".to_string(), + day_period: "narrow".to_string(), + hour: "numeric".to_string(), + minute: "numeric".to_string(), + second: "numeric".to_string(), + fractional_second_digits: "".to_string(), + time_zone_name: "".to_string(), + hour_cycle: "h24".to_string(), + pattern: "{hour}:{minute}".to_string(), + bound_format: "undefined".to_string(), + })), + ); + Ok(JsValue::Object(date_time_format)) + } +} diff --git a/boa_engine/src/builtins/intl/mod.rs b/boa_engine/src/builtins/intl/mod.rs index 0410c755249..b343604e79b 100644 --- a/boa_engine/src/builtins/intl/mod.rs +++ b/boa_engine/src/builtins/intl/mod.rs @@ -8,12 +8,16 @@ //! [spec]: https://tc39.es/ecma402/#intl-object use crate::{ + builtins::intl::date_time_format::DateTimeFormat, builtins::{Array, BuiltIn, JsArgs}, - object::ObjectInitializer, + object::{FunctionBuilder, ObjectInitializer}, property::Attribute, symbol::WellKnownSymbols, Context, JsResult, JsString, JsValue, }; + +pub mod date_time_format; + use boa_profiler::Profiler; use indexmap::IndexSet; use tap::{Conv, Pipe}; @@ -29,6 +33,11 @@ impl BuiltIn for Intl { let _timer = Profiler::global().start_event(Self::NAME, "init"); let string_tag = WellKnownSymbols::to_string_tag(); + let date_time_format = FunctionBuilder::native(context, DateTimeFormat::constructor) + .name("Intl.DateTimeFormat") + .length(0) + .constructor(true) + .build(); ObjectInitializer::new(context) .function(Self::get_canonical_locales, "getCanonicalLocales", 1) .property( @@ -36,6 +45,11 @@ impl BuiltIn for Intl { Self::NAME, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ) + .property( + "DateTimeFormat", + date_time_format, + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ) .build() .conv::() .pipe(Some) diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index a1fddb1920e..de11c360a9a 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -107,6 +107,7 @@ pub struct StandardConstructors { typed_float64_array: StandardConstructor, array_buffer: StandardConstructor, data_view: StandardConstructor, + date_time_format: StandardConstructor, } impl Default for StandardConstructors { @@ -157,6 +158,7 @@ impl Default for StandardConstructors { typed_float64_array: StandardConstructor::default(), array_buffer: StandardConstructor::default(), data_view: StandardConstructor::default(), + date_time_format: StandardConstructor::default(), } } } @@ -341,6 +343,11 @@ impl StandardConstructors { pub fn data_view(&self) -> &StandardConstructor { &self.data_view } + + #[inline] + pub fn date_time_format(&self) -> &StandardConstructor { + &self.date_time_format + } } /// Cached intrinsic objects diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 206e7d2c108..8a9d39e7b0f 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -29,6 +29,7 @@ use crate::{ arguments::ParameterMap, BoundFunction, Captures, Function, NativeFunctionSignature, }, generator::Generator, + intl::date_time_format::DateTimeFormat, map::map_iterator::MapIterator, map::ordered_map::OrderedMap, object::for_in_iterator::ForInIterator, @@ -161,6 +162,7 @@ pub enum ObjectKind { Arguments(Arguments), NativeObject(Box), IntegerIndexed(IntegerIndexed), + DateTimeFormat(Box), } impl ObjectData { @@ -417,6 +419,14 @@ impl ObjectData { internal_methods: &INTEGER_INDEXED_EXOTIC_INTERNAL_METHODS, } } + + /// Create the `DateTimeFormat` object data + pub fn date_time_format(date_time_fmt: Box) -> Self { + Self { + kind: ObjectKind::DateTimeFormat(date_time_fmt), + internal_methods: &ORDINARY_INTERNAL_METHODS, + } + } } impl Display for ObjectKind { @@ -451,6 +461,7 @@ impl Display for ObjectKind { Self::NativeObject(_) => "NativeObject", Self::IntegerIndexed(_) => "TypedArray", Self::DataView(_) => "DataView", + Self::DateTimeFormat(_) => "DateTimeFormat", }) } }