Skip to content

Commit 08ac2ab

Browse files
authored
feat(ecmascript): implement Date constructor and prototype methods (#586)
1 parent 48ea659 commit 08ac2ab

File tree

10 files changed

+2327
-756
lines changed

10 files changed

+2327
-756
lines changed

nova_vm/src/builtin_strings

+1-1
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ unregister
461461
unscopables
462462
unshift
463463
URIError
464-
utc
464+
UTC
465465
value
466466
valueOf
467467
values

nova_vm/src/ecmascript/abstract_operations/type_conversion.rs

+20
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,26 @@ impl IntegerOrInfinity {
449449
}
450450
}
451451

452+
/// ### [7.1.5 ToIntegerOrInfinity ( argument )](https://tc39.es/ecma262/#sec-tointegerorinfinity)
453+
pub(crate) fn to_integer_or_infinity_f64(number: f64) -> f64 {
454+
// `ToIntegerOrInfinity ( argument )`
455+
if number.is_nan() || number == 0.0 {
456+
// 2. If number is NaN, +0𝔽, or -0𝔽, return 0.
457+
0.0
458+
} else if number == f64::INFINITY {
459+
// 3. If number is +∞𝔽, return +∞.
460+
f64::INFINITY
461+
} else if number == f64::NEG_INFINITY {
462+
// 4. If number is -∞𝔽, return -∞.
463+
f64::NEG_INFINITY
464+
} else {
465+
// 5. Let integer be floor(abs(ℝ(number))).
466+
// 6. If number < +0𝔽, set integer to -integer.
467+
// 7. Return integer.
468+
number.trunc()
469+
}
470+
}
471+
452472
/// ### [7.1.5 ToIntegerOrInfinity ( argument )](https://tc39.es/ecma262/#sec-tointegerorinfinity)
453473
pub(crate) fn to_integer_or_infinity(
454474
agent: &mut Agent,

nova_vm/src/ecmascript/builtins/date.rs

+14
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub(crate) mod data;
66

77
use core::ops::{Index, IndexMut};
88

9+
use data::DateValue;
10+
911
use crate::{
1012
ecmascript::{
1113
execution::{Agent, ProtoIntrinsics},
@@ -38,6 +40,18 @@ impl Date<'_> {
3840
Scoped::new(agent, self.unbind(), gc)
3941
}
4042

43+
/// ### get [[DateValue]]
44+
#[inline]
45+
pub(crate) fn date_value(self, agent: &Agent) -> DateValue {
46+
agent[self].date
47+
}
48+
49+
/// ### set [[DateValue]]
50+
#[inline]
51+
pub(crate) fn set_date_value(self, agent: &mut Agent, date: DateValue) {
52+
agent[self].date = date;
53+
}
54+
4155
pub(crate) const fn _def() -> Self {
4256
Self(DateIndex::from_u32_index(0))
4357
}

nova_vm/src/ecmascript/builtins/date/data.rs

+78-4
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,97 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5+
use std::time::SystemTime;
6+
57
use crate::{
6-
ecmascript::types::OrdinaryObject,
8+
SmallInteger,
9+
ecmascript::types::{IntoValue, OrdinaryObject, Value},
710
heap::{CompactionLists, HeapMarkAndSweep, WorkQueues},
811
};
9-
use std::time::SystemTime;
12+
13+
/// ### [21.4.1.1 Time Values and Time Range](https://tc39.es/ecma262/#sec-time-values-and-time-range)
14+
///
15+
/// A Number can exactly represent all integers from -9,007,199,254,740,992
16+
/// to 9,007,199,254,740,992 (21.1.2.8 and 21.1.2.6). A time value supports
17+
/// a slightly smaller range of -8,640,000,000,000,000 to 8,640,000,000,000,000 milliseconds.
18+
/// This yields a supported time value range of exactly -100,000,000 days
19+
/// to 100,000,000 days relative to midnight at the beginning of 1 January 1970 UTC.
20+
///
21+
/// In that case, the time value can be either:
22+
///
23+
/// - Invalid, which is presented as `i64::MAX`
24+
/// - An integer in the range of -8,640,000,000,000,000 to 8,640,000,000,000,000,
25+
/// which is represented as a non-max `i64`, and can also fit in `SmallInteger`
26+
#[derive(Debug, Clone, Copy)]
27+
#[repr(transparent)]
28+
pub(crate) struct DateValue(i64);
29+
30+
impl DateValue {
31+
pub const NAN: Self = Self(i64::MAX);
32+
33+
pub fn get_i64(self) -> Option<i64> {
34+
if self.0 == i64::MAX {
35+
None
36+
} else {
37+
Some(self.0)
38+
}
39+
}
40+
41+
pub fn get_f64(self) -> Option<f64> {
42+
self.get_i64().map(|v| v as f64)
43+
}
44+
45+
pub fn now() -> Self {
46+
let now = SystemTime::now();
47+
let now = now
48+
.duration_since(SystemTime::UNIX_EPOCH)
49+
.expect("Time went backwards")
50+
.as_millis();
51+
Self(now as i64)
52+
}
53+
}
54+
55+
/// ### [21.4.1.31 TimeClip ( time )](https://tc39.es/ecma262/#sec-timeclip)
56+
///
57+
/// The abstract operation TimeClip takes argument time (a Number) and returns
58+
/// a Number. It calculates a number of milliseconds.
59+
pub(crate) fn time_clip(time: f64) -> DateValue {
60+
// 1. If time is not finite, return NaN.
61+
if !time.is_finite() {
62+
return DateValue::NAN;
63+
}
64+
65+
// 2. If abs(ℝ(time)) > 8.64 × 10**15, return NaN.
66+
if time.abs() > 8.64e15 {
67+
return DateValue::NAN;
68+
}
69+
70+
// 3. Return 𝔽(! ToIntegerOrInfinity(time)).
71+
DateValue(time.trunc() as i64)
72+
}
73+
74+
impl<'a> IntoValue<'a> for DateValue {
75+
fn into_value(self) -> Value<'a> {
76+
if let Some(value) = self.get_f64() {
77+
// SAFETY: `value` is guaranteed to be in the range of `SmallInteger`.
78+
Value::Integer(SmallInteger::try_from(value).unwrap())
79+
} else {
80+
Value::nan()
81+
}
82+
}
83+
}
1084

1185
#[derive(Debug, Clone, Copy)]
1286
pub struct DateHeapData {
1387
pub(crate) object_index: Option<OrdinaryObject<'static>>,
14-
pub(crate) date: Option<SystemTime>,
88+
pub(crate) date: DateValue,
1589
}
1690

1791
impl DateHeapData {
1892
pub(crate) fn new_invalid() -> Self {
1993
Self {
2094
object_index: None,
21-
date: None,
95+
date: DateValue::NAN,
2296
}
2397
}
2498
}

0 commit comments

Comments
 (0)