Skip to content

Commit

Permalink
Update tz components for new design (#3543)
Browse files Browse the repository at this point in the history
  • Loading branch information
nekevss authored Jan 5, 2024
1 parent 84a5e45 commit 0cb17cf
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 63 deletions.
76 changes: 76 additions & 0 deletions core/engine/src/builtins/temporal/time_zone/custom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//! A custom `TimeZone` object.
use crate::{property::PropertyKey, string::utf16, Context, JsObject, JsValue};

use boa_gc::{Finalize, Trace};
use boa_temporal::{
components::{tz::TzProtocol, Instant},
TemporalError, TemporalResult,
};
use num_bigint::BigInt;

#[derive(Debug, Clone, Trace, Finalize)]
pub(crate) struct JsCustomTimeZone {
tz: JsObject,
}

impl TzProtocol for JsCustomTimeZone {
fn get_offset_nanos_for(&self, ctx: &mut dyn std::any::Any) -> TemporalResult<BigInt> {
let context = ctx
.downcast_mut::<Context>()
.expect("Context was not provided for a CustomTz");

let method = self
.tz
.get(utf16!("getOffsetNanosFor"), context)
.expect("Method must exist for the custom calendar to be valid.");

let result = method
.as_callable()
.expect("is method")
.call(&method, &[], context)
.map_err(|e| TemporalError::general(e.to_string()))?;

// TODO (nekevss): Validate that the below conversion is fine vs. matching to JsValue::BigInt()
let Some(bigint) = result.as_bigint() else {
return Err(TemporalError::r#type()
.with_message("Expected BigInt return from getOffsetNanosFor"));
};

Ok(bigint.as_inner().clone())
}

fn get_possible_instant_for(
&self,
ctx: &mut dyn std::any::Any,
) -> TemporalResult<Vec<Instant>> {
let _context = ctx
.downcast_mut::<Context>()
.expect("Context was not provided for a CustomTz");

// TODO: Implement once Instant has been migrated to `boa_temporal`'s Instant.
Err(TemporalError::range().with_message("Not yet implemented."))
}

fn id(&self, ctx: &mut dyn std::any::Any) -> TemporalResult<String> {
let context = ctx
.downcast_mut::<Context>()
.expect("Context was not provided for a CustomTz");

let ident = self
.tz
.__get__(
&PropertyKey::from(utf16!("id")),
JsValue::undefined(),
&mut context.into(),
)
.expect("Method must exist for the custom calendar to be valid.");

let JsValue::String(id) = ident else {
return Err(
TemporalError::r#type().with_message("Invalid custom Time Zone identifier type.")
);
};

Ok(id.to_std_string_escaped())
}
}
30 changes: 22 additions & 8 deletions core/engine/src/builtins/temporal/time_zone/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Boa's implemetation of the `Temporal.TimeZone` builtin object.
#![allow(dead_code)]

use crate::{
Expand All @@ -13,16 +14,29 @@ use crate::{
string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
};
use boa_gc::{Finalize, Trace};
use boa_gc::{custom_trace, Finalize, Trace};
use boa_profiler::Profiler;
use boa_temporal::components::tz::{TimeZoneSlot, TzProtocol};
use boa_temporal::components::tz::TimeZoneSlot;

mod custom;

#[doc(inline)]
pub(crate) use custom::JsCustomTimeZone;

/// The `Temporal.TimeZone` object.
#[derive(Debug, Clone, Trace, Finalize, JsData)]
// SAFETY: `TimeZone` doesn't contain traceable data.
#[boa_gc(unsafe_empty_trace)]
#[derive(Debug, Clone, Finalize, JsData)]
pub struct TimeZone {
slot: TimeZoneSlot,
slot: TimeZoneSlot<JsCustomTimeZone>,
}

unsafe impl Trace for TimeZone {
custom_trace!(this, mark, {
match &this.slot {
TimeZoneSlot::Protocol(custom) => mark(custom),
// SAFETY: No values that are exposed to gc are in TZ
TimeZoneSlot::Tz(_) => {}
}
});
}

impl BuiltInObject for TimeZone {
Expand Down Expand Up @@ -143,7 +157,7 @@ impl TimeZone {
.ok_or_else(|| {
JsNativeError::typ().with_message("this value must be a Temporal.TimeZone")
})?;
Ok(JsString::from(tz.slot.id(context)).into())
Ok(JsString::from(tz.slot.id(context)?).into())
}

pub(crate) fn get_offset_nanoseconds_for(
Expand Down Expand Up @@ -257,7 +271,7 @@ impl TimeZone {
JsNativeError::typ().with_message("this value must be a Temporal.TimeZone")
})?;
// 3. Return timeZone.[[Identifier]].
Ok(JsString::from(tz.slot.id(context)).into())
Ok(JsString::from(tz.slot.id(context)?).into())
}
}

Expand Down
28 changes: 21 additions & 7 deletions core/engine/src/builtins/temporal/zoned_date_time/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,32 @@ use crate::{
string::common::StaticJsStrings,
Context, JsBigInt, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
};
use boa_gc::{Finalize, Trace};
use boa_gc::{custom_trace, Finalize, Trace};
use boa_profiler::Profiler;
use boa_temporal::components::{Duration as TemporalDuration, ZonedDateTime as InnerZdt};
use boa_temporal::components::{
calendar::CalendarSlot, tz::TimeZoneSlot, Duration as TemporalDuration,
ZonedDateTime as InnerZdt,
};

use super::JsCustomCalendar;
use super::{JsCustomCalendar, JsCustomTimeZone};

/// The `Temporal.ZonedDateTime` object.
#[derive(Debug, Clone, Finalize, Trace, JsData)]
// SAFETY: ZonedDateTime does not contain any traceable types.
#[boa_gc(unsafe_empty_trace)]
#[derive(Debug, Clone, Finalize, JsData)]
pub struct ZonedDateTime {
pub(crate) inner: InnerZdt<JsCustomCalendar>,
pub(crate) inner: InnerZdt<JsCustomCalendar, JsCustomTimeZone>,
}

unsafe impl Trace for ZonedDateTime {
custom_trace!(this, mark, {
match this.inner.calendar() {
CalendarSlot::Protocol(custom) => mark(custom),
CalendarSlot::Builtin(_) => {}
}
match this.inner.tz() {
TimeZoneSlot::Protocol(custom) => mark(custom),
TimeZoneSlot::Tz(_) => {}
}
});
}

impl BuiltInObject for ZonedDateTime {
Expand Down
6 changes: 3 additions & 3 deletions core/temporal/src/components/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
};
use std::{any::Any, str::FromStr};

use super::calendar::CalendarProtocol;
use super::{calendar::CalendarProtocol, tz::TzProtocol};

// ==== `DateDuration` ====

Expand Down Expand Up @@ -1161,15 +1161,15 @@ impl Duration {
/// seconds, milliseconds, microseconds, nanoseconds, increment, unit,
/// roundingMode [ , plainRelativeTo [, zonedRelativeTo [, precalculatedDateTime]]] )`
#[allow(clippy::type_complexity)]
pub fn round_duration<C: CalendarProtocol>(
pub fn round_duration<C: CalendarProtocol, Z: TzProtocol>(
&self,
unbalance_date_duration: DateDuration,
increment: f64,
unit: TemporalUnit,
rounding_mode: TemporalRoundingMode,
relative_targets: (
Option<&Date<C>>,
Option<&ZonedDateTime<C>>,
Option<&ZonedDateTime<C, Z>>,
Option<&DateTime<C>>,
),
context: &mut dyn Any,
Expand Down
64 changes: 29 additions & 35 deletions core/temporal/src/components/tz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,14 @@ use super::calendar::CalendarProtocol;
pub const TIME_ZONE_PROPERTIES: [&str; 3] =
["getOffsetNanosecondsFor", "getPossibleInstantsFor", "id"];

/// A clonable `TzProtocol`
pub trait TzProtocolClone {
/// Clones the current `TimeZoneProtocol`.
fn clone_box(&self) -> Box<dyn TzProtocol>;
}

impl<P> TzProtocolClone for P
where
P: 'static + TzProtocol + Clone,
{
fn clone_box(&self) -> Box<dyn TzProtocol> {
Box::new(self.clone())
}
}

/// The Time Zone Protocol that must be implemented for time zones.
pub trait TzProtocol: TzProtocolClone {
pub trait TzProtocol: Clone {
/// Get the Offset nanoseconds for this `TimeZone`
fn get_offset_nanos_for(&self, context: &mut dyn Any) -> TemporalResult<BigInt>;
/// Get the possible Instant for this `TimeZone`
fn get_possible_instant_for(&self, context: &mut dyn Any) -> TemporalResult<Vec<Instant>>; // TODO: Implement Instant
/// Get the `TimeZone`'s identifier.
fn id(&self, context: &mut dyn Any) -> String;
fn id(&self, context: &mut dyn Any) -> TemporalResult<String>;
}

/// A Temporal `TimeZone`.
Expand All @@ -50,14 +35,15 @@ pub struct TimeZone {
}

/// The `TimeZoneSlot` represents a `[[TimeZone]]` internal slot value.
pub enum TimeZoneSlot {
#[derive(Clone)]
pub enum TimeZoneSlot<Z: TzProtocol> {
/// A native `TimeZone` representation.
Tz(TimeZone),
/// A Custom `TimeZone` that implements the `TzProtocol`.
Protocol(Box<dyn TzProtocol>),
Protocol(Z),
}

impl core::fmt::Debug for TimeZoneSlot {
impl<Z: TzProtocol> core::fmt::Debug for TimeZoneSlot<Z> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Tz(tz) => write!(f, "{tz:?}"),
Expand All @@ -66,16 +52,7 @@ impl core::fmt::Debug for TimeZoneSlot {
}
}

impl Clone for TimeZoneSlot {
fn clone(&self) -> Self {
match self {
Self::Tz(tz) => Self::Tz(tz.clone()),
Self::Protocol(p) => Self::Protocol(p.clone_box()),
}
}
}

impl TimeZoneSlot {
impl<Z: TzProtocol> TimeZoneSlot<Z> {
pub(crate) fn get_datetime_for<C: CalendarProtocol>(
&self,
instant: &Instant,
Expand All @@ -87,8 +64,9 @@ impl TimeZoneSlot {
}
}

impl TzProtocol for TimeZoneSlot {
fn get_offset_nanos_for(&self, context: &mut dyn Any) -> TemporalResult<BigInt> {
impl<Z: TzProtocol> TimeZoneSlot<Z> {
/// Get the offset for this current `TimeZoneSlot`.
pub fn get_offset_nanos_for(&self, context: &mut dyn Any) -> TemporalResult<BigInt> {
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
// 3. Set instant to ? ToTemporalInstant(instant).
Expand All @@ -106,14 +84,30 @@ impl TzProtocol for TimeZoneSlot {
}
}

fn get_possible_instant_for(&self, _context: &mut dyn Any) -> TemporalResult<Vec<Instant>> {
/// Get the possible `Instant`s for this `TimeZoneSlot`.
pub fn get_possible_instant_for(&self, _context: &mut dyn Any) -> TemporalResult<Vec<Instant>> {
Err(TemporalError::general("Not yet implemented."))
}

fn id(&self, context: &mut dyn Any) -> String {
/// Returns the current `TimeZoneSlot`'s identifier.
pub fn id(&self, context: &mut dyn Any) -> TemporalResult<String> {
match self {
Self::Tz(_) => todo!("implement tz.to_string"),
Self::Tz(_) => Err(TemporalError::range().with_message("Not yet implemented.")), // TODO: Implement Display for Time Zone.
Self::Protocol(tz) => tz.id(context),
}
}
}

impl TzProtocol for () {
fn get_offset_nanos_for(&self, _: &mut dyn Any) -> TemporalResult<BigInt> {
unreachable!()
}

fn get_possible_instant_for(&self, _: &mut dyn Any) -> TemporalResult<Vec<Instant>> {
unreachable!()
}

fn id(&self, _: &mut dyn Any) -> TemporalResult<String> {
Ok("() TimeZone".to_owned())
}
}
Loading

0 comments on commit 0cb17cf

Please sign in to comment.