Browse Source

update Boa to be inline with Temporal (#4034)

* update Boa to be inline with Temporal

* use TinyAsciiStr

* tidyup

* updates MonthDay

* remove commented code

* use intoOrUndefined

* add documentation to macros tests

* use tinyasciiistr from temporal

* patch update_temporal per discussion + version 0.0.4 bump changes

---------

Co-authored-by: Kevin Ness <46825870+nekevss@users.noreply.github.com>
pull/4069/head v0.20
Jason Williams 1 week ago committed by GitHub
parent
commit
b345775138
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 54
      Cargo.lock
  2. 2
      Cargo.toml
  3. 2
      core/engine/src/builtins/temporal/calendar/mod.rs
  4. 3
      core/engine/src/builtins/temporal/duration/mod.rs
  5. 34
      core/engine/src/builtins/temporal/instant/mod.rs
  6. 5
      core/engine/src/builtins/temporal/mod.rs
  7. 2
      core/engine/src/builtins/temporal/now.rs
  8. 4
      core/engine/src/builtins/temporal/options.rs
  9. 132
      core/engine/src/builtins/temporal/plain_date/mod.rs
  10. 32
      core/engine/src/builtins/temporal/plain_date_time/mod.rs
  11. 108
      core/engine/src/builtins/temporal/plain_month_day/mod.rs
  12. 30
      core/engine/src/builtins/temporal/plain_time/mod.rs
  13. 26
      core/engine/src/builtins/temporal/plain_year_month/mod.rs
  14. 2
      core/engine/src/builtins/temporal/zoned_date_time/mod.rs

54
Cargo.lock generated

@ -50,6 +50,15 @@ version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anes"
version = "0.1.6"
@ -873,6 +882,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "core_maths"
version = "0.1.0"
@ -1487,6 +1502,29 @@ dependencies = [
"itoa",
]
[[package]]
name = "iana-time-zone"
version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "icu"
version = "1.5.0"
@ -3291,13 +3329,14 @@ checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078"
[[package]]
name = "temporal_rs"
version = "0.0.3"
source = "git+https://github.com/boa-dev/temporal.git?rev=1e7901d07a83211e62373ab94284a7d1ada4c913#1e7901d07a83211e62373ab94284a7d1ada4c913"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abc1a6b736bd71cd492b8810334efff905c4a09787a46e9a0ddd54b46a2f4e08"
dependencies = [
"bitflags 2.6.0",
"iana-time-zone",
"icu_calendar",
"ixdtf",
"num-bigint",
"num-traits",
"rustc-hash 2.1.0",
"tinystr",
@ -3928,6 +3967,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.48.0"

2
Cargo.toml

@ -111,7 +111,7 @@ intrusive-collections = "0.9.7"
cfg-if = "1.0.0"
either = "1.13.0"
sys-locale = "0.3.2"
temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "1e7901d07a83211e62373ab94284a7d1ada4c913" }
temporal_rs = "0.0.4"
web-time = "1.1.0"
criterion = "0.5.1"
float-cmp = "0.10.0"

2
core/engine/src/builtins/temporal/calendar/mod.rs

@ -4,7 +4,7 @@ use std::str::FromStr;
use super::extract_from_temporal_type;
use crate::{js_string, Context, JsNativeError, JsObject, JsResult, JsValue};
use temporal_rs::components::calendar::Calendar;
use temporal_rs::Calendar;
// -- `Calendar` Abstract Operations --

3
core/engine/src/builtins/temporal/duration/mod.rs

@ -16,9 +16,10 @@ use crate::{
use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
use temporal_rs::{
components::{duration::PartialDuration, Duration as InnerDuration},
options::{RelativeTo, RoundingIncrement, RoundingOptions, TemporalRoundingMode, TemporalUnit},
partial::PartialDuration,
primitive::FiniteF64,
Duration as InnerDuration,
};
use super::{

34
core/engine/src/builtins/temporal/instant/mod.rs

@ -24,8 +24,8 @@ use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
use num_traits::ToPrimitive;
use temporal_rs::{
components::Instant as InnerInstant,
options::{RoundingIncrement, RoundingOptions, TemporalRoundingMode},
Instant as InnerInstant,
};
use super::options::get_difference_settings;
@ -46,19 +46,19 @@ impl IntrinsicObject for Instant {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
let get_seconds = BuiltInBuilder::callable(realm, Self::get_epoc_seconds)
let get_seconds = BuiltInBuilder::callable(realm, Self::get_epoch_seconds)
.name(js_string!("get epochSeconds"))
.build();
let get_millis = BuiltInBuilder::callable(realm, Self::get_epoc_milliseconds)
let get_millis = BuiltInBuilder::callable(realm, Self::get_epoch_milliseconds)
.name(js_string!("get epochMilliseconds"))
.build();
let get_micros = BuiltInBuilder::callable(realm, Self::get_epoc_microseconds)
let get_micros = BuiltInBuilder::callable(realm, Self::get_epoch_microseconds)
.name(js_string!("get epochMicroseconds"))
.build();
let get_nanos = BuiltInBuilder::callable(realm, Self::get_epoc_nanoseconds)
let get_nanos = BuiltInBuilder::callable(realm, Self::get_epoch_nanoseconds)
.name(js_string!("get epochNanoseconds"))
.build();
@ -150,7 +150,7 @@ impl BuiltInConstructor for Instant {
// 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
// NOTE: temporal_rs::Instant asserts that the epochNanoseconds are valid.
let instant = InnerInstant::new(epoch_nanos.as_inner().to_i128().unwrap_or(i128::MAX))?;
let instant = InnerInstant::try_new(epoch_nanos.as_inner().to_i128().unwrap_or(i128::MAX))?;
// 4. Return ? CreateTemporalInstant(epochNanoseconds, NewTarget).
create_temporal_instant(instant, Some(new_target.clone()), context)
}
@ -201,7 +201,7 @@ impl Instant {
// 3. Return ! CreateTemporalInstant(epochNanoseconds).
let nanos = epoch_nanos.as_inner().to_i128();
create_temporal_instant(
InnerInstant::new(nanos.unwrap_or(i128::MAX))?,
InnerInstant::try_new(nanos.unwrap_or(i128::MAX))?,
None,
context,
)
@ -226,7 +226,7 @@ impl Instant {
impl Instant {
/// 8.3.3 get Temporal.Instant.prototype.epochSeconds
pub(crate) fn get_epoc_seconds(
pub(crate) fn get_epoch_seconds(
this: &JsValue,
_: &[JsValue],
_: &mut Context,
@ -240,11 +240,11 @@ impl Instant {
JsNativeError::typ().with_message("the this object must be an instant object.")
})?;
// 3. Let ns be instant.[[Nanoseconds]].
Ok(instant.inner.epoch_seconds().into())
Ok(JsBigInt::from(instant.inner.epoch_seconds()).into())
}
/// 8.3.4 get Temporal.Instant.prototype.epochMilliseconds
pub(crate) fn get_epoc_milliseconds(
pub(crate) fn get_epoch_milliseconds(
this: &JsValue,
_: &[JsValue],
_: &mut Context,
@ -260,11 +260,11 @@ impl Instant {
// 3. Let ns be instant.[[Nanoseconds]].
// 4. Let ms be floor(ℝ(ns) / 106).
// 5. Return 𝔽(ms).
Ok(instant.inner.epoch_milliseconds().into())
Ok(JsBigInt::from(instant.inner.epoch_milliseconds()).into())
}
/// 8.3.5 get Temporal.Instant.prototype.epochMicroseconds
pub(crate) fn get_epoc_microseconds(
pub(crate) fn get_epoch_microseconds(
this: &JsValue,
_: &[JsValue],
_: &mut Context,
@ -280,13 +280,11 @@ impl Instant {
// 3. Let ns be instant.[[Nanoseconds]].
// 4. Let µs be floor(ℝ(ns) / 103).
// 5. Return ℤ(µs).
let big_int = JsBigInt::try_from(instant.inner.epoch_microseconds())
.expect("valid microseconds is in range of BigInt");
Ok(big_int.into())
Ok(JsBigInt::from(instant.inner.epoch_microseconds()).into())
}
/// 8.3.6 get Temporal.Instant.prototype.epochNanoseconds
pub(crate) fn get_epoc_nanoseconds(
pub(crate) fn get_epoch_nanoseconds(
this: &JsValue,
_: &[JsValue],
_: &mut Context,
@ -301,9 +299,7 @@ impl Instant {
})?;
// 3. Let ns be instant.[[Nanoseconds]].
// 4. Return ns.
let big_int = JsBigInt::try_from(instant.inner.epoch_nanoseconds())
.expect("valid nanoseconds is in range of BigInt");
Ok(big_int.into())
Ok(JsBigInt::from(instant.inner.epoch_nanoseconds()).into())
}
/// 8.3.7 `Temporal.Instant.prototype.add ( temporalDurationLike )`

5
core/engine/src/builtins/temporal/mod.rs

@ -37,10 +37,7 @@ use crate::{
Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
};
use boa_profiler::Profiler;
use temporal_rs::{
components::{Date as TemporalDate, ZonedDateTime as TemporalZonedDateTime},
NS_PER_DAY,
};
use temporal_rs::{PlainDate as TemporalDate, ZonedDateTime as TemporalZonedDateTime, NS_PER_DAY};
// TODO: Remove in favor of `temporal_rs`
pub(crate) fn ns_max_instant() -> JsBigInt {

2
core/engine/src/builtins/temporal/now.rs

@ -11,7 +11,7 @@ use crate::{
Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
};
use boa_profiler::Profiler;
use temporal_rs::components::tz::TimeZone;
use temporal_rs::TimeZone;
use super::{ns_max_instant, ns_min_instant, time_zone::default_time_zone};

4
core/engine/src/builtins/temporal/options.rs

@ -13,7 +13,7 @@ use crate::{
js_string, Context, JsNativeError, JsObject, JsResult, JsString, JsValue,
};
use temporal_rs::options::{
ArithmeticOverflow, CalendarName, DifferenceSettings, DurationOverflow, InstantDisambiguation,
ArithmeticOverflow, CalendarName, DifferenceSettings, Disambiguation, DurationOverflow,
OffsetDisambiguation, RoundingIncrement, TemporalRoundingMode, TemporalUnit,
};
@ -113,7 +113,7 @@ fn datetime_units() -> impl Iterator<Item = TemporalUnit> {
impl ParsableOptionType for TemporalUnit {}
impl ParsableOptionType for ArithmeticOverflow {}
impl ParsableOptionType for DurationOverflow {}
impl ParsableOptionType for InstantDisambiguation {}
impl ParsableOptionType for Disambiguation {}
impl ParsableOptionType for OffsetDisambiguation {}
impl ParsableOptionType for TemporalRoundingMode {}
impl ParsableOptionType for CalendarName {}

132
core/engine/src/builtins/temporal/plain_date/mod.rs

@ -3,8 +3,6 @@
// TODO (nekevss): DOCS DOCS AND MORE DOCS
use std::str::FromStr;
use crate::{
builtins::{
options::{get_option, get_options_object},
@ -16,19 +14,15 @@ use crate::{
property::Attribute,
realm::Realm,
string::StaticJsStrings,
value::IntoOrUndefined,
Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol,
JsValue,
};
use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
use temporal_rs::{
components::{
calendar::{Calendar, GetTemporalCalendar},
Date as InnerDate, DateTime, MonthCode, PartialDate,
},
iso::IsoDateSlots,
options::ArithmeticOverflow,
TemporalFields, TinyAsciiStr,
options::ArithmeticOverflow, partial::PartialDate, Calendar, PlainDate as InnerDate,
TinyAsciiStr,
};
use super::{
@ -51,18 +45,6 @@ impl PlainDate {
}
}
impl IsoDateSlots for JsObject<PlainDate> {
fn iso_date(&self) -> temporal_rs::iso::IsoDate {
self.borrow().data().inner.iso_date()
}
}
impl GetTemporalCalendar for JsObject<PlainDate> {
fn get_calendar(&self) -> Calendar {
self.borrow().data().inner.get_calendar()
}
}
impl BuiltInObject for PlainDate {
const NAME: JsString = StaticJsStrings::PLAIN_DATE_NAME;
}
@ -262,15 +244,15 @@ impl BuiltInConstructor for PlainDate {
let iso_day = super::to_integer_with_truncation(args.get_or_undefined(2), context)?;
let calendar_slot = to_temporal_calendar_slot_value(args.get_or_undefined(3))?;
let date = InnerDate::new(
Ok(create_temporal_date(
iso_year,
iso_month,
iso_day,
calendar_slot,
ArithmeticOverflow::Reject,
)?;
Ok(create_temporal_date(date, Some(new_target), context)?.into())
Some(new_target),
context,
)?
.into())
}
}
@ -391,7 +373,7 @@ impl PlainDate {
.into());
};
Ok(date.inner.week_of_year()?.into())
Ok(date.inner.week_of_year()?.into_or_undefined())
}
/// 3.3.11 get `Temporal.PlainDate.prototype.yearOfWeek`
@ -406,7 +388,7 @@ impl PlainDate {
.into());
};
Ok(date.inner.year_of_week()?.into())
Ok(date.inner.year_of_week()?.into_or_undefined())
}
/// 3.3.12 get `Temporal.PlainDate.prototype.daysInWeek`
@ -504,11 +486,23 @@ impl PlainDate {
if let Some(date) = item.as_object().and_then(JsObject::downcast_ref::<Self>) {
let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?;
let _ = get_option::<ArithmeticOverflow>(&options, js_string!("overflow"), context)?;
return create_temporal_date(date.inner.clone(), None, context).map(Into::into);
return create_temporal_date(
date.inner.iso_year(),
date.inner.iso_month().into(),
date.inner.iso_day().into(),
date.inner.calendar().clone(),
None,
context,
)
.map(Into::into);
}
let resolved_date = to_temporal_date(item, options.cloned(), context)?;
create_temporal_date(
to_temporal_date(item, options.cloned(), context)?,
resolved_date.iso_year(),
resolved_date.iso_month().into(),
resolved_date.iso_day().into(),
resolved_date.calendar().clone(),
None,
context,
)
@ -600,7 +594,16 @@ impl PlainDate {
// 5. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »).
// 6. Return ? AddDate(calendarRec, temporalDate, duration, options).
create_temporal_date(date.inner.add(&duration, overflow)?, None, context).map(Into::into)
let resolved_date = date.inner.add(&duration, overflow)?;
create_temporal_date(
resolved_date.iso_year(),
resolved_date.iso_month().into(),
resolved_date.iso_day().into(),
resolved_date.calendar().clone(),
None,
context,
)
.map(Into::into)
}
fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
@ -623,8 +626,16 @@ impl PlainDate {
// 5. Let negatedDuration be CreateNegatedTemporalDuration(duration).
// 6. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »).
// 7. Return ? AddDate(calendarRec, temporalDate, negatedDuration, options).
create_temporal_date(date.inner.subtract(&duration, overflow)?, None, context)
.map(Into::into)
let resolved_date = date.inner.subtract(&duration, overflow)?;
create_temporal_date(
resolved_date.iso_year(),
resolved_date.iso_month().into(),
resolved_date.iso_day().into(),
resolved_date.calendar().clone(),
None,
context,
)
.map(Into::into)
}
// 3.3.24 Temporal.PlainDate.prototype.with ( temporalDateLike [ , options ] )
@ -659,7 +670,16 @@ impl PlainDate {
let partial = to_partial_date_record(partial_object, context)?;
// 10. Return ? CalendarDateFromFields(calendarRec, fields, resolvedOptions).
create_temporal_date(date.inner.with(partial, overflow)?, None, context).map(Into::into)
let resolved_date = date.inner.with(partial, overflow)?;
create_temporal_date(
resolved_date.iso_year(),
resolved_date.iso_month().into(),
resolved_date.iso_day().into(),
resolved_date.calendar().clone(),
None,
context,
)
.map(Into::into)
}
/// 3.3.26 Temporal.PlainDate.prototype.withCalendar ( calendarLike )
@ -672,8 +692,16 @@ impl PlainDate {
})?;
let calendar = to_temporal_calendar_slot_value(args.get_or_undefined(0))?;
create_temporal_date(date.inner.with_calendar(calendar)?, None, context).map(Into::into)
let resolved_date = date.inner.with_calendar(calendar)?;
create_temporal_date(
resolved_date.iso_year(),
resolved_date.iso_month().into(),
resolved_date.iso_day().into(),
resolved_date.calendar().clone(),
None,
context,
)
.map(Into::into)
}
fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
@ -757,7 +785,14 @@ impl PlainDate {
impl PlainDate {
/// Utitily function for translating a `Temporal.PlainDate` into a `JsObject`.
pub(crate) fn as_object(&self, context: &mut Context) -> JsResult<JsObject> {
create_temporal_date(self.inner.clone(), None, context)
create_temporal_date(
self.inner.iso_year(),
self.inner.iso_month().into(),
self.inner.iso_day().into(),
self.inner.calendar().clone(),
None,
context,
)
}
}
@ -766,19 +801,16 @@ impl PlainDate {
/// 3.5.3 `CreateTemporalDate ( isoYear, isoMonth, isoDay, calendar [ , newTarget ] )`
pub(crate) fn create_temporal_date(
inner: InnerDate,
iso_year: i32,
iso_month: i32,
iso_day: i32,
calendar_slot: Calendar,
new_target: Option<&JsValue>,
context: &mut Context,
) -> JsResult<JsObject> {
// NOTE (nekevss): The below should never trigger as `IsValidISODate` is enforced by Date.
// 1. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.
// 2. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
if !DateTime::validate(&inner) {
return Err(JsNativeError::range()
.with_message("Date is not within ISO date time limits.")
.into());
}
let inner = InnerDate::try_new(iso_year, iso_month, iso_day, calendar_slot)?;
// 3. If newTarget is not present, set newTarget to %Temporal.PlainDate%.
let new_target = if let Some(new_target) = new_target {
@ -843,7 +875,7 @@ pub(crate) fn to_temporal_date(
let _o = get_option(&options_obj, js_string!("overflow"), context)?
.unwrap_or(ArithmeticOverflow::Constrain);
let date = InnerDate::from(date_time.inner().clone());
let date = InnerDate::from(date_time.inner.clone());
// ii. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]).
return Ok(date);
@ -867,11 +899,10 @@ pub(crate) fn to_temporal_date(
.with_message("A partial date must have at least one defined field.")
.into());
}
let mut fields = TemporalFields::from(partial);
// g. Return ? CalendarDateFromFields(calendar, fields, options).
return calendar
.date_from_fields(&mut fields, overflow)
.date_from_partial(&partial, overflow)
.map_err(Into::into);
}
@ -920,7 +951,8 @@ pub(crate) fn to_partial_date_record(
.with_message("The monthCode field value must be a string.")
.into());
};
MonthCode::from_str(&month_code.to_std_string_escaped()).map_err(Into::<JsError>::into)
TinyAsciiStr::<4>::from_str(&month_code.to_std_string_escaped())
.map_err(|e| JsError::from(JsNativeError::typ().with_message(e.to_string())))
})
.transpose()?;
let year = partial_object
@ -943,7 +975,7 @@ pub(crate) fn to_partial_date_record(
));
};
// TODO: double check if an invalid monthCode is a range or type error.
TinyAsciiStr::<16>::from_str(&era.to_std_string_escaped())
TinyAsciiStr::<19>::from_str(&era.to_std_string_escaped())
.map_err(|e| JsError::from(JsNativeError::range().with_message(e.to_string())))
})
.transpose()?;

32
core/engine/src/builtins/temporal/plain_date_time/mod.rs

@ -13,6 +13,7 @@ use crate::{
property::Attribute,
realm::Realm,
string::StaticJsStrings,
value::IntoOrUndefined,
Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
};
use boa_gc::{Finalize, Trace};
@ -22,13 +23,9 @@ use boa_profiler::Profiler;
mod tests;
use temporal_rs::{
components::{
calendar::{Calendar, GetTemporalCalendar},
DateTime as InnerDateTime, PartialDateTime, Time,
},
iso::{IsoDate, IsoDateSlots},
options::{ArithmeticOverflow, RoundingIncrement, RoundingOptions, TemporalRoundingMode},
TemporalFields,
partial::PartialDateTime,
PlainDateTime as InnerDateTime, PlainTime,
};
use super::{
@ -55,18 +52,6 @@ impl PlainDateTime {
}
}
impl IsoDateSlots for JsObject<PlainDateTime> {
fn iso_date(&self) -> IsoDate {
self.borrow().data().inner.iso_date()
}
}
impl GetTemporalCalendar for JsObject<PlainDateTime> {
fn get_calendar(&self) -> Calendar {
self.borrow().data().inner.get_calendar()
}
}
impl BuiltInObject for PlainDateTime {
const NAME: JsString = StaticJsStrings::PLAIN_DATETIME_NAME;
}
@ -558,7 +543,7 @@ impl PlainDateTime {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
Ok(dt.inner.week_of_year()?.into())
Ok(dt.inner.week_of_year()?.into_or_undefined())
}
/// 5.3.17 get `Temporal.PlainDatedt.prototype.yearOfWeek`
@ -570,7 +555,7 @@ impl PlainDateTime {
JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
})?;
Ok(dt.inner.year_of_week()?.into())
Ok(dt.inner.year_of_week()?.into_or_undefined())
}
/// 5.3.18 get `Temporal.PlainDatedt.prototype.daysInWeek`
@ -1034,18 +1019,17 @@ pub(crate) fn to_temporal_datetime(
}
// g. Let result be ? InterpretTemporalDateTimeFields(calendarRec, fields, resolvedOptions).
let overflow = get_option::<ArithmeticOverflow>(&options, js_string!("overflow"), context)?;
let date = calendar.date_from_fields(
&mut TemporalFields::from(partial_date),
let date = calendar.date_from_partial(
&partial_date,
overflow.unwrap_or(ArithmeticOverflow::Constrain),
)?;
let time = Time::new(
let time = PlainTime::new(
partial_time.hour.unwrap_or(0),
partial_time.minute.unwrap_or(0),
partial_time.second.unwrap_or(0),
partial_time.millisecond.unwrap_or(0),
partial_time.microsecond.unwrap_or(0),
partial_time.nanosecond.unwrap_or(0),
ArithmeticOverflow::Constrain,
)?;
return InnerDateTime::new(

108
core/engine/src/builtins/temporal/plain_month_day/mod.rs

@ -13,18 +13,16 @@ use crate::{
property::Attribute,
realm::Realm,
string::StaticJsStrings,
Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol,
JsValue,
};
use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
use temporal_rs::{
components::{
calendar::{Calendar, GetTemporalCalendar},
DateTime, MonthDay as InnerMonthDay,
},
iso::IsoDateSlots,
options::{ArithmeticOverflow, CalendarName},
partial::PartialDate,
PlainDateTime, PlainMonthDay as InnerMonthDay, TinyAsciiStr,
};
use super::{calendar::to_temporal_calendar_slot_value, DateTimeValues};
@ -118,17 +116,6 @@ impl PlainMonthDay {
Ok(month_day_to_string(inner, show_calendar))
}
}
impl IsoDateSlots for JsObject<PlainMonthDay> {
fn iso_date(&self) -> temporal_rs::iso::IsoDate {
self.borrow().data().inner.iso_date()
}
}
impl GetTemporalCalendar for JsObject<PlainMonthDay> {
fn get_calendar(&self) -> Calendar {
self.borrow().data().inner.get_calendar()
}
}
impl BuiltInObject for PlainMonthDay {
const NAME: JsString = StaticJsStrings::PLAIN_MD_NAME;
@ -196,9 +183,33 @@ impl BuiltInConstructor for PlainMonthDay {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
Err(JsNativeError::range()
.with_message("Not yet implemented.")
.into())
// 1. If NewTarget is undefined, then
if new_target.is_undefined() {
// a. Throw a TypeError exception.
return Err(JsNativeError::typ()
.with_message("NewTarget cannot be undefined when constructing a PlainYearMonth.")
.into());
}
let year = args.get_or_undefined(3);
let ref_year = if year.is_undefined() {
None
} else {
Some(super::to_integer_with_truncation(year, context)?)
};
// We can ignore 2 as the underlying temporal library handles the reference year
let m = super::to_integer_with_truncation(args.get_or_undefined(0), context)?;
let d = super::to_integer_with_truncation(args.get_or_undefined(1), context)?;
let calendar = to_temporal_calendar_slot_value(args.get_or_undefined(2))?;
let inner = InnerMonthDay::new_with_overflow(
m,
d,
calendar,
ArithmeticOverflow::Constrain,
ref_year,
)?;
create_temporal_month_day(inner, Some(new_target), context)
}
}
@ -214,6 +225,7 @@ fn month_day_to_string(inner: &InnerMonthDay, show_calendar: CalendarName) -> Js
// 3. Let result be the string-concatenation of month and the code unit 0x002D (HYPHEN-MINUS).
let mut result = format!("{month:0>2}-{day:0>2}");
// 4. Let calendarId be monthDay.[[Calendar]].[[id]].
let calendar_id = inner.calendar().identifier();
// 5. Let calendar be monthDay.[[Calendar]].
@ -227,12 +239,13 @@ fn month_day_to_string(inner: &InnerMonthDay, show_calendar: CalendarName) -> Js
CalendarName::Critical | CalendarName::Always | CalendarName::Auto
)) && !(matches!(show_calendar, CalendarName::Auto) && calendar_id == "iso8601")
{
let year = inner.iso_year().to_string();
let flag = if matches!(show_calendar, CalendarName::Critical) {
"!"
} else {
""
};
result.push_str(&format!("[{flag}c={calendar_id}]",));
result = format!("{year}-{result}[{flag}u-ca={calendar_id}]");
}
// 8. Return result.
js_string!(result).into()
@ -245,7 +258,7 @@ pub(crate) fn create_temporal_month_day(
) -> JsResult<JsValue> {
// 1. If IsValidISODate(referenceISOYear, isoMonth, isoDay) is false, throw a RangeError exception.
// 2. If ISODateTimeWithinLimits(referenceISOYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
if !DateTime::validate(&inner) {
if !PlainDateTime::validate(&inner) {
return Err(JsNativeError::range()
.with_message("PlainMonthDay does not hold a valid ISO date time.")
.into());
@ -301,18 +314,45 @@ fn to_temporal_month_day(
} else if let Some(item_string) = item.as_string() {
InnerMonthDay::from_str(item_string.to_std_string_escaped().as_str())?
} else if item.is_object() {
InnerMonthDay::new(
item.get_v(js_string!("month"), context)
.expect("Month not found")
.to_i32(context)
.expect("Cannot convert month to i32"),
item.get_v(js_string!("day"), context)
.expect("Day not found")
.to_i32(context)
.expect("Cannot convert day to i32"),
calendar,
overflow,
)?
let day = item
.get_v(js_string!("day"), context)
.expect("Day not found")
.to_i32(context)
.expect("Cannot convert day to i32");
let month = item
.get_v(js_string!("month"), context)
.expect("Month not found")
.to_i32(context)
.expect("Cannot convert month to i32");
let month_code = item
.get_v(js_string!("monthCode"), context)
.expect("monthCode not found");
let resolved_month_code = if month_code.is_undefined() {
None
} else {
TinyAsciiStr::<4>::from_str(
&month_code
.to_string(context)
.expect("Cannot convert monthCode to string")
.to_std_string_escaped(),
)
.map_err(|e| JsError::from(JsNativeError::range().with_message(e.to_string())))
.ok()
};
let year = item.get_v(js_string!("year"), context).map_or(1972, |val| {
val.to_i32(context).expect("Cannot convert year to i32")
});
let partial_date = &PartialDate {
month: Some(month),
day: Some(day),
year: Some(year),
month_code: resolved_month_code,
..Default::default()
};
calendar.month_day_from_partial(partial_date, overflow)?
} else {
return Err(JsNativeError::typ()
.with_message("item must be an object or a string")

30
core/engine/src/builtins/temporal/plain_time/mod.rs

@ -16,8 +16,9 @@ use crate::{
use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
use temporal_rs::{
components::{PartialTime, Time},
options::{ArithmeticOverflow, TemporalRoundingMode},
partial::PartialTime,
PlainTime as PlainTimeInner,
};
use super::{
@ -31,7 +32,7 @@ use super::{
// Safety: Time does not contain any traceable types.
#[boa_gc(unsafe_empty_trace)]
pub struct PlainTime {
inner: Time,
inner: PlainTimeInner,
}
impl BuiltInObject for PlainTime {
@ -184,15 +185,8 @@ impl BuiltInConstructor for PlainTime {
.transpose()?
.unwrap_or(0);
let inner = Time::new(
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
ArithmeticOverflow::Reject,
)?;
let inner =
PlainTimeInner::new(hour, minute, second, millisecond, microsecond, nanosecond)?;
// 8. Return ? CreateTemporalTime(hour, minute, second, millisecond, microsecond, nanosecond, NewTarget).
create_temporal_time(inner, Some(new_target), context).map(Into::into)
@ -597,7 +591,7 @@ impl PlainTime {
// ==== PlainTime Abstract Operations ====
pub(crate) fn create_temporal_time(
inner: Time,
inner: PlainTimeInner,
new_target: Option<&JsValue>,
context: &mut Context,
) -> JsResult<JsObject> {
@ -633,12 +627,14 @@ pub(crate) fn create_temporal_time(
Ok(obj)
}
/// 4.5.3 `ToTemporalTime ( item [ , overflow ] )`
pub(crate) fn to_temporal_time(
value: &JsValue,
overflow: Option<ArithmeticOverflow>,
context: &mut Context,
) -> JsResult<Time> {
) -> JsResult<PlainTimeInner> {
// 1.If overflow is not present, set overflow to "constrain".
let resolved_overflow = overflow.unwrap_or(ArithmeticOverflow::Constrain);
// 2. If item is an Object, then
match value {
JsValue::Object(object) => {
@ -662,7 +658,7 @@ pub(crate) fn to_temporal_time(
// i. Return ! CreateTemporalTime(item.[[ISOHour]], item.[[ISOMinute]],
// item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]],
// item.[[ISONanosecond]]).
return Ok(Time::from(dt.inner.clone()));
return Ok(PlainTimeInner::from(dt.inner.clone()));
}
// d. Let result be ? ToTemporalTimeRecord(item).
// e. Set result to ? RegulateTime(result.[[Hour]], result.[[Minute]],
@ -670,14 +666,14 @@ pub(crate) fn to_temporal_time(
// result.[[Nanosecond]], overflow).
let partial = to_partial_time_record(object, context)?;
Time::new(
PlainTimeInner::new_with_overflow(
partial.hour.unwrap_or(0),
partial.minute.unwrap_or(0),
partial.second.unwrap_or(0),
partial.millisecond.unwrap_or(0),
partial.microsecond.unwrap_or(0),
partial.nanosecond.unwrap_or(0),
overflow.unwrap_or(ArithmeticOverflow::Constrain),
resolved_overflow,
)
.map_err(Into::into)
}
@ -686,7 +682,7 @@ pub(crate) fn to_temporal_time(
// b. Let result be ? ParseTemporalTimeString(item).
// c. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
str.to_std_string_escaped()
.parse::<Time>()
.parse::<PlainTimeInner>()
.map_err(Into::into)
}
// a. If item is not a String, throw a TypeError exception.

26
core/engine/src/builtins/temporal/plain_year_month/mod.rs

@ -19,12 +19,8 @@ use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
use temporal_rs::{
components::{
calendar::{Calendar as InnerCalendar, GetTemporalCalendar},
Duration, YearMonth as InnerYearMonth,
},
iso::IsoDateSlots,
options::{ArithmeticOverflow, CalendarName},
Duration, PlainYearMonth as InnerYearMonth,
};
use super::{calendar::to_temporal_calendar_slot_value, to_temporal_duration, DateTimeValues};
@ -42,18 +38,6 @@ impl PlainYearMonth {
}
}
impl IsoDateSlots for JsObject<PlainYearMonth> {
fn iso_date(&self) -> temporal_rs::iso::IsoDate {
self.borrow().data().inner.iso_date()
}
}
impl GetTemporalCalendar for JsObject<PlainYearMonth> {
fn get_calendar(&self) -> InnerCalendar {
self.borrow().data().inner.get_calendar()
}
}
impl BuiltInObject for PlainYearMonth {
const NAME: JsString = StaticJsStrings::PLAIN_YM_NAME;
}
@ -203,7 +187,8 @@ impl BuiltInConstructor for PlainYearMonth {
let calendar = to_temporal_calendar_slot_value(args.get_or_undefined(2))?;
// 7. Return ? CreateTemporalYearMonth(y, m, calendar, ref, NewTarget).
let inner = InnerYearMonth::new(y, m, ref_day, calendar, ArithmeticOverflow::Reject)?;
let inner =
InnerYearMonth::new_with_overflow(y, m, ref_day, calendar, ArithmeticOverflow::Reject)?;
create_temporal_year_month(inner, Some(new_target), context)
}
@ -235,7 +220,7 @@ impl PlainYearMonth {
// a. Let calendar be ? ToTemporalCalendar(item).
let calendar = to_temporal_calendar_slot_value(args.get_or_undefined(1))?;
InnerYearMonth::new(
InnerYearMonth::new_with_overflow(
super::to_integer_with_truncation(
&item.get_v(js_string!("year"), context)?,
context,
@ -298,7 +283,8 @@ impl PlainYearMonth {
.into());
};
Ok(js_string!(year_month.get_calendar().identifier()).into())
let calendar = year_month.borrow().data().inner.calendar().clone();
Ok(js_string!(calendar.identifier()).into())
}
fn get_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {

2
core/engine/src/builtins/temporal/zoned_date_time/mod.rs

@ -9,7 +9,7 @@ use crate::{
};
use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
use temporal_rs::components::{Duration as TemporalDuration, ZonedDateTime as InnerZdt};
use temporal_rs::{Duration as TemporalDuration, ZonedDateTime as InnerZdt};
/// The `Temporal.ZonedDateTime` object.
#[derive(Debug, Clone, Trace, Finalize, JsData)]

Loading…
Cancel
Save