Skip to content

Commit 11ce9da

Browse files
authored
ecmascript(temporal): PlainTime.prototype.add + subtract (#979)
1 parent b5548ba commit 11ce9da

4 files changed

Lines changed: 118 additions & 32 deletions

File tree

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

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ use crate::{
1414
ecmascript::{
1515
Agent, ExceptionType, Function, InternalMethods, InternalSlots, JsResult, OrdinaryObject,
1616
ProtoIntrinsics, Value, object_handle, ordinary_populate_from_constructor,
17+
temporal_err_to_js_err, to_temporal_duration,
1718
},
18-
engine::{Bindable, GcScope, NoGcScope},
19+
engine::{Bindable, GcScope, NoGcScope, Scopable},
1920
heap::{
2021
ArenaAccess, ArenaAccessMut, BaseIndex, CompactionLists, CreateHeapData, Heap,
2122
HeapMarkAndSweep, HeapSweepWeakReference, WorkQueues, arena_vec_access,
@@ -133,3 +134,51 @@ pub(crate) fn create_temporal_plain_time<'gc>(
133134
.unwrap(),
134135
)
135136
}
137+
138+
/// [4.5.18 AddDurationToTime ( operation, temporalTime, temporalDurationLike )](https://tc39.es/proposal-temporal/#sec-temporal-adddurationtotime)
139+
///
140+
/// The abstract operation AddDurationToTime takes arguments operation
141+
/// (either add or subtract), temporalTime (a Temporal.PlainTime), and
142+
/// temporalDurationLike (an ECMAScript language value) and returns either
143+
/// a normal completion containing a Temporal.PlainTime or a throw completion.
144+
/// It adds/subtracts temporalDurationLike to/from temporalTime, returning a
145+
/// point in time that is in the future/past relative to temporalTime.
146+
/// It performs the following steps when called:
147+
fn add_duration_to_time<'gc, const IS_ADD: bool>(
148+
agent: &mut Agent,
149+
plan_time: TemporalPlainTime,
150+
duration: Value,
151+
mut gc: GcScope<'gc, '_>,
152+
) -> JsResult<'gc, TemporalPlainTime<'gc>> {
153+
let duration = duration.bind(gc.nogc());
154+
let mut plain_time = plan_time.bind(gc.nogc());
155+
156+
// 1. Let duration be ? ToTemporalDuration(temporalDurationLike).
157+
let duration = if let Value::Duration(duration) = duration {
158+
duration.get(agent).duration
159+
} else {
160+
let scoped_instant = plain_time.scope(agent, gc.nogc());
161+
let res = to_temporal_duration(agent, duration.unbind(), gc.reborrow()).unbind()?;
162+
// SAFETY: not shared
163+
unsafe {
164+
plain_time = scoped_instant.take(agent);
165+
}
166+
res
167+
};
168+
169+
// 2. If operation is subtract, set duration to CreateNegatedTemporalDuration(duration).
170+
// 3. Let internalDuration be ToInternalDurationRecord(duration).
171+
// 4. Let result be AddTime(temporalTime.[[Time]], internalDuration.[[Time]]).
172+
let ns_result = if IS_ADD {
173+
temporal_rs::PlainTime::add(plain_time.inner_plain_time(agent), &duration)
174+
.map_err(|err| temporal_err_to_js_err(agent, err, gc.nogc()))
175+
.unbind()?
176+
} else {
177+
temporal_rs::PlainTime::subtract(plain_time.inner_plain_time(agent), &duration)
178+
.map_err(|err| temporal_err_to_js_err(agent, err, gc.nogc()))
179+
.unbind()?
180+
};
181+
182+
// 5. Return ! CreateTemporalTime(result).
183+
Ok(create_temporal_plain_time(agent, ns_result, None, gc).unwrap())
184+
}

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

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
use crate::{
66
ecmascript::{
77
Agent, ArgumentsList, BUILTIN_STRING_MEMORY, Behaviour, Builtin, BuiltinGetter, JsResult,
8-
PropertyKey, Realm, String, Value, builders::OrdinaryObjectBuilder,
9-
builtins::temporal::plain_time::require_internal_slot_temporal_plain_time,
8+
PropertyKey, Realm, String, Value,
9+
builders::OrdinaryObjectBuilder,
10+
builtins::temporal::plain_time::{
11+
add_duration_to_time, require_internal_slot_temporal_plain_time,
12+
},
1013
},
11-
engine::{GcScope, NoGcScope},
14+
engine::{Bindable, GcScope, NoGcScope},
1215
heap::WellKnownSymbols,
1316
};
1417

@@ -73,6 +76,20 @@ impl Builtin for TemporalPlainTimePrototypeGetNanosecond {
7376
}
7477
impl BuiltinGetter for TemporalPlainTimePrototypeGetNanosecond {}
7578

79+
struct TemporalPlainTimePrototypeAdd;
80+
impl Builtin for TemporalPlainTimePrototypeAdd {
81+
const NAME: String<'static> = BUILTIN_STRING_MEMORY.add;
82+
const LENGTH: u8 = 1;
83+
const BEHAVIOUR: Behaviour = Behaviour::Regular(TemporalPlainTimePrototype::add);
84+
}
85+
86+
struct TemporalPlainTimePrototypeSubtract;
87+
impl Builtin for TemporalPlainTimePrototypeSubtract {
88+
const NAME: String<'static> = BUILTIN_STRING_MEMORY.subtract;
89+
const LENGTH: u8 = 1;
90+
const BEHAVIOUR: Behaviour = Behaviour::Regular(TemporalPlainTimePrototype::subtract);
91+
}
92+
7693
impl TemporalPlainTimePrototype {
7794
/// ### [4.3.4 get Temporal.PlainTime.prototype.minute](https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.minute)
7895
pub(crate) fn get_minute<'gc>(
@@ -169,14 +186,56 @@ impl TemporalPlainTimePrototype {
169186
Ok(value.into())
170187
}
171188

189+
/// ### [4.3.9 Temporal.PlainTime.prototype.add ( temporalDurationLike )](https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.add)
190+
fn add<'gc>(
191+
agent: &mut Agent,
192+
this_value: Value,
193+
args: ArgumentsList,
194+
gc: GcScope<'gc, '_>,
195+
) -> JsResult<'gc, Value<'gc>> {
196+
let duration = args.get(0).bind(gc.nogc());
197+
// 1. Let plainTime be the this value.
198+
let plain_time = this_value.bind(gc.nogc());
199+
// 2. Perform ? RequireInternalSlot(plainTime, [[InitializedTemporalTime]]).
200+
let plain_time =
201+
require_internal_slot_temporal_plain_time(agent, plain_time.unbind(), gc.nogc())
202+
.unbind()?
203+
.bind(gc.nogc());
204+
// 3. Return ? AddDurationToTime(add, plainTime, temporalDurationLike).
205+
const SUBTRACT: bool = true;
206+
add_duration_to_time::<SUBTRACT>(agent, plain_time.unbind(), duration.unbind(), gc)
207+
.map(Value::from)
208+
}
209+
210+
/// ### [4.3.10 Temporal.PlainTime.prototype.subtract ( temporalDurationLike )](https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.subtract)
211+
fn subtract<'gc>(
212+
agent: &mut Agent,
213+
this_value: Value,
214+
args: ArgumentsList,
215+
gc: GcScope<'gc, '_>,
216+
) -> JsResult<'gc, Value<'gc>> {
217+
let duration = args.get(0).bind(gc.nogc());
218+
// 1. Let plainTime be the this value.
219+
let plain_time = this_value.bind(gc.nogc());
220+
// 2. Perform ? RequireInternalSlot(plainTime, [[InitializedTemporalTime]]).
221+
let plain_time =
222+
require_internal_slot_temporal_plain_time(agent, plain_time.unbind(), gc.nogc())
223+
.unbind()?
224+
.bind(gc.nogc());
225+
// 3. Return ? AddDurationToTime(subtract, plainTime, temporalDurationLike).
226+
const ADD: bool = false;
227+
add_duration_to_time::<ADD>(agent, plain_time.unbind(), duration.unbind(), gc)
228+
.map(Value::from)
229+
}
230+
172231
pub(crate) fn create_intrinsic(agent: &mut Agent, realm: Realm<'static>, _: NoGcScope) {
173232
let intrinsics = agent.get_realm_record_by_id(realm).intrinsics();
174233
let this = intrinsics.temporal_plain_time_prototype();
175234
let object_prototype = intrinsics.object_prototype();
176235
let plain_time_constructor = intrinsics.temporal_plain_time();
177236

178237
OrdinaryObjectBuilder::new_intrinsic_object(agent, realm, this)
179-
.with_property_capacity(8)
238+
.with_property_capacity(10)
180239
.with_prototype(object_prototype)
181240
.with_constructor_property(plain_time_constructor)
182241
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetHour>()
@@ -185,6 +244,8 @@ impl TemporalPlainTimePrototype {
185244
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetMicrosecond>()
186245
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetNanosecond>()
187246
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetMillisecond>()
247+
.with_builtin_function_property::<TemporalPlainTimePrototypeAdd>()
248+
.with_builtin_function_property::<TemporalPlainTimePrototypeSubtract>()
188249
.with_property(|builder| {
189250
builder
190251
.with_key(WellKnownSymbols::ToStringTag.into())

tests/expectations.json

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3899,35 +3899,23 @@
38993899
"built-ins/Temporal/PlainTime/minute-undefined.js": "FAIL",
39003900
"built-ins/Temporal/PlainTime/nanosecond-undefined.js": "FAIL",
39013901
"built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js": "FAIL",
3902-
"built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js": "FAIL",
3903-
"built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js": "FAIL",
3904-
"built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js": "FAIL",
39053902
"built-ins/Temporal/PlainTime/prototype/add/argument-duration.js": "FAIL",
39063903
"built-ins/Temporal/PlainTime/prototype/add/argument-higher-units.js": "FAIL",
39073904
"built-ins/Temporal/PlainTime/prototype/add/argument-invalid-property.js": "FAIL",
39083905
"built-ins/Temporal/PlainTime/prototype/add/argument-mixed-sign.js": "FAIL",
39093906
"built-ins/Temporal/PlainTime/prototype/add/argument-not-object.js": "FAIL",
39103907
"built-ins/Temporal/PlainTime/prototype/add/argument-object.js": "FAIL",
39113908
"built-ins/Temporal/PlainTime/prototype/add/argument-singular-properties.js": "FAIL",
3912-
"built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js": "FAIL",
3913-
"built-ins/Temporal/PlainTime/prototype/add/argument-string-fractional-units-rounding-mode.js": "FAIL",
3914-
"built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js": "FAIL",
39153909
"built-ins/Temporal/PlainTime/prototype/add/argument-string.js": "FAIL",
39163910
"built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js": "FAIL",
39173911
"built-ins/Temporal/PlainTime/prototype/add/blank-duration.js": "FAIL",
3918-
"built-ins/Temporal/PlainTime/prototype/add/branding.js": "FAIL",
3919-
"built-ins/Temporal/PlainTime/prototype/add/builtin.js": "FAIL",
39203912
"built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js": "FAIL",
3921-
"built-ins/Temporal/PlainTime/prototype/add/length.js": "FAIL",
3922-
"built-ins/Temporal/PlainTime/prototype/add/name.js": "FAIL",
39233913
"built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js": "FAIL",
39243914
"built-ins/Temporal/PlainTime/prototype/add/non-integer-throws-rangeerror.js": "FAIL",
3925-
"built-ins/Temporal/PlainTime/prototype/add/not-a-constructor.js": "FAIL",
39263915
"built-ins/Temporal/PlainTime/prototype/add/options-ignored.js": "FAIL",
39273916
"built-ins/Temporal/PlainTime/prototype/add/order-of-operations.js": "FAIL",
39283917
"built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js": "FAIL",
39293918
"built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js": "FAIL",
3930-
"built-ins/Temporal/PlainTime/prototype/add/prop-desc.js": "FAIL",
39313919
"built-ins/Temporal/PlainTime/prototype/add/subclassing-ignored.js": "FAIL",
39323920
"built-ins/Temporal/PlainTime/prototype/equals/argument-cast.js": "FAIL",
39333921
"built-ins/Temporal/PlainTime/prototype/equals/argument-number.js": "FAIL",
@@ -4070,35 +4058,23 @@
40704058
"built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js": "FAIL",
40714059
"built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js": "FAIL",
40724060
"built-ins/Temporal/PlainTime/prototype/since/year-zero.js": "FAIL",
4073-
"built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js": "FAIL",
4074-
"built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-out-of-range.js": "FAIL",
4075-
"built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-precision-exact-numerical-values.js": "FAIL",
40764061
"built-ins/Temporal/PlainTime/prototype/subtract/argument-duration.js": "FAIL",
40774062
"built-ins/Temporal/PlainTime/prototype/subtract/argument-higher-units.js": "FAIL",
40784063
"built-ins/Temporal/PlainTime/prototype/subtract/argument-invalid-property.js": "FAIL",
40794064
"built-ins/Temporal/PlainTime/prototype/subtract/argument-mixed-sign.js": "FAIL",
40804065
"built-ins/Temporal/PlainTime/prototype/subtract/argument-not-object.js": "FAIL",
40814066
"built-ins/Temporal/PlainTime/prototype/subtract/argument-object.js": "FAIL",
40824067
"built-ins/Temporal/PlainTime/prototype/subtract/argument-singular-properties.js": "FAIL",
4083-
"built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js": "FAIL",
4084-
"built-ins/Temporal/PlainTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js": "FAIL",
4085-
"built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js": "FAIL",
40864068
"built-ins/Temporal/PlainTime/prototype/subtract/argument-string.js": "FAIL",
40874069
"built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js": "FAIL",
40884070
"built-ins/Temporal/PlainTime/prototype/subtract/blank-duration.js": "FAIL",
4089-
"built-ins/Temporal/PlainTime/prototype/subtract/branding.js": "FAIL",
4090-
"built-ins/Temporal/PlainTime/prototype/subtract/builtin.js": "FAIL",
40914071
"built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js": "FAIL",
4092-
"built-ins/Temporal/PlainTime/prototype/subtract/length.js": "FAIL",
4093-
"built-ins/Temporal/PlainTime/prototype/subtract/name.js": "FAIL",
40944072
"built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js": "FAIL",
40954073
"built-ins/Temporal/PlainTime/prototype/subtract/non-integer-throws-rangeerror.js": "FAIL",
4096-
"built-ins/Temporal/PlainTime/prototype/subtract/not-a-constructor.js": "FAIL",
40974074
"built-ins/Temporal/PlainTime/prototype/subtract/options-ignored.js": "FAIL",
40984075
"built-ins/Temporal/PlainTime/prototype/subtract/order-of-operations.js": "FAIL",
40994076
"built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js": "FAIL",
41004077
"built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js": "FAIL",
4101-
"built-ins/Temporal/PlainTime/prototype/subtract/prop-desc.js": "FAIL",
41024078
"built-ins/Temporal/PlainTime/prototype/subtract/subclassing-ignored.js": "FAIL",
41034079
"built-ins/Temporal/PlainTime/prototype/toJSON/basic.js": "FAIL",
41044080
"built-ins/Temporal/PlainTime/prototype/toJSON/branding.js": "FAIL",
@@ -7026,4 +7002,4 @@
70267002
"staging/sm/syntax/yield-as-identifier.js": "FAIL",
70277003
"staging/source-phase-imports/import-source-source-text-module.js": "FAIL",
70287004
"staging/top-level-await/tla-hang-entry.js": "FAIL"
7029-
}
7005+
}

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": 6920,
5-
"pass": 40380,
4+
"fail": 6896,
5+
"pass": 40404,
66
"skip": 3326,
77
"timeout": 18,
88
"unresolved": 37

0 commit comments

Comments
 (0)