Skip to content

Commit 93a45c0

Browse files
committed
fix: finish Temporal.Instant.prototype.toString impl
1 parent cf818d0 commit 93a45c0

File tree

4 files changed

+76
-33
lines changed

4 files changed

+76
-33
lines changed

nova_vm/src/ecmascript/builtins/temporal.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ pub mod instant;
88
pub mod options;
99
pub mod plain_time;
1010

11-
use temporal_rs::options::{DifferenceSettings, RoundingIncrement, RoundingMode, Unit, UnitGroup};
11+
use temporal_rs::{
12+
options::{DifferenceSettings, RoundingIncrement, RoundingMode, Unit, UnitGroup},
13+
parsers::Precision,
14+
};
1215

1316
use crate::{
1417
ecmascript::{
@@ -41,7 +44,7 @@ impl Temporal {
4144
let plain_time_constructor = intrinsics.temporal_plain_time();
4245

4346
OrdinaryObjectBuilder::new_intrinsic_object(agent, realm, this)
44-
.with_property_capacity(3)
47+
.with_property_capacity(4)
4548
.with_prototype(object_prototype)
4649
// 1.2.1 Temporal.Instant ( . . . )
4750
.with_property(|builder| {
@@ -93,6 +96,7 @@ trivially_bindable!(UnitGroup);
9396
trivially_bindable!(Unit);
9497
trivially_bindable!(RoundingMode);
9598
trivially_bindable!(RoundingIncrement);
99+
trivially_bindable!(Precision);
96100

97101
/// [13.15 GetTemporalFractionalSecondDigitsOption ( options )](https://tc39.es/proposal-temporal/#sec-temporal-gettemporalfractionalseconddigitsoption)
98102
/// The abstract operation GetTemporalFractionalSecondDigitsOption takes argument
@@ -104,10 +108,10 @@ pub(crate) fn get_temporal_fractional_second_digits_option<'gc>(
104108
agent: &mut Agent,
105109
options: Object<'gc>,
106110
mut gc: GcScope<'gc, '_>,
107-
) -> JsResult<'gc, Value<'gc>> {
111+
) -> JsResult<'gc, temporal_rs::parsers::Precision> {
108112
let options = options.bind(gc.nogc());
109113
// 1. Let digitsValue be ? Get(options, "fractionalSecondDigits").
110-
let digits_value = get(
114+
let mut digits_value = get(
111115
agent,
112116
options.unbind(),
113117
BUILTIN_STRING_MEMORY
@@ -118,26 +122,35 @@ pub(crate) fn get_temporal_fractional_second_digits_option<'gc>(
118122
.unbind()?
119123
.bind(gc.nogc());
120124
// 2. If digitsValue is undefined, return auto.
121-
if digits_value.unbind().is_undefined().bind(gc.nogc()) {
122-
return Ok(BUILTIN_STRING_MEMORY.auto.into_value());
125+
if digits_value.is_undefined() {
126+
return Ok(temporal_rs::parsers::Precision::Auto);
127+
}
128+
if let Value::Integer(digits_value) = digits_value
129+
&& (0..=9).contains(&digits_value.into_i64())
130+
{
131+
return Ok(temporal_rs::parsers::Precision::Digit(
132+
digits_value.into_i64() as u8,
133+
));
123134
}
124-
let digits_value = digits_value.unbind();
125135
// 3. If digitsValue is not a Number, then
126136
if !digits_value.is_number() {
137+
let scoped_digits_value = digits_value.scope(agent, gc.nogc());
127138
// a. If ? ToString(digitsValue) is not "auto", throw a RangeError exception.
128139
if digits_value
140+
.unbind()
129141
.to_string(agent, gc.reborrow())
130142
.unbind()?
131143
.as_bytes(agent)
132144
!= b"auto"
133145
{
134146
// b. Return auto.
135-
return Ok(BUILTIN_STRING_MEMORY.auto.into_value());
147+
return Ok(temporal_rs::parsers::Precision::Auto);
136148
}
149+
// SAFETY: not shared.
150+
digits_value = unsafe { scoped_digits_value.take(agent) }.bind(gc.nogc());
137151
}
138-
let digits_value = digits_value.bind(gc.nogc());
139152
// 4. If digitsValue is NaN, +∞𝔽, or -∞𝔽, throw a RangeError exception.
140-
if digits_value.unbind().is_nan(agent)
153+
if digits_value.is_nan(agent)
141154
|| digits_value.is_pos_infinity(agent)
142155
|| digits_value.is_neg_infinity(agent)
143156
{
@@ -153,17 +166,17 @@ pub(crate) fn get_temporal_fractional_second_digits_option<'gc>(
153166
.to_number(agent, gc.reborrow())
154167
.unbind()?
155168
.bind(gc.nogc());
156-
let digit_count = digit_count.into_f64(agent).floor() as i32;
169+
let digit_count = digit_count.into_f64(agent).floor();
157170
// 6. If digitCount < 0 or digitCount > 9, throw a RangeError exception.
158-
if digit_count < 0 || digit_count > 9 {
171+
if digit_count < 0.0 || digit_count > 9.0 {
159172
return Err(agent.throw_exception_with_static_message(
160173
ExceptionType::RangeError,
161174
"fractionalSecondDigits must be between 0 and 9",
162175
gc.into_nogc(),
163176
));
164177
}
165178
// 7. Return digitCount.
166-
Ok(Number::from_i64(agent, digit_count.into(), gc.into_nogc()).into_value())
179+
Ok(temporal_rs::parsers::Precision::Digit(digit_count as u8))
167180
}
168181

169182
/// [13.42 GetDifferenceSettings ( operation, options, unitGroup, disallowedUnits, fallbackSmallestUnit, smallestLargestDefaultUnit )](https://tc39.es/proposal-temporal/#sec-temporal-getdifferencesettings)

nova_vm/src/ecmascript/builtins/temporal/duration/duration_constructor.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use crate::{
22
ecmascript::{
33
builders::builtin_function_builder::BuiltinFunctionBuilder,
4-
builtins::{
5-
ArgumentsList, Behaviour, Builtin, BuiltinIntrinsicConstructor
6-
},
4+
builtins::{ArgumentsList, Behaviour, Builtin, BuiltinIntrinsicConstructor},
75
execution::{Agent, JsResult, Realm},
86
types::{BUILTIN_STRING_MEMORY, IntoObject, Object, String, Value},
97
},
@@ -40,7 +38,7 @@ impl TemporalDurationConstructor {
4038
BuiltinFunctionBuilder::new_intrinsic_constructor::<TemporalDurationConstructor>(
4139
agent, realm,
4240
)
43-
.with_property_capacity(5)
41+
.with_property_capacity(1)
4442
.with_prototype_property(duration_prototype.into_object())
4543
.build();
4644
}

nova_vm/src/ecmascript/builtins/temporal/duration/duration_prototype.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ pub(crate) struct TemporalDurationPrototype;
1212
impl TemporalDurationPrototype {
1313
pub fn create_intrinsic(agent: &mut Agent, realm: Realm<'static>, _: NoGcScope) {
1414
let intrinsics = agent.get_realm_record_by_id(realm).intrinsics();
15-
let this = intrinsics.temporal_instant_prototype();
15+
let this = intrinsics.temporal_duration_prototype();
1616
let object_prototype = intrinsics.object_prototype();
1717
let duration_constructor = intrinsics.temporal_duration();
1818

1919
OrdinaryObjectBuilder::new_intrinsic_object(agent, realm, this)
20-
.with_property_capacity(15)
20+
.with_property_capacity(2)
2121
.with_prototype(object_prototype)
2222
.with_constructor_property(duration_constructor)
2323
.with_property(|builder| {

nova_vm/src/ecmascript/builtins/temporal/instant/instant_prototype.rs

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use temporal_rs::options::{RoundingMode, RoundingOptions, Unit};
1+
use temporal_rs::{
2+
TimeZone,
3+
options::{RoundingMode, RoundingOptions, ToStringRoundingOptions, Unit},
4+
};
25

36
use crate::{
47
ecmascript::{
@@ -33,7 +36,7 @@ use crate::{
3336
context::{Bindable, GcScope, NoGcScope},
3437
rootable::Scopable,
3538
},
36-
heap::WellKnownSymbolIndexes,
39+
heap::{CreateHeapData, WellKnownSymbolIndexes},
3740
};
3841

3942
pub(crate) struct TemporalInstantPrototype;
@@ -419,24 +422,27 @@ impl TemporalInstantPrototype {
419422
let options = args.get(0).bind(gc.nogc());
420423
let instant = this_value.bind(gc.nogc());
421424
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
422-
let _instant = require_internal_slot_temporal_instant(agent, instant.unbind(), gc.nogc())
425+
let instant = require_internal_slot_temporal_instant(agent, instant.unbind(), gc.nogc())
423426
.unbind()?
424-
.bind(gc.nogc());
427+
.scope(agent, gc.nogc());
425428
// 3. Let resolvedOptions be ? GetOptionsObject(options).
426429
let resolved_options = get_options_object(agent, options.unbind(), gc.nogc())
427430
.unbind()?
428431
.scope(agent, gc.nogc());
429-
// 4. NOTE: The following steps read options and perform independent validation in alphabetical order (GetTemporalFractionalSecondDigitsOption reads "fractionalSecondDigits" and GetRoundingModeOption reads "roundingMode").
432+
// 4. NOTE: The following steps read options and perform independent
433+
// validation in alphabetical order
434+
// (GetTemporalFractionalSecondDigitsOption reads
435+
// "fractionalSecondDigits" and GetRoundingModeOption reads
436+
// "roundingMode").
430437
// 5. Let digits be ? GetTemporalFractionalSecondDigitsOption(resolvedOptions).
431-
let _digits = get_temporal_fractional_second_digits_option(
438+
let digits = get_temporal_fractional_second_digits_option(
432439
agent,
433440
resolved_options.get(agent),
434441
gc.reborrow(),
435442
)
436-
.unbind()?
437-
.bind(gc.nogc());
443+
.unbind()?;
438444
// 6. Let roundingMode be ? GetRoundingModeOption(resolvedOptions, trunc).
439-
let _rounding_mode = get_rounding_mode_option(
445+
let rounding_mode = get_rounding_mode_option(
440446
agent,
441447
resolved_options.get(agent),
442448
RoundingMode::Trunc,
@@ -463,24 +469,50 @@ impl TemporalInstantPrototype {
463469
.unbind()?
464470
.bind(gc.nogc());
465471
// 9. Perform ? ValidateTemporalUnitValue(smallestUnit, time).
472+
if !smallest_unit.is_none_or(|su| su.is_time_unit()) {
473+
return Err(agent.throw_exception_with_static_message(
474+
ExceptionType::RangeError,
475+
"smallestUnit is not a valid time unit",
476+
gc.into_nogc(),
477+
));
478+
}
466479
// 10. If smallestUnit is hour, throw a RangeError exception.
467-
if smallest_unit.unwrap() == Unit::Hour {
480+
if smallest_unit == Some(Unit::Hour) {
468481
return Err(agent.throw_exception_with_static_message(
469482
ExceptionType::RangeError,
470483
"smallestUnit is hour",
471484
gc.into_nogc(),
472485
));
473486
}
474487
// 11. If timeZone is not undefined, then
475-
let _tz = if !tz.is_undefined() {
488+
let time_zone = if !tz.is_undefined() {
476489
// a. Set timeZone to ? ToTemporalTimeZoneIdentifier(timeZone).
477-
todo!()
490+
Some(TimeZone::utc())
491+
} else {
492+
None
478493
};
494+
let instant = unsafe { instant.take(agent) }.bind(gc.nogc());
479495
// 12. Let precision be ToSecondsStringPrecisionRecord(smallestUnit, digits).
480-
// 13. Let roundedNs be RoundTemporalInstant(instant.[[EpochNanoseconds]], precision.[[Increment]], precision.[[Unit]], roundingMode).
496+
// 13. Let roundedNs be RoundTemporalInstant(
497+
// instant.[[EpochNanoseconds]],
498+
// precision.[[Increment]],
499+
// precision.[[Unit]],
500+
// roundingMode
501+
// ).
481502
// 14. Let roundedInstant be ! CreateTemporalInstant(roundedNs).
482503
// 15. Return TemporalInstantToString(roundedInstant, timeZone, precision.[[Precision]]).
483-
unimplemented!()
504+
let options = ToStringRoundingOptions {
505+
precision: digits,
506+
smallest_unit,
507+
rounding_mode: Some(rounding_mode),
508+
};
509+
match instant
510+
.inner_instant(agent)
511+
.to_ixdtf_string(time_zone, options)
512+
{
513+
Ok(string) => Ok(Value::from_string(agent, string, gc.into_nogc())),
514+
Err(err) => Err(temporal_err_to_js_err(agent, err, gc.into_nogc())),
515+
}
484516
}
485517

486518
/// ### [8.3.12 Temporal.Instant.prototype.toLocaleString ( [ locales [ , options ] ] )](https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tolocalestring)

0 commit comments

Comments
 (0)