Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First portion of the Temporal implementation #3277

Merged
merged 32 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7330da9
Started with the Temporal implementation
Razican Apr 23, 2023
4ebb098
Implemented some useful functions
Razican Apr 23, 2023
37e509d
Updaating some spec references
Razican Apr 23, 2023
4f3414b
Initial work on TimeZone and Instant
nekevss May 5, 2023
16afd23
More work completed on Temporal.Duration and Temporal.Instant
nekevss May 7, 2023
e2e9883
General scaffolding and heavy work on Instant and Duration complete
nekevss May 14, 2023
0da33f4
ZonedDateTime and Calendar started with further work on duration abst…
nekevss May 17, 2023
69d957a
Further work on temporal work and clippy fixes
nekevss Jun 2, 2023
e4d2b8c
Post rebase fixes/reverts
nekevss Jun 4, 2023
5e54f0d
Add BuiltinCalendar and begin IsoCalendar impl
nekevss Aug 17, 2023
5de602f
More work completed on calendar/date/yearmonth/monthday
nekevss Aug 26, 2023
d71b896
Calendar and iso impl close to completion - no datelike parsing
nekevss Sep 5, 2023
529687b
Initial work on temporal ISO8601 parsing and grammar
nekevss Sep 11, 2023
8d497c4
Post rebase fixes and updates
nekevss Sep 11, 2023
168c323
More on parser/Duration and work through clippy lints
nekevss Sep 14, 2023
2fa4e10
Fix bug on peek_n and add temporal cfg
nekevss Sep 14, 2023
bfee590
Fix clippy lints on parser tests
nekevss Sep 14, 2023
d2568ed
Build out calendar with icu_calendar, add some tests, and misc.
nekevss Sep 19, 2023
30f1372
Fix spec hyperlinks
jedel1043 Sep 19, 2023
b173fcd
Parser clean up and invalid annotations
nekevss Sep 20, 2023
94cea39
Add Duration and Temporal Parsing
nekevss Sep 21, 2023
1b39512
Remove IsoYearMonthRecord
nekevss Sep 21, 2023
2e692ab
Post rebase update
nekevss Sep 21, 2023
aecf583
Fix and add to ISO Parser docs
nekevss Sep 22, 2023
2762c03
Parser/ast cleanup and duration refactor/additions
nekevss Sep 27, 2023
b5a6e4e
Review feedback, options update, and duration changes
nekevss Sep 29, 2023
6eb3b15
Review changes, general cleanup, and post rebase fixes
nekevss Sep 30, 2023
9db8b47
Fix time zone parsing issue/test logic
nekevss Sep 30, 2023
b52631b
Clean up parse output nodes
nekevss Oct 3, 2023
dc311e6
Apply review feedback and various fixes
nekevss Oct 5, 2023
c127bb1
Review feedback and get_option changes
nekevss Oct 5, 2023
26f6334
Review feedback
nekevss Oct 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boa_ast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ rust-version.workspace = true
[features]
serde = ["dep:serde", "boa_interner/serde", "bitflags/serde", "num-bigint/serde"]
arbitrary = ["dep:arbitrary", "boa_interner/arbitrary", "num-bigint/arbitrary"]
experimental = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other cases, we have used more specific feature names (such as intl). Should we just name the feature temporal?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We opted for this approach because temporal would be removed anyways when it reaches stage 4. Do other engines use a broad experimental flag for Temporal, or a specific temporal flag?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense... but going forward, we might want to have features specific to some JavaScript characteristics (maybe a feature per built-in, things like that), so it might not hurt to have a temporal feature, and experimental would just depend on temporal. Could make it easier to support other experimental features in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, since it allows users to reduce binary size if they need to.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the feature flag update be included in this PR or a different PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be done separately :)


[dependencies]
boa_interner.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions boa_ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub mod operations;
pub mod pattern;
pub mod property;
pub mod statement;
#[cfg(feature = "experimental")]
pub mod temporal;
pub mod visitor;

use boa_interner::{Interner, ToIndentedString, ToInternedString};
Expand Down
113 changes: 113 additions & 0 deletions boa_ast/src/temporal/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//! AST nodes for Temporal's implementation of ISO8601 grammar.

/// An ISO Date Node consisting of non-validated date fields and calendar value.
#[derive(Default, Debug)]
pub struct IsoDate {
/// Date Year
pub year: i32,
/// Date Month
pub month: i32,
/// Date Day
pub day: i32,
/// The calendar value.
pub calendar: Option<String>,
}

/// The `IsoTime` node consists of non-validated time fields.
#[derive(Default, Debug, Clone, Copy)]
pub struct IsoTime {
/// An hour value between 0-23
pub hour: u8,
/// A minute value between 0-59
pub minute: u8,
/// A second value between 0-60
pub second: u8,
/// A millisecond value between 0-999
pub millisecond: u16,
/// A microsecond value between 0-999
pub microsecond: u16,
/// A nanosecond value between 0-999
pub nanosecond: u16,
}

impl IsoTime {
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
/// A utility initialization function to create `ISOTime` from the `TimeSpec` components.
pub fn from_components(hour: u8, minute: u8, second: u8, fraction: f64) -> Self {
// Note: Precision on nanoseconds drifts, so opting for round over floor or ceil for now.
// e.g. 0.329402834 becomes 329.402833.999
let millisecond = fraction * 1000.0;
let micros = millisecond.rem_euclid(1.0) * 1000.0;
let nanos = micros.rem_euclid(1.0) * 1000.0;

Self {
hour,
minute,
second,
millisecond: millisecond.floor() as u16,
microsecond: micros.floor() as u16,
nanosecond: nanos.round() as u16,
}
}
}

/// The `IsoDateTime` node output by the ISO parser
#[derive(Default, Debug)]
pub struct IsoDateTime {
/// The `ISODate` record
pub date: IsoDate,
/// The `ISOTime` record
pub time: IsoTime,
/// The `TimeZone` value for this `ISODateTime`
pub tz: Option<TimeZone>,
}

/// `TimeZone` data
#[derive(Default, Debug, Clone)]
pub struct TimeZone {
/// TimeZoneIANAName
pub name: Option<String>,
/// TimeZoneOffset
pub offset: Option<UTCOffset>,
}

/// A full precision `UtcOffset`
#[derive(Debug, Clone, Copy)]
pub struct UTCOffset {
/// The `+`/`-` sign of this `UtcOffset`
pub sign: i8,
/// The hour value of the `UtcOffset`
pub hour: u8,
/// The minute value of the `UtcOffset`.
pub minute: u8,
/// The second value of the `UtcOffset`.
pub second: u8,
/// Any sub second components of the `UTCOffset`
pub fraction: f64,
}

/// An `IsoDuration` Node output by the ISO parser.
#[derive(Debug, Default, Clone, Copy)]
pub struct IsoDuration {
/// Years value.
pub years: i32,
/// Months value.
pub months: i32,
/// Weeks value.
pub weeks: i32,
/// Days value.
pub days: i32,
/// Hours value.
pub hours: i32,
/// Minutes value.
pub minutes: f64,
/// Seconds value.
pub seconds: f64,
/// Milliseconds value.
pub milliseconds: f64,
/// Microseconds value.
pub microseconds: f64,
/// Nanoseconds value.
pub nanoseconds: f64,
}
2 changes: 1 addition & 1 deletion boa_engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ trace = []
annex-b = ["boa_parser/annex-b"]

# Enable experimental features, like Stage 3 proposals.
experimental = []
experimental = ["boa_parser/experimental", "dep:icu_calendar"]

[dependencies]
boa_interner.workspace = true
Expand Down
11 changes: 11 additions & 0 deletions boa_engine/src/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,17 @@ impl JsBigInt {
Self::new(x.inner.as_ref().clone().add(y.inner.as_ref()))
}

/// Utility function for performing `+` operation on more than two values.
#[inline]
#[cfg(feature = "experimental")]
pub(crate) fn add_n(values: &[Self]) -> Self {
let mut result = Self::zero();
for big_int in values {
result = Self::add(&result, big_int);
}
result
}

/// Performs the `-` operation.
#[inline]
#[must_use]
Expand Down
2 changes: 1 addition & 1 deletion boa_engine/src/builtins/date/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! [spec]: https://tc39.es/ecma262/#sec-date-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date

mod utils;
pub(crate) mod utils;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was the visibility of utils changed? Whats now using it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

14.8.1 GetUTCEpochNanoseconds uses the preexisting MakeDay and MakeTime.

use utils::{make_date, make_day, make_time, replace_params, time_clip, DateParameters};

#[cfg(test)]
Expand Down
6 changes: 3 additions & 3 deletions boa_engine/src/builtins/date/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub(super) const fn day_from_year(year: i64) -> i64 {
/// Abstract operation [`MakeTime`][spec].
///
/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-maketime
pub(super) fn make_time(hour: i64, min: i64, sec: i64, ms: i64) -> Option<i64> {
pub(crate) fn make_time(hour: i64, min: i64, sec: i64, ms: i64) -> Option<i64> {
// 1. If hour is not finite or min is not finite or sec is not finite or ms is not finite, return NaN.
// 2. Let h be 𝔽(! ToIntegerOrInfinity(hour)).
// 3. Let m be 𝔽(! ToIntegerOrInfinity(min)).
Expand All @@ -59,7 +59,7 @@ pub(super) fn make_time(hour: i64, min: i64, sec: i64, ms: i64) -> Option<i64> {
/// Abstract operation [`MakeDay`][spec].
///
/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-makeday
pub(super) fn make_day(mut year: i64, mut month: i64, date: i64) -> Option<i64> {
pub(crate) fn make_day(mut year: i64, mut month: i64, date: i64) -> Option<i64> {
// 1. If year is not finite or month is not finite or date is not finite, return NaN.
// 2. Let y be 𝔽(! ToIntegerOrInfinity(year)).
// 3. Let m be 𝔽(! ToIntegerOrInfinity(month)).
Expand Down Expand Up @@ -101,7 +101,7 @@ pub(super) fn make_day(mut year: i64, mut month: i64, date: i64) -> Option<i64>
/// Abstract operation [`MakeDate`][spec].
///
/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-makedate
pub(super) fn make_date(day: i64, time: i64) -> Option<i64> {
pub(crate) fn make_date(day: i64, time: i64) -> Option<i64> {
// 1. If day is not finite or time is not finite, return NaN.
// 2. Let tv be day × msPerDay + time.
// 3. If tv is not finite, return NaN.
Expand Down
24 changes: 24 additions & 0 deletions boa_engine/src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ pub mod intl;
#[cfg(any(feature = "intl", feature = "experimental"))]
pub(crate) mod options;

#[cfg(feature = "experimental")]
pub mod temporal;

pub(crate) use self::{
array::Array,
async_function::AsyncFunction,
Expand Down Expand Up @@ -275,6 +278,22 @@ impl Realm {
intl::segmenter::SegmentIterator::init(self);
intl::PluralRules::init(self);
}

#[cfg(feature = "experimental")]
{
temporal::TimeZone::init(self);
temporal::Temporal::init(self);
temporal::Now::init(self);
temporal::Instant::init(self);
temporal::Duration::init(self);
temporal::PlainDate::init(self);
temporal::PlainTime::init(self);
temporal::PlainDateTime::init(self);
temporal::PlainMonthDay::init(self);
temporal::PlainYearMonth::init(self);
temporal::ZonedDateTime::init(self);
temporal::Calendar::init(self);
}
}
}

Expand Down Expand Up @@ -374,6 +393,11 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult
#[cfg(feature = "intl")]
global_binding::<intl::Intl>(context)?;

#[cfg(feature = "experimental")]
{
global_binding::<temporal::Temporal>(context)?;
}

Ok(())
}

Expand Down
47 changes: 47 additions & 0 deletions boa_engine/src/builtins/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,53 @@ pub(crate) enum RoundingMode {
HalfEven,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum UnsignedRoundingMode {
Infinity,
Zero,
HalfInfinity,
HalfZero,
HalfEven,
}

impl RoundingMode {
pub(crate) const fn negate(self) -> Self {
use RoundingMode::{
Ceil, Expand, Floor, HalfCeil, HalfEven, HalfExpand, HalfFloor, HalfTrunc, Trunc,
};

match self {
Ceil => Self::Floor,
Floor => Self::Ceil,
HalfCeil => Self::HalfFloor,
HalfFloor => Self::HalfCeil,
Trunc => Self::Trunc,
Expand => Self::Expand,
HalfTrunc => Self::HalfTrunc,
HalfExpand => Self::HalfExpand,
HalfEven => Self::HalfEven,
}
}

pub(crate) const fn get_unsigned_round_mode(self, is_negative: bool) -> UnsignedRoundingMode {
use RoundingMode::{
Ceil, Expand, Floor, HalfCeil, HalfEven, HalfExpand, HalfFloor, HalfTrunc, Trunc,
};

match self {
Ceil if !is_negative => UnsignedRoundingMode::Infinity,
Ceil => UnsignedRoundingMode::Zero,
Floor if !is_negative => UnsignedRoundingMode::Zero,
Floor | Trunc | Expand => UnsignedRoundingMode::Infinity,
HalfCeil if !is_negative => UnsignedRoundingMode::HalfInfinity,
HalfCeil | HalfTrunc => UnsignedRoundingMode::HalfZero,
HalfFloor if !is_negative => UnsignedRoundingMode::HalfZero,
HalfFloor | HalfExpand => UnsignedRoundingMode::HalfInfinity,
HalfEven => UnsignedRoundingMode::HalfEven,
}
}
}

#[derive(Debug)]
pub(crate) struct ParseRoundingModeError;

Expand Down
Loading
Loading