Browse Source

Refactor the `Date` builtin (#2449)

Just a general cleanup of the `Date` builtin to use slightly better patterns and to fix our warnings about deprecated functions.

About the regressed tests. It seems to be a `chrono` bug, so I opened up an issue (https://github.com/chronotope/chrono/issues/884) for it and they've already opened a PR fixing it (https://github.com/chronotope/chrono/pull/885).

However, while checking out the remaining failing tests, I realized there's a more fundamental limitation with the library. Currently, [`chrono`](https://github.com/chronotope/chrono) specifies:

> Date types are limited in about +/- 262,000 years from the common epoch.

While the [ECMAScript spec](https://tc39.es/ecma262/#sec-time-values-and-time-range) says:

> The smaller range supported by a time value as specified in this section is approximately -273,790 to 273,790 years relative to 1970.

The range allowed by the spec is barely outside of the range supported by `chrono`! This is why the remaining `Date` tests fail.
Seeing that, I would like to ping @djc and @esheppa (the maintainers of `chrono`) to ask if it would be feasible to add a feature, akin to the `large-dates` feature from the `time` crate, that expands the supported range of `chrono`.

EDIT: Filed https://github.com/chronotope/chrono/issues/886
pull/2459/head
José Julián Espina 2 years ago
parent
commit
1ae48441fd
  1. 2430
      boa_engine/src/builtins/date/mod.rs
  2. 711
      boa_engine/src/builtins/date/tests.rs
  3. 200
      boa_engine/src/builtins/date/utils.rs
  4. 24
      boa_engine/src/builtins/set/mod.rs
  5. 6
      boa_engine/src/builtins/set/ordered_set.rs
  6. 2
      boa_engine/src/builtins/weak/weak_ref.rs
  7. 1
      boa_engine/src/context/mod.rs
  8. 112
      boa_engine/src/object/builtins/jsdate.rs
  9. 11
      boa_engine/src/object/mod.rs
  10. 26
      boa_engine/src/value/integer.rs
  11. 26
      boa_engine/src/value/mod.rs
  12. 3
      boa_engine/src/vm/mod.rs

2430
boa_engine/src/builtins/date/mod.rs

File diff suppressed because it is too large Load Diff

711
boa_engine/src/builtins/date/tests.rs

File diff suppressed because it is too large Load Diff

200
boa_engine/src/builtins/date/utils.rs

@ -0,0 +1,200 @@
use chrono::{Datelike, Local, NaiveDateTime, TimeZone, Timelike};
use crate::value::IntegerOrNan;
/// The absolute maximum value of a timestamp
pub(super) const MAX_TIMESTAMP: i64 = 864 * 10i64.pow(13);
/// The number of milliseconds in a second.
pub(super) const MILLIS_PER_SECOND: i64 = 1000;
/// The number of milliseconds in a minute.
pub(super) const MILLIS_PER_MINUTE: i64 = MILLIS_PER_SECOND * 60;
/// The number of milliseconds in an hour.
pub(super) const MILLIS_PER_HOUR: i64 = MILLIS_PER_MINUTE * 60;
/// The number of milliseconds in a day.
pub(super) const MILLIS_PER_DAY: i64 = MILLIS_PER_HOUR * 24;
// https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-time-values-and-time-range
//
// The smaller range supported by a time value as specified in this section is approximately -273,790 to 273,790
// years relative to 1970.
pub(super) const MIN_YEAR: i64 = -300_000;
pub(super) const MAX_YEAR: i64 = -MIN_YEAR;
pub(super) const MIN_MONTH: i64 = MIN_YEAR * 12;
pub(super) const MAX_MONTH: i64 = MAX_YEAR * 12;
/// Calculates the absolute day number from the year number.
pub(super) const fn day_from_year(year: i64) -> i64 {
// Taken from https://chromium.googlesource.com/v8/v8/+/refs/heads/main/src/date/date.cc#496
// Useful to avoid negative divisions and overflows on 32-bit platforms (if we plan to support them).
const YEAR_DELTA: i64 = 399_999;
const fn day(year: i64) -> i64 {
let year = year + YEAR_DELTA;
365 * year + year / 4 - year / 100 + year / 400
}
assert!(MIN_YEAR <= year && year <= MAX_YEAR);
day(year) - day(1970)
}
/// 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> {
// 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)).
// 4. Let s be 𝔽(! ToIntegerOrInfinity(sec)).
// 5. Let milli be 𝔽(! ToIntegerOrInfinity(ms)).
// 6. Let t be ((h * msPerHour + m * msPerMinute) + s * msPerSecond) + milli, performing the arithmetic according to IEEE 754-2019 rules (that is, as if using the ECMAScript operators * and +).
// 7. Return t.
let h_ms = hour.checked_mul(MILLIS_PER_HOUR)?;
let m_ms = min.checked_mul(MILLIS_PER_MINUTE)?;
let s_ms = sec.checked_mul(MILLIS_PER_SECOND)?;
h_ms.checked_add(m_ms)?.checked_add(s_ms)?.checked_add(ms)
}
/// 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> {
// 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)).
// 4. Let dt be 𝔽(! ToIntegerOrInfinity(date)).
if !(MIN_YEAR..=MAX_YEAR).contains(&year) || !(MIN_MONTH..=MAX_MONTH).contains(&month) {
return None;
}
// At this point, we've already asserted that year and month are much less than its theoretical
// maximum and minimum values (i64::MAX/MIN), so we don't need to do checked operations.
// 5. Let ym be y + 𝔽(floor(ℝ(m) / 12)).
// 6. If ym is not finite, return NaN.
year += month / 12;
// 7. Let mn be 𝔽(ℝ(m) modulo 12).
month %= 12;
if month < 0 {
month += 12;
year -= 1;
}
// 8. Find a finite time value t such that YearFromTime(t) is ym and MonthFromTime(t) is mn and DateFromTime(t) is
// 1𝔽; but if this is not possible (because some argument is out of range), return NaN.
let month = usize::try_from(month).expect("month must be between 0 and 11 at this point");
let mut day = day_from_year(year);
// Consider leap years when calculating the cumulative days added to the year from the input month
if (year % 4 != 0) || (year % 100 == 0 && year % 400 != 0) {
day += [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][month];
} else {
day += [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335][month];
}
// 9. Return Day(t) + dt - 1𝔽.
(day - 1).checked_add(date)
}
/// 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> {
// 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.
// 4. Return tv.
day.checked_mul(MILLIS_PER_DAY)?.checked_add(time)
}
/// Abstract operation [`TimeClip`][spec]
/// Returns the timestamp (number of milliseconds) if it is in the expected range.
/// Otherwise, returns `None`.
///
/// [spec]: https://tc39.es/ecma262/#sec-timeclip
#[inline]
pub(super) fn time_clip(time: i64) -> Option<i64> {
// 1. If time is not finite, return NaN.
// 2. If abs(ℝ(time)) > 8.64 × 10^15, return NaN.
// 3. Return 𝔽(! ToIntegerOrInfinity(time)).
(time.checked_abs()? <= MAX_TIMESTAMP).then_some(time)
}
#[derive(Default, Debug, Clone, Copy)]
pub(super) struct DateParameters {
pub(super) year: Option<IntegerOrNan>,
pub(super) month: Option<IntegerOrNan>,
pub(super) date: Option<IntegerOrNan>,
pub(super) hour: Option<IntegerOrNan>,
pub(super) minute: Option<IntegerOrNan>,
pub(super) second: Option<IntegerOrNan>,
pub(super) millisecond: Option<IntegerOrNan>,
}
/// Replaces some (or all) parameters of `date` with the specified parameters
pub(super) fn replace_params(
datetime: NaiveDateTime,
params: DateParameters,
local: bool,
) -> Option<NaiveDateTime> {
let DateParameters {
year,
month,
date,
hour,
minute,
second,
millisecond,
} = params;
let datetime = if local {
Local.from_utc_datetime(&datetime).naive_local()
} else {
datetime
};
let year = match year {
Some(i) => i.as_integer()?,
None => i64::from(datetime.year()),
};
let month = match month {
Some(i) => i.as_integer()?,
None => i64::from(datetime.month() - 1),
};
let date = match date {
Some(i) => i.as_integer()?,
None => i64::from(datetime.day()),
};
let hour = match hour {
Some(i) => i.as_integer()?,
None => i64::from(datetime.hour()),
};
let minute = match minute {
Some(i) => i.as_integer()?,
None => i64::from(datetime.minute()),
};
let second = match second {
Some(i) => i.as_integer()?,
None => i64::from(datetime.second()),
};
let millisecond = match millisecond {
Some(i) => i.as_integer()?,
None => i64::from(datetime.timestamp_subsec_millis()),
};
let new_day = make_day(year, month, date)?;
let new_time = make_time(hour, minute, second, millisecond)?;
let mut ts = make_date(new_day, new_time)?;
if local {
ts = Local
.from_local_datetime(&NaiveDateTime::from_timestamp_millis(ts)?)
.earliest()?
.naive_utc()
.timestamp_millis();
}
NaiveDateTime::from_timestamp_millis(time_clip(ts)?)
}

24
boa_engine/src/builtins/set/mod.rs

@ -251,20 +251,18 @@ impl Set {
/// [spec]: https://tc39.es/ecma262/#sec-set.prototype.clear /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.clear
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear
pub(crate) fn clear(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { pub(crate) fn clear(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
if let Some(object) = this.as_object() { let mut object = this
if object.borrow().is_set() { .as_object()
this.set_data(ObjectData::set(OrderedSet::new())); .map(JsObject::borrow_mut)
.ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Set"))?;
let set = object
.as_set_mut()
.ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Set"))?;
set.clear();
Ok(JsValue::undefined()) Ok(JsValue::undefined())
} else {
Err(JsNativeError::typ()
.with_message("'this' is not a Set")
.into())
}
} else {
Err(JsNativeError::typ()
.with_message("'this' is not a Set")
.into())
}
} }
/// `Set.prototype.delete( value )` /// `Set.prototype.delete( value )`

6
boa_engine/src/builtins/set/ordered_set.rs

@ -90,6 +90,12 @@ where
self.inner.shift_remove(value) self.inner.shift_remove(value)
} }
/// Removes all elements in the set, while preserving its capacity.
#[inline]
pub fn clear(&mut self) {
self.inner.clear();
}
/// Checks if a given value is present in the set /// Checks if a given value is present in the set
/// ///
/// Return `true` if `value` is present in set, false otherwise. /// Return `true` if `value` is present in set, false otherwise.

2
boa_engine/src/builtins/weak/weak_ref.rs

@ -161,6 +161,6 @@ mod tests {
) )
.unwrap(), .unwrap(),
JsValue::undefined() JsValue::undefined()
) );
} }
} }

1
boa_engine/src/context/mod.rs

@ -641,6 +641,7 @@ impl ContextBuilder {
/// ///
/// This function is only available if the `fuzz` feature is enabled. /// This function is only available if the `fuzz` feature is enabled.
#[cfg(feature = "fuzz")] #[cfg(feature = "fuzz")]
#[must_use]
pub fn instructions_remaining(mut self, instructions_remaining: usize) -> Self { pub fn instructions_remaining(mut self, instructions_remaining: usize) -> Self {
self.instructions_remaining = instructions_remaining; self.instructions_remaining = instructions_remaining;
self self

112
boa_engine/src/object/builtins/jsdate.rs

@ -1,11 +1,12 @@
use std::ops::Deref; use std::ops::Deref;
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
use chrono::DateTime;
use crate::{ use crate::{
builtins::Date, builtins::Date,
object::{JsObject, JsObjectType}, object::{JsObject, JsObjectType, ObjectData},
Context, JsResult, JsValue, Context, JsNativeError, JsResult, JsValue,
}; };
/// `JsDate` is a wrapper for JavaScript `JsDate` builtin object /// `JsDate` is a wrapper for JavaScript `JsDate` builtin object
@ -39,7 +40,8 @@ impl JsDate {
/// Create a new `Date` object with universal time. /// Create a new `Date` object with universal time.
#[inline] #[inline]
pub fn new(context: &mut Context) -> Self { pub fn new(context: &mut Context) -> Self {
let inner = Date::date_create(None, context); let prototype = context.intrinsics().constructors().date().prototype();
let inner = JsObject::from_proto_and_data(prototype, ObjectData::date(Date::default()));
Self { inner } Self { inner }
} }
@ -79,7 +81,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getDate()`. /// Same as JavaScript's `Date.prototype.getDate()`.
#[inline] #[inline]
pub fn get_date(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_date(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_date(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_date::<true>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the day of the week (0–6) for the specified date /// Returns the day of the week (0–6) for the specified date
@ -88,7 +90,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getDay()`. /// Same as JavaScript's `Date.prototype.getDay()`.
#[inline] #[inline]
pub fn get_day(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_day(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_day(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_day::<true>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the year (4 digits for 4-digit years) of the specified date /// Returns the year (4 digits for 4-digit years) of the specified date
@ -97,7 +99,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getFullYear()`. /// Same as JavaScript's `Date.prototype.getFullYear()`.
#[inline] #[inline]
pub fn get_full_year(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_full_year(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_full_year(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_full_year::<true>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the hour (0–23) in the specified date according to local time. /// Returns the hour (0–23) in the specified date according to local time.
@ -105,7 +107,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getHours()`. /// Same as JavaScript's `Date.prototype.getHours()`.
#[inline] #[inline]
pub fn get_hours(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_hours(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_hours(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_hours::<true>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the milliseconds (0–999) in the specified date according /// Returns the milliseconds (0–999) in the specified date according
@ -114,7 +116,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getMilliseconds()`. /// Same as JavaScript's `Date.prototype.getMilliseconds()`.
#[inline] #[inline]
pub fn get_milliseconds(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_milliseconds(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_milliseconds(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_milliseconds::<true>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the minutes (0–59) in the specified date according to local time. /// Returns the minutes (0–59) in the specified date according to local time.
@ -122,7 +124,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getMinutes()`. /// Same as JavaScript's `Date.prototype.getMinutes()`.
#[inline] #[inline]
pub fn get_minutes(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_minutes(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_minutes(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_minutes::<true>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the month (0–11) in the specified date according to local time. /// Returns the month (0–11) in the specified date according to local time.
@ -130,7 +132,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getMonth()`. /// Same as JavaScript's `Date.prototype.getMonth()`.
#[inline] #[inline]
pub fn get_month(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_month(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_month(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_month::<true>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the seconds (0–59) in the specified date according to local time. /// Returns the seconds (0–59) in the specified date according to local time.
@ -138,7 +140,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getSeconds()`. /// Same as JavaScript's `Date.prototype.getSeconds()`.
#[inline] #[inline]
pub fn get_seconds(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_seconds(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_seconds(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_seconds::<true>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the numeric value of the specified date as the number /// Returns the numeric value of the specified date as the number
@ -165,7 +167,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getUTCDate()`. /// Same as JavaScript's `Date.prototype.getUTCDate()`.
#[inline] #[inline]
pub fn get_utc_date(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_utc_date(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_date(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_date::<false>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the day of the week (0–6) in the specified /// Returns the day of the week (0–6) in the specified
@ -174,7 +176,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getUTCDay()`. /// Same as JavaScript's `Date.prototype.getUTCDay()`.
#[inline] #[inline]
pub fn get_utc_day(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_utc_day(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_day(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_day::<false>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the year (4 digits for 4-digit years) in the specified /// Returns the year (4 digits for 4-digit years) in the specified
@ -183,7 +185,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getUTCFullYear()`. /// Same as JavaScript's `Date.prototype.getUTCFullYear()`.
#[inline] #[inline]
pub fn get_utc_full_year(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_utc_full_year(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_full_year(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_full_year::<false>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the hours (0–23) in the specified date according /// Returns the hours (0–23) in the specified date according
@ -192,7 +194,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getUTCHours()`. /// Same as JavaScript's `Date.prototype.getUTCHours()`.
#[inline] #[inline]
pub fn get_utc_hours(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_utc_hours(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_hours(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_hours::<false>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the milliseconds (0–999) in the specified date /// Returns the milliseconds (0–999) in the specified date
@ -201,7 +203,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getUTCMilliseconds()`. /// Same as JavaScript's `Date.prototype.getUTCMilliseconds()`.
#[inline] #[inline]
pub fn get_utc_milliseconds(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_utc_milliseconds(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_milliseconds(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_milliseconds::<false>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the minutes (0–59) in the specified date according /// Returns the minutes (0–59) in the specified date according
@ -210,7 +212,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getUTCMinutes()`. /// Same as JavaScript's `Date.prototype.getUTCMinutes()`.
#[inline] #[inline]
pub fn get_utc_minutes(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_utc_minutes(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_minutes(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_minutes::<false>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the month (0–11) in the specified date according /// Returns the month (0–11) in the specified date according
@ -219,7 +221,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getUTCMonth()`. /// Same as JavaScript's `Date.prototype.getUTCMonth()`.
#[inline] #[inline]
pub fn get_utc_month(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_utc_month(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_month(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_month::<false>(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Returns the seconds (0–59) in the specified date according /// Returns the seconds (0–59) in the specified date according
@ -228,19 +230,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.getUTCSeconds()`. /// Same as JavaScript's `Date.prototype.getUTCSeconds()`.
#[inline] #[inline]
pub fn get_utc_seconds(&self, context: &mut Context) -> JsResult<JsValue> { pub fn get_utc_seconds(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_seconds(&self.inner.clone().into(), &[JsValue::null()], context) Date::get_seconds::<false>(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// DEPRECATED: This feature is no longer recommended.
/// USE: `get_full_year()` instead.
/// Returns the year (usually 2–3 digits) in the specified date
/// according to local time.
///
/// Same as JavaScript's `Date.prototype.getYear()`.
#[deprecated]
#[inline]
pub fn get_year(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_year(&self.inner.clone().into(), &[JsValue::null()], context)
} }
/// Sets the day of the month for a specified date according /// Sets the day of the month for a specified date according
@ -255,7 +245,7 @@ impl JsDate {
where where
T: Into<JsValue>, T: Into<JsValue>,
{ {
Date::set_date(&self.inner.clone().into(), &[value.into()], context) Date::set_date::<true>(&self.inner.clone().into(), &[value.into()], context)
} }
/// Sets the full year (e.g. 4 digits for 4-digit years) for a /// Sets the full year (e.g. 4 digits for 4-digit years) for a
@ -267,7 +257,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.setFullYear()`. /// Same as JavaScript's `Date.prototype.setFullYear()`.
#[inline] #[inline]
pub fn set_full_year(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> { pub fn set_full_year(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_full_year(&self.inner.clone().into(), values, context) Date::set_full_year::<true>(&self.inner.clone().into(), values, context)
} }
/// Sets the hours for a specified date according to local time. /// Sets the hours for a specified date according to local time.
@ -278,7 +268,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.setHours()`. /// Same as JavaScript's `Date.prototype.setHours()`.
#[inline] #[inline]
pub fn set_hours(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> { pub fn set_hours(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_hours(&self.inner.clone().into(), values, context) Date::set_hours::<true>(&self.inner.clone().into(), values, context)
} }
/// Sets the milliseconds for a specified date according to local time. /// Sets the milliseconds for a specified date according to local time.
@ -292,7 +282,7 @@ impl JsDate {
where where
T: Into<JsValue>, T: Into<JsValue>,
{ {
Date::set_milliseconds(&self.inner.clone().into(), &[value.into()], context) Date::set_milliseconds::<true>(&self.inner.clone().into(), &[value.into()], context)
} }
/// Sets the minutes for a specified date according to local time. /// Sets the minutes for a specified date according to local time.
@ -303,7 +293,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.setMinutes()`. /// Same as JavaScript's `Date.prototype.setMinutes()`.
#[inline] #[inline]
pub fn set_minutes(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> { pub fn set_minutes(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_minutes(&self.inner.clone().into(), values, context) Date::set_minutes::<true>(&self.inner.clone().into(), values, context)
} }
/// Sets the month for a specified date according to local time. /// Sets the month for a specified date according to local time.
@ -314,7 +304,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.setMonth()`. /// Same as JavaScript's `Date.prototype.setMonth()`.
#[inline] #[inline]
pub fn set_month(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> { pub fn set_month(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_month(&self.inner.clone().into(), values, context) Date::set_month::<true>(&self.inner.clone().into(), values, context)
} }
/// Sets the seconds for a specified date according to local time. /// Sets the seconds for a specified date according to local time.
@ -325,7 +315,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.setSeconds()`. /// Same as JavaScript's `Date.prototype.setSeconds()`.
#[inline] #[inline]
pub fn set_seconds(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> { pub fn set_seconds(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_seconds(&self.inner.clone().into(), values, context) Date::set_seconds::<true>(&self.inner.clone().into(), values, context)
} }
/// Sets the Date object to the time represented by a number /// Sets the Date object to the time represented by a number
@ -356,7 +346,7 @@ impl JsDate {
where where
T: Into<JsValue>, T: Into<JsValue>,
{ {
Date::set_utc_date(&self.inner.clone().into(), &[value.into()], context) Date::set_date::<false>(&self.inner.clone().into(), &[value.into()], context)
} }
/// Sets the full year (e.g. 4 digits for 4-digit years) for a /// Sets the full year (e.g. 4 digits for 4-digit years) for a
@ -372,7 +362,7 @@ impl JsDate {
values: &[JsValue], values: &[JsValue],
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
Date::set_utc_full_year(&self.inner.clone().into(), values, context) Date::set_full_year::<false>(&self.inner.clone().into(), values, context)
} }
/// Sets the hours for a specified date according to universal time. /// Sets the hours for a specified date according to universal time.
@ -383,7 +373,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.setUTCHours()`. /// Same as JavaScript's `Date.prototype.setUTCHours()`.
#[inline] #[inline]
pub fn set_utc_hours(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> { pub fn set_utc_hours(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_utc_hours(&self.inner.clone().into(), values, context) Date::set_hours::<false>(&self.inner.clone().into(), values, context)
} }
/// Sets the milliseconds for a specified date according to universal time. /// Sets the milliseconds for a specified date according to universal time.
@ -397,7 +387,7 @@ impl JsDate {
where where
T: Into<JsValue>, T: Into<JsValue>,
{ {
Date::set_utc_milliseconds(&self.inner.clone().into(), &[value.into()], context) Date::set_milliseconds::<false>(&self.inner.clone().into(), &[value.into()], context)
} }
/// Sets the minutes for a specified date according to universal time. /// Sets the minutes for a specified date according to universal time.
@ -408,7 +398,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.setUTCMinutes()`. /// Same as JavaScript's `Date.prototype.setUTCMinutes()`.
#[inline] #[inline]
pub fn set_utc_minutes(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> { pub fn set_utc_minutes(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_utc_minutes(&self.inner.clone().into(), values, context) Date::set_minutes::<false>(&self.inner.clone().into(), values, context)
} }
/// Sets the month for a specified date according to universal time. /// Sets the month for a specified date according to universal time.
@ -419,7 +409,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.setUTCMonth()`. /// Same as JavaScript's `Date.prototype.setUTCMonth()`.
#[inline] #[inline]
pub fn set_utc_month(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> { pub fn set_utc_month(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_month(&self.inner.clone().into(), values, context) Date::set_month::<false>(&self.inner.clone().into(), values, context)
} }
/// Sets the seconds for a specified date according to universal time. /// Sets the seconds for a specified date according to universal time.
@ -430,23 +420,7 @@ impl JsDate {
/// Same as JavaScript's `Date.prototype.setUTCSeconds()`. /// Same as JavaScript's `Date.prototype.setUTCSeconds()`.
#[inline] #[inline]
pub fn set_utc_seconds(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> { pub fn set_utc_seconds(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_utc_seconds(&self.inner.clone().into(), values, context) Date::set_seconds::<false>(&self.inner.clone().into(), values, context)
}
/// DEPRECATED: This feature is no longer recommended.
/// USE: `set_full_year()` instead.
/// Sets the year for a specified date according to local time.
/// Return a `Number` representing the milliseconds elapsed since
/// the UNIX epoch.
///
/// Same as JavaScript's legacy `Date.prototype.setYear()`.
#[deprecated]
#[inline]
pub fn set_year<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
Date::set_year(&self.inner.clone().into(), &[value.into()], context)
} }
/// Returns the "date" portion of the Date as a human-readable string. /// Returns the "date" portion of the Date as a human-readable string.
@ -554,9 +528,19 @@ impl JsDate {
/// Utility create a `Date` object from RFC3339 string /// Utility create a `Date` object from RFC3339 string
#[inline] #[inline]
pub fn new_from_parse(value: &JsValue, context: &mut Context) -> Self { pub fn new_from_parse(value: &JsValue, context: &mut Context) -> JsResult<Self> {
let inner = Date::create_obj(value, context); let prototype = context.intrinsics().constructors().date().prototype();
Self { inner } let string = value
.to_string(context)?
.to_std_string()
.map_err(|_| JsNativeError::typ().with_message("unpaired surrogate on date string"))?;
let date_time = DateTime::parse_from_rfc3339(&string)
.map_err(|err| JsNativeError::typ().with_message(err.to_string()))?;
let date_time = Date::new(Some(date_time.naive_local()));
Ok(Self {
inner: JsObject::from_proto_and_data(prototype, ObjectData::date(date_time)),
})
} }
} }

11
boa_engine/src/object/mod.rs

@ -1188,6 +1188,17 @@ impl Object {
} }
} }
#[inline]
pub fn as_date_mut(&mut self) -> Option<&mut Date> {
match self.data {
ObjectData {
kind: ObjectKind::Date(ref mut date),
..
} => Some(date),
_ => None,
}
}
/// Checks if it a `RegExp` object. /// Checks if it a `RegExp` object.
#[inline] #[inline]
pub fn is_regexp(&self) -> bool { pub fn is_regexp(&self) -> bool {

26
boa_engine/src/value/integer.rs

@ -1,6 +1,6 @@
use std::cmp::Ordering; use std::cmp::Ordering;
/// Represents the result of `ToIntegerOrInfinity` operation /// Represents the result of the `ToIntegerOrInfinity` operation
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum IntegerOrInfinity { pub enum IntegerOrInfinity {
PositiveInfinity, PositiveInfinity,
@ -90,3 +90,27 @@ impl PartialOrd<IntegerOrInfinity> for i64 {
} }
} }
} }
/// Represents the result of the `to_integer_or_nan` method.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum IntegerOrNan {
Integer(i64),
Nan,
}
impl IntegerOrNan {
/// Gets the wrapped `i64` if the variant is an `Integer`.
pub(crate) fn as_integer(self) -> Option<i64> {
match self {
Self::Integer(i) => Some(i),
Self::Nan => None,
}
}
}
impl From<IntegerOrInfinity> for IntegerOrNan {
fn from(ior: IntegerOrInfinity) -> Self {
ior.as_integer()
.map_or(IntegerOrNan::Nan, IntegerOrNan::Integer)
}
}

26
boa_engine/src/value/mod.rs

@ -44,6 +44,8 @@ pub use integer::IntegerOrInfinity;
pub use operations::*; pub use operations::*;
pub use r#type::Type; pub use r#type::Type;
pub(crate) use self::integer::IntegerOrNan;
static TWO_E_64: Lazy<BigInt> = Lazy::new(|| { static TWO_E_64: Lazy<BigInt> = Lazy::new(|| {
const TWO_E_64: u128 = 2u128.pow(64); const TWO_E_64: u128 = 2u128.pow(64);
BigInt::from(TWO_E_64) BigInt::from(TWO_E_64)
@ -333,14 +335,6 @@ impl JsValue {
} }
} }
/// Set the kind of an object.
#[inline]
pub fn set_data(&self, data: ObjectData) {
if let Self::Object(ref obj) = *self {
obj.borrow_mut().data = data;
}
}
/// The abstract operation `ToPrimitive` takes an input argument and an optional argumen`PreferredType`pe. /// The abstract operation `ToPrimitive` takes an input argument and an optional argumen`PreferredType`pe.
/// ///
/// <https://tc39.es/ecma262/#sec-toprimitive> /// <https://tc39.es/ecma262/#sec-toprimitive>
@ -869,6 +863,22 @@ impl JsValue {
Ok(IntegerOrInfinity::from(number)) Ok(IntegerOrInfinity::from(number))
} }
/// Modified abstract operation `ToIntegerOrInfinity ( argument )`.
///
/// This function is almost the same as [`Self::to_integer_or_infinity`], but with the exception
/// that this will return `Nan` if [`Self::to_number`] returns a non-finite number.
pub(crate) fn to_integer_or_nan(&self, context: &mut Context) -> JsResult<IntegerOrNan> {
// 1. Let number be ? ToNumber(argument).
let number = self.to_number(context)?;
if number.is_nan() {
return Ok(IntegerOrNan::Nan);
}
// Continues on `IntegerOrInfinity::from::<f64>`
Ok(IntegerOrInfinity::from(number).into())
}
/// Converts a value to a double precision floating point. /// Converts a value to a double precision floating point.
/// ///
/// This function is equivalent to the unary `+` operator (`+value`) in JavaScript /// This function is equivalent to the unary `+` operator (`+value`) in JavaScript

3
boa_engine/src/vm/mod.rs

@ -182,9 +182,10 @@ impl Context {
while self.vm.frame().pc < self.vm.frame().code.code.len() { while self.vm.frame().pc < self.vm.frame().code.code.len() {
#[cfg(feature = "fuzz")] #[cfg(feature = "fuzz")]
{
if self.instructions_remaining == 0 { if self.instructions_remaining == 0 {
return Err(JsError::from_native(JsNativeError::no_instructions_remain())); return Err(JsError::from_native(JsNativeError::no_instructions_remain()));
} else { }
self.instructions_remaining -= 1; self.instructions_remaining -= 1;
} }

Loading…
Cancel
Save