Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion nova_vm/src/ecmascript/builtins/temporal/plain_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ use crate::{
ecmascript::{
Agent, ExceptionType, Function, InternalMethods, InternalSlots, JsResult, OrdinaryObject,
ProtoIntrinsics, Value, object_handle, ordinary_populate_from_constructor,
temporal_err_to_js_err, to_temporal_duration,
},
engine::{Bindable, GcScope, NoGcScope},
engine::{Bindable, GcScope, NoGcScope, Scopable},
heap::{
ArenaAccess, ArenaAccessMut, BaseIndex, CompactionLists, CreateHeapData, Heap,
HeapMarkAndSweep, HeapSweepWeakReference, WorkQueues, arena_vec_access,
Expand Down Expand Up @@ -133,3 +134,51 @@ pub(crate) fn create_temporal_plain_time<'gc>(
.unwrap(),
)
}

/// [4.5.18 AddDurationToTime ( operation, temporalTime, temporalDurationLike )](https://tc39.es/proposal-temporal/#sec-temporal-adddurationtotime)
///
/// The abstract operation AddDurationToTime takes arguments operation
/// (either add or subtract), temporalTime (a Temporal.PlainTime), and
/// temporalDurationLike (an ECMAScript language value) and returns either
/// a normal completion containing a Temporal.PlainTime or a throw completion.
/// It adds/subtracts temporalDurationLike to/from temporalTime, returning a
/// point in time that is in the future/past relative to temporalTime.
/// It performs the following steps when called:
fn add_duration_to_time<'gc, const IS_ADD: bool>(
agent: &mut Agent,
plan_time: TemporalPlainTime,
duration: Value,
mut gc: GcScope<'gc, '_>,
) -> JsResult<'gc, TemporalPlainTime<'gc>> {
let duration = duration.bind(gc.nogc());
let mut plain_time = plan_time.bind(gc.nogc());

// 1. Let duration be ? ToTemporalDuration(temporalDurationLike).
let duration = if let Value::Duration(duration) = duration {
duration.get(agent).duration
} else {
let scoped_instant = plain_time.scope(agent, gc.nogc());
let res = to_temporal_duration(agent, duration.unbind(), gc.reborrow()).unbind()?;
// SAFETY: not shared
unsafe {
plain_time = scoped_instant.take(agent);
}
res
};

// If operation is subtract, set duration to CreateNegatedTemporalDuration(duration).
// 3. Let internalDuration be ToInternalDurationRecord(duration).
// 4. Let result be AddTime(temporalTime.[[Time]], internalDuration.[[Time]]).
let ns_result = if IS_ADD {
temporal_rs::PlainTime::add(plain_time.inner_plain_time(agent), &duration)
.map_err(|err| temporal_err_to_js_err(agent, err, gc.nogc()))
.unbind()?
} else {
temporal_rs::PlainTime::subtract(plain_time.inner_plain_time(agent), &duration)
.map_err(|err| temporal_err_to_js_err(agent, err, gc.nogc()))
.unbind()?
};

// 5. Return ! CreateTemporalTime(result).
Ok(create_temporal_plain_time(agent, ns_result, None, gc).unwrap())
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
use crate::{
ecmascript::{
Agent, ArgumentsList, BUILTIN_STRING_MEMORY, Behaviour, Builtin, BuiltinGetter, JsResult,
PropertyKey, Realm, String, Value, builders::OrdinaryObjectBuilder,
builtins::temporal::plain_time::require_internal_slot_temporal_plain_time,
PropertyKey, Realm, String, Value,
builders::OrdinaryObjectBuilder,
builtins::temporal::plain_time::{
add_duration_to_time, require_internal_slot_temporal_plain_time,
},
},
engine::{GcScope, NoGcScope},
engine::{Bindable, GcScope, NoGcScope},
heap::WellKnownSymbols,
};

Expand Down Expand Up @@ -73,6 +76,20 @@ impl Builtin for TemporalPlainTimePrototypeGetNanosecond {
}
impl BuiltinGetter for TemporalPlainTimePrototypeGetNanosecond {}

struct TemporalPlainTimePrototypeAdd;
impl Builtin for TemporalPlainTimePrototypeAdd {
const NAME: String<'static> = BUILTIN_STRING_MEMORY.add;
const LENGTH: u8 = 1;
const BEHAVIOUR: Behaviour = Behaviour::Regular(TemporalPlainTimePrototype::add);
}

struct TemporalPlainTimePrototypeSubtract;
impl Builtin for TemporalPlainTimePrototypeSubtract {
const NAME: String<'static> = BUILTIN_STRING_MEMORY.subtract;
const LENGTH: u8 = 1;
const BEHAVIOUR: Behaviour = Behaviour::Regular(TemporalPlainTimePrototype::subtract);
}

impl TemporalPlainTimePrototype {
/// ### [4.3.4 get Temporal.PlainTime.prototype.minute](https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.minute)
pub(crate) fn get_minute<'gc>(
Expand Down Expand Up @@ -169,14 +186,56 @@ impl TemporalPlainTimePrototype {
Ok(value.into())
}

/// ### [4.3.9 Temporal.PlainTime.prototype.add ( temporalDurationLike )](https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.add)
fn add<'gc>(
agent: &mut Agent,
this_value: Value,
args: ArgumentsList,
gc: GcScope<'gc, '_>,
) -> JsResult<'gc, Value<'gc>> {
let duration = args.get(0).bind(gc.nogc());
// 1. Let plainTime be the this value.
let plain_time = this_value.bind(gc.nogc());
// 2. Perform ? RequireInternalSlot(plainTime, [[InitializedTemporalTime]]).
let plain_time =
require_internal_slot_temporal_plain_time(agent, plain_time.unbind(), gc.nogc())
.unbind()?
.bind(gc.nogc());
// 3. Return ? AddDurationToTime(add, plainTime, temporalDurationLike).
const ADD: bool = true;
add_duration_to_time::<ADD>(agent, plain_time.unbind(), duration.unbind(), gc)
.map(Value::from)
}

/// ### [4.3.10 Temporal.PlainTime.prototype.subtract ( temporalDurationLike )](https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.subtract)
fn subtract<'gc>(
agent: &mut Agent,
this_value: Value,
args: ArgumentsList,
gc: GcScope<'gc, '_>,
) -> JsResult<'gc, Value<'gc>> {
let duration = args.get(0).bind(gc.nogc());
// 1. Let plainTime be the this value.
let plain_time = this_value.bind(gc.nogc());
// 2. Perform ? RequireInternalSlot(plainTime, [[InitializedTemporalTime]]).
let plain_time =
require_internal_slot_temporal_plain_time(agent, plain_time.unbind(), gc.nogc())
.unbind()?
.bind(gc.nogc());
// 3. Return ? AddDurationToTime(subtract, plainTime, temporalDurationLike).
const ADD: bool = false;
add_duration_to_time::<ADD>(agent, plain_time.unbind(), duration.unbind(), gc)
.map(Value::from)
}

pub(crate) fn create_intrinsic(agent: &mut Agent, realm: Realm<'static>, _: NoGcScope) {
let intrinsics = agent.get_realm_record_by_id(realm).intrinsics();
let this = intrinsics.temporal_plain_time_prototype();
let object_prototype = intrinsics.object_prototype();
let plain_time_constructor = intrinsics.temporal_plain_time();

OrdinaryObjectBuilder::new_intrinsic_object(agent, realm, this)
.with_property_capacity(8)
.with_property_capacity(10)
.with_prototype(object_prototype)
.with_constructor_property(plain_time_constructor)
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetHour>()
Expand All @@ -185,6 +244,8 @@ impl TemporalPlainTimePrototype {
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetMicrosecond>()
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetNanosecond>()
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetMillisecond>()
.with_builtin_function_property::<TemporalPlainTimePrototypeAdd>()
.with_builtin_function_property::<TemporalPlainTimePrototypeSubtract>()
.with_property(|builder| {
builder
.with_key(WellKnownSymbols::ToStringTag.into())
Expand Down
Loading