Skip to content

Commit 61b8e1d

Browse files
committed
fix: finish Temporal.Instant.prototype.toString impl
1 parent 6c9f50b commit 61b8e1d

File tree

4 files changed

+77
-44
lines changed

4 files changed

+77
-44
lines changed

nova_vm/src/ecmascript/builtins/temporal.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@ 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::{
1518
abstract_operations::operations_on_objects::get,
1619
builders::ordinary_object_builder::OrdinaryObjectBuilder,
1720
builtins::temporal::{
18-
instant::instant_prototype::{DefaultOption, get_temporal_unit_valued_option},
21+
instant::instant_prototype::get_temporal_unit_valued_option,
1922
options::{get_rounding_increment_option, get_rounding_mode_option},
2023
},
2124
execution::{Agent, JsResult, Realm, agent::ExceptionType},
@@ -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
@@ -119,20 +123,31 @@ pub(crate) fn get_temporal_fractional_second_digits_option<'gc>(
119123
.bind(gc.nogc());
120124
// 2. If digitsValue is undefined, return auto.
121125
if digits_value.is_undefined() {
122-
return Ok(BUILTIN_STRING_MEMORY.auto.bind(gc.nogc()).into_value());
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
}
124135
// 3. If digitsValue is not a Number, then
125136
if !digits_value.is_number() {
137+
let scoped_digits_value = digits_value.scope(agent, gc.nogc());
126138
// a. If ? ToString(digitsValue) is not "auto", throw a RangeError exception.
127139
if digits_value
140+
.unbind()
128141
.to_string(agent, gc.reborrow())
129142
.unbind()?
130143
.as_bytes(agent)
131144
!= b"auto"
132145
{
133146
// b. Return auto.
134-
return Ok(BUILTIN_STRING_MEMORY.auto.bind(gc.nogc()).into_value());
147+
return Ok(temporal_rs::parsers::Precision::Auto);
135148
}
149+
// SAFETY: not shared.
150+
digits_value = unsafe { scoped_digits_value.take(agent) }.bind(gc.nogc());
136151
}
137152
// 4. If digitsValue is NaN, +∞𝔽, or -∞𝔽, throw a RangeError exception.
138153
if digits_value.is_nan(agent)
@@ -150,17 +165,17 @@ pub(crate) fn get_temporal_fractional_second_digits_option<'gc>(
150165
.to_number(agent, gc.reborrow())
151166
.unbind()?
152167
.bind(gc.nogc());
153-
let digit_count = digit_count.into_f64(agent).floor() as i32;
168+
let digit_count = digit_count.into_f64(agent).floor();
154169
// 6. If digitCount < 0 or digitCount > 9, throw a RangeError exception.
155-
if digit_count < 0 || digit_count > 9 {
170+
if digit_count < 0.0 || digit_count > 9.0 {
156171
return Err(agent.throw_exception_with_static_message(
157172
ExceptionType::RangeError,
158173
"fractionalSecondDigits must be between 0 and 9",
159174
gc.into_nogc(),
160175
));
161176
}
162177
// 7. Return digitCount.
163-
Ok(Number::from_i64(agent, digit_count.into(), gc.nogc()).into_value())
178+
Ok(temporal_rs::parsers::Precision::Digit(digit_count as u8))
164179
}
165180

166181
/// [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: 48 additions & 28 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::{
@@ -420,80 +423,97 @@ impl TemporalInstantPrototype {
420423
let options = args.get(0).bind(gc.nogc());
421424
let instant = this_value.bind(gc.nogc());
422425
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
423-
<<<<<<< HEAD
424-
let instant = require_internal_slot_temporal_instant(agent, instant, gc.nogc())
425-
=======
426-
let _instant = require_internal_slot_temporal_instant(agent, instant.unbind(), gc.nogc())
427-
>>>>>>> cf818d02 (small check fixes)
426+
let instant = require_internal_slot_temporal_instant(agent, instant.unbind(), gc.nogc())
428427
.unbind()?
429-
.bind(gc.nogc());
428+
.scope(agent, gc.nogc());
430429
// 3. Let resolvedOptions be ? GetOptionsObject(options).
431430
let resolved_options = get_options_object(agent, options, gc.nogc())
432431
.unbind()?
433-
.bind(gc.nogc());
434-
// 4. NOTE: The following steps read options and perform independent validation in alphabetical order (GetTemporalFractionalSecondDigitsOption reads "fractionalSecondDigits" and GetRoundingModeOption reads "roundingMode").
432+
.scope(agent, gc.nogc());
433+
// 4. NOTE: The following steps read options and perform independent
434+
// validation in alphabetical order
435+
// (GetTemporalFractionalSecondDigitsOption reads
436+
// "fractionalSecondDigits" and GetRoundingModeOption reads
437+
// "roundingMode").
435438
// 5. Let digits be ? GetTemporalFractionalSecondDigitsOption(resolvedOptions).
436-
let _digits = get_temporal_fractional_second_digits_option(
439+
let digits = get_temporal_fractional_second_digits_option(
437440
agent,
438441
resolved_options.unbind(),
439442
gc.reborrow(),
440443
)
441-
.unbind()?
442-
.bind(gc.nogc());
444+
.unbind()?;
443445
// 6. Let roundingMode be ? GetRoundingModeOption(resolvedOptions, trunc).
444-
<<<<<<< HEAD
445-
let rounding_mode =
446-
get_rounding_mode_option(agent, resolved_options, RoundingMode::Trunc, gc.reborrow())
447-
.unbind()?
448-
.bind(gc.nogc());
449-
=======
450-
let _rounding_mode = get_rounding_mode_option(
446+
let rounding_mode = get_rounding_mode_option(
451447
agent,
452448
resolved_options.get(agent),
453449
RoundingMode::Trunc,
454450
gc.reborrow(),
455451
)
456452
.unbind()?
457453
.bind(gc.nogc());
458-
>>>>>>> cf818d02 (small check fixes)
459454
// 7. Let smallestUnit be ? GetTemporalUnitValuedOption(resolvedOptions, "smallestUnit", unset).
460455
let smallest_unit = get_temporal_unit_valued_option(
461456
agent,
462-
resolved_options,
457+
resolved_options.get(agent),
463458
BUILTIN_STRING_MEMORY.smallestUnit.to_property_key(),
464-
DefaultOption::Unset,
465459
gc.reborrow(),
466460
)
467461
.unbind()?
468462
.bind(gc.nogc());
469463
// 8. Let timeZone be ? Get(resolvedOptions, "timeZone").
470464
let tz = get(
471465
agent,
472-
resolved_options,
466+
resolved_options.get(agent),
473467
BUILTIN_STRING_MEMORY.timeZone.to_property_key(),
474468
gc.reborrow(),
475469
)
476470
.unbind()?
477471
.bind(gc.nogc());
478472
// 9. Perform ? ValidateTemporalUnitValue(smallestUnit, time).
473+
if !smallest_unit.is_none_or(|su| su.is_time_unit()) {
474+
return Err(agent.throw_exception_with_static_message(
475+
ExceptionType::RangeError,
476+
"smallestUnit is not a valid time unit",
477+
gc.into_nogc(),
478+
));
479+
}
479480
// 10. If smallestUnit is hour, throw a RangeError exception.
480-
if smallest_unit == Unit::Hour {
481+
if smallest_unit == Some(Unit::Hour) {
481482
return Err(agent.throw_exception_with_static_message(
482483
ExceptionType::RangeError,
483484
"smallestUnit is hour",
484485
gc.into_nogc(),
485486
));
486487
}
487488
// 11. If timeZone is not undefined, then
488-
let tz = if !tz.is_undefined() {
489+
let time_zone = if !tz.is_undefined() {
489490
// a. Set timeZone to ? ToTemporalTimeZoneIdentifier(timeZone).
490-
todo!()
491+
Some(TimeZone::utc())
492+
} else {
493+
None
491494
};
495+
let instant = unsafe { instant.take(agent) }.bind(gc.nogc());
492496
// 12. Let precision be ToSecondsStringPrecisionRecord(smallestUnit, digits).
493-
// 13. Let roundedNs be RoundTemporalInstant(instant.[[EpochNanoseconds]], precision.[[Increment]], precision.[[Unit]], roundingMode).
497+
// 13. Let roundedNs be RoundTemporalInstant(
498+
// instant.[[EpochNanoseconds]],
499+
// precision.[[Increment]],
500+
// precision.[[Unit]],
501+
// roundingMode
502+
// ).
494503
// 14. Let roundedInstant be ! CreateTemporalInstant(roundedNs).
495504
// 15. Return TemporalInstantToString(roundedInstant, timeZone, precision.[[Precision]]).
496-
unimplemented!()
505+
let options = ToStringRoundingOptions {
506+
precision: digits,
507+
smallest_unit,
508+
rounding_mode: Some(rounding_mode),
509+
};
510+
match instant
511+
.inner_instant(agent)
512+
.to_ixdtf_string(time_zone, options)
513+
{
514+
Ok(string) => Ok(Value::from_string(agent, string, gc.into_nogc())),
515+
Err(err) => Err(temporal_err_to_js_err(agent, err, gc.into_nogc())),
516+
}
497517
}
498518

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

0 commit comments

Comments
 (0)