Skip to content

Commit bece61a

Browse files
0Zenoaapoalas
andauthored
feat(ecmascript): PlainTime.prototype.round (#988)
Co-authored-by: Aapo Alasuutari <aapo.alasuutari@gmail.com>
1 parent 0d068db commit bece61a

3 files changed

Lines changed: 107 additions & 40 deletions

File tree

nova_vm/src/ecmascript/builtins/temporal/plain_time/plain_time_prototype.rs

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
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 temporal_rs::options::{RoundingMode, ToStringRoundingOptions};
5+
use temporal_rs::options::{RoundingMode, RoundingOptions, ToStringRoundingOptions, Unit};
66

77
use crate::{
88
ecmascript::{
99
Agent, ArgumentsList, BUILTIN_STRING_MEMORY, Behaviour, Builtin, BuiltinGetter,
10-
ExceptionType, JsResult, PropertyKey, Realm, String, Value,
10+
ExceptionType, JsResult, PropertyKey, Realm, String, StringOptionType, Value,
1111
builders::OrdinaryObjectBuilder,
1212
builtins::temporal::plain_time::{
1313
add_duration_to_time, require_internal_slot_temporal_plain_time,
1414
},
15-
get_options_object, get_rounding_mode_option, get_temporal_fractional_second_digits_option,
15+
create_temporal_plain_time, get_options_object, get_rounding_increment_option,
16+
get_rounding_mode_option, get_temporal_fractional_second_digits_option,
1617
get_temporal_unit_valued_option, temporal_err_to_js_err,
1718
},
1819
engine::{Bindable, GcScope, NoGcScope, Scopable},
@@ -87,6 +88,13 @@ impl Builtin for TemporalPlainTimePrototypeAdd {
8788
const BEHAVIOUR: Behaviour = Behaviour::Regular(TemporalPlainTimePrototype::add);
8889
}
8990

91+
struct TemporalPlainTimePrototypeRound;
92+
impl Builtin for TemporalPlainTimePrototypeRound {
93+
const NAME: String<'static> = BUILTIN_STRING_MEMORY.round;
94+
const LENGTH: u8 = 1;
95+
const BEHAVIOUR: Behaviour = Behaviour::Regular(TemporalPlainTimePrototype::round);
96+
}
97+
9098
struct TemporalPlainTimePrototypeSubtract;
9199
impl Builtin for TemporalPlainTimePrototypeSubtract {
92100
const NAME: String<'static> = BUILTIN_STRING_MEMORY.subtract;
@@ -239,6 +247,98 @@ impl TemporalPlainTimePrototype {
239247
.map(Value::from)
240248
}
241249

250+
/// ### [5.3.14 Temporal.PlainTime.prototype.round ( roundTo )](https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.round)
251+
fn round<'gc>(
252+
agent: &mut Agent,
253+
this_value: Value,
254+
args: ArgumentsList,
255+
mut gc: GcScope<'gc, '_>,
256+
) -> JsResult<'gc, Value<'gc>> {
257+
let nogc = gc.nogc();
258+
let this_value = this_value.bind(nogc);
259+
let round_to = args.get(0).bind(nogc);
260+
// 1. Let plainTime be the this value.
261+
// 2. Perform ? RequireInternalSlot(plainTime, [[InitializedTemporalTime]]).
262+
let plain_time = require_internal_slot_temporal_plain_time(agent, this_value, nogc)
263+
.unbind()?
264+
.bind(nogc);
265+
let inner_time = *plain_time.inner_plain_time(agent);
266+
// 3. If roundTo is undefined, throw a TypeError exception.
267+
if round_to.is_undefined() {
268+
return Err(agent.throw_exception_with_static_message(
269+
ExceptionType::TypeError,
270+
"rountTo cannot be undefined",
271+
gc.into_nogc(),
272+
));
273+
}
274+
275+
// 4. If roundTo is a String, then
276+
let options = if let Ok(round_to) = String::try_from(round_to) {
277+
// a. Let paramString be roundTo.
278+
// b. Set roundTo to OrdinaryObjectCreate(null).
279+
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString).
280+
let mut options = RoundingOptions::default();
281+
options.smallest_unit =
282+
Some(Unit::from_string(agent, round_to.unbind(), nogc).unbind()?);
283+
options
284+
} else {
285+
// 5. Else,
286+
// a. Set roundTo to ? GetOptionsObject(roundTo).
287+
let round_to = get_options_object(agent, round_to, nogc)
288+
.unbind()?
289+
.map(|r| r.scope(agent, nogc));
290+
// 6. NOTE: The following steps read options and perform independent validation in alphabetical
291+
// order (GetRoundingIncrementOption reads "roundingIncrement" and GetRoundingModeOption reads "roundingMode").
292+
let mut options = RoundingOptions::default();
293+
294+
let (rounding_increment, rounding_mode, smallest_unit) =
295+
if let Some(round_to) = round_to {
296+
// 7. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo).
297+
let rounding_increment =
298+
get_rounding_increment_option(agent, round_to.get(agent), gc.reborrow())
299+
.unbind()?;
300+
// 8. Let roundingMode be ? GetRoundingModeOption(roundTo, half-expand).
301+
let rounding_mode = get_rounding_mode_option(
302+
agent,
303+
round_to.get(agent),
304+
RoundingMode::default(),
305+
gc.reborrow(),
306+
)
307+
.unbind()?;
308+
// 9. Let smallestUnit be ? GetTemporalUnitValuedOption(roundTo, "smallestUnit", required).
309+
let smallest_unit = get_temporal_unit_valued_option(
310+
agent,
311+
round_to.get(agent),
312+
BUILTIN_STRING_MEMORY.smallestUnit.into(),
313+
gc.reborrow(),
314+
)
315+
.unbind()?;
316+
(rounding_increment, rounding_mode, smallest_unit)
317+
} else {
318+
Default::default()
319+
};
320+
321+
options.increment = Some(rounding_increment);
322+
options.rounding_mode = Some(rounding_mode);
323+
options.smallest_unit = smallest_unit;
324+
options
325+
};
326+
327+
// 10. Perform ? ValidateTemporalUnitValue(smallestUnit, time).
328+
// 11. Let maximum be MaximumTemporalDurationRoundingIncrement(smallestUnit).
329+
// 12. Assert: maximum is not unset.
330+
// 13. Perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false).
331+
// 14. Let result be RoundTime(plainTime.[[Time]], roundingIncrement, smallestUnit, roundingMode).
332+
let result = inner_time
333+
.round(options)
334+
.map_err(|e| temporal_err_to_js_err(agent, e, gc.nogc()))
335+
.unbind()?;
336+
// 15. Return ! CreateTemporalTime(result).
337+
Ok(create_temporal_plain_time(agent, result, None, gc)
338+
.unwrap()
339+
.into())
340+
}
341+
242342
/// ### [4.3.10 Temporal.PlainTime.prototype.subtract ( temporalDurationLike )](https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.subtract)
243343
fn subtract<'gc>(
244344
agent: &mut Agent,
@@ -395,7 +495,7 @@ impl TemporalPlainTimePrototype {
395495
let plain_time_constructor = intrinsics.temporal_plain_time();
396496

397497
OrdinaryObjectBuilder::new_intrinsic_object(agent, realm, this)
398-
.with_property_capacity(14)
498+
.with_property_capacity(15)
399499
.with_prototype(object_prototype)
400500
.with_constructor_property(plain_time_constructor)
401501
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetHour>()
@@ -405,6 +505,7 @@ impl TemporalPlainTimePrototype {
405505
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetNanosecond>()
406506
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetMillisecond>()
407507
.with_builtin_function_property::<TemporalPlainTimePrototypeAdd>()
508+
.with_builtin_function_property::<TemporalPlainTimePrototypeRound>()
408509
.with_builtin_function_property::<TemporalPlainTimePrototypeSubtract>()
409510
.with_builtin_function_property::<TemporalPlainTimePrototypeToJSON>()
410511
.with_builtin_function_property::<TemporalPlainTimePrototypeToLocaleString>()

tests/expectations.json

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3933,45 +3933,11 @@
39333933
"built-ins/Temporal/PlainTime/prototype/equals/plaintime-propertybag-no-time-units.js": "FAIL",
39343934
"built-ins/Temporal/PlainTime/prototype/equals/prop-desc.js": "FAIL",
39353935
"built-ins/Temporal/PlainTime/prototype/equals/year-zero.js": "FAIL",
3936-
"built-ins/Temporal/PlainTime/prototype/round/branding.js": "FAIL",
3937-
"built-ins/Temporal/PlainTime/prototype/round/builtin.js": "FAIL",
3938-
"built-ins/Temporal/PlainTime/prototype/round/length.js": "FAIL",
3939-
"built-ins/Temporal/PlainTime/prototype/round/name.js": "FAIL",
3940-
"built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js": "FAIL",
3941-
"built-ins/Temporal/PlainTime/prototype/round/options-read-before-algorithmic-validation.js": "FAIL",
3942-
"built-ins/Temporal/PlainTime/prototype/round/prop-desc.js": "FAIL",
39433936
"built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js": "FAIL",
3944-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js": "FAIL",
39453937
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-invalid.js": "FAIL",
3946-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-microseconds.js": "FAIL",
3947-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-milliseconds.js": "FAIL",
3948-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-minutes.js": "FAIL",
3949-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nan.js": "FAIL",
3950-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nanoseconds.js": "FAIL",
3951-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-non-integer.js": "FAIL",
3952-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js": "FAIL",
3953-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-seconds.js": "FAIL",
3954-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-undefined.js": "FAIL",
3955-
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-wrong-type.js": "FAIL",
3956-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-ceil.js": "FAIL",
3957-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-expand.js": "FAIL",
3958-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-floor.js": "FAIL",
3959-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfCeil.js": "FAIL",
3960-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfEven.js": "FAIL",
3961-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfExpand.js": "FAIL",
3962-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfFloor.js": "FAIL",
3963-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfTrunc.js": "FAIL",
3964-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-invalid-string.js": "FAIL",
3965-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-trunc.js": "FAIL",
39663938
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-undefined.js": "FAIL",
3967-
"built-ins/Temporal/PlainTime/prototype/round/roundingmode-wrong-type.js": "FAIL",
3968-
"built-ins/Temporal/PlainTime/prototype/round/roundto-invalid-string.js": "FAIL",
3969-
"built-ins/Temporal/PlainTime/prototype/round/smallestunit-invalid-string.js": "FAIL",
3970-
"built-ins/Temporal/PlainTime/prototype/round/smallestunit-missing.js": "FAIL",
39713939
"built-ins/Temporal/PlainTime/prototype/round/smallestunit-plurals-accepted.js": "FAIL",
39723940
"built-ins/Temporal/PlainTime/prototype/round/smallestunit-string-shorthand.js": "FAIL",
3973-
"built-ins/Temporal/PlainTime/prototype/round/smallestunit-wrong-type.js": "FAIL",
3974-
"built-ins/Temporal/PlainTime/prototype/round/subclassing-ignored.js": "FAIL",
39753941
"built-ins/Temporal/PlainTime/prototype/since/argument-cast.js": "FAIL",
39763942
"built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation-invalid-key.js": "FAIL",
39773943
"built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation.js": "FAIL",

tests/metrics.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"results": {
33
"crash": 52,
4-
"fail": 6819,
5-
"pass": 40481,
4+
"fail": 6785,
5+
"pass": 40515,
66
"skip": 3326,
77
"timeout": 18,
88
"unresolved": 37

0 commit comments

Comments
 (0)