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
40 changes: 37 additions & 3 deletions nova_vm/src/ecmascript/builtins/temporal/plain_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ pub(crate) use plain_time_prototype::*;

use crate::{
ecmascript::{
Agent, ExceptionType, InternalMethods, InternalSlots, JsResult, OrdinaryObject,
ProtoIntrinsics, Value, object_handle,
Agent, ExceptionType, Function, InternalMethods, InternalSlots, JsResult, OrdinaryObject,
ProtoIntrinsics, Value, object_handle, ordinary_populate_from_constructor,
},
engine::{Bindable, NoGcScope},
engine::{Bindable, GcScope, NoGcScope},
heap::{
ArenaAccess, ArenaAccessMut, BaseIndex, CompactionLists, CreateHeapData, Heap,
HeapMarkAndSweep, HeapSweepWeakReference, WorkQueues, arena_vec_access,
Expand Down Expand Up @@ -99,3 +99,37 @@ fn require_internal_slot_temporal_plain_time<'a>(
)),
}
}

/// ### [4.5.11 CreateTemporalTime](https://tc39.es/proposal-temporal/#sec-temporal-createtemporaltime)
pub(crate) fn create_temporal_plain_time<'gc>(
agent: &mut Agent,
plain_time: temporal_rs::PlainTime,
new_target: Option<Function>,
gc: GcScope<'gc, '_>,
) -> JsResult<'gc, TemporalPlainTime<'gc>> {
// 1. If newTarget is not present, set newTarget to %Temporal.PlainTime%.
let new_target = new_target.unwrap_or_else(|| {
agent
.current_realm_record()
.intrinsics()
.temporal_plain_time()
.into()
});
// 2. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainTime.prototype%", « [[InitializedTemporalTime]], [[Time]] »).
// 3. Set object.[[Time]] to time.
// 4. Return object.
let object = agent.heap.create(PlainTimeRecord {
object_index: None,
plain_time,
});
Ok(
TemporalPlainTime::try_from(ordinary_populate_from_constructor(
agent,
object.unbind().into(),
new_target,
ProtoIntrinsics::TemporalPlainTime,
gc,
)?)
.unwrap(),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
use crate::{
ecmascript::{
Agent, ArgumentsList, BUILTIN_STRING_MEMORY, Behaviour, Builtin,
BuiltinIntrinsicConstructor, JsResult, Object, Realm, String, Value,
builders::BuiltinFunctionBuilder,
BuiltinIntrinsicConstructor, ExceptionType, Function, JsResult, Object, Realm, String,
Value, builders::BuiltinFunctionBuilder, create_temporal_plain_time,
temporal_err_to_js_err, to_integer_with_truncation,
},
engine::{GcScope, NoGcScope},
engine::{Bindable as _, GcScope, NoGcScope, Scopable},
heap::IntrinsicConstructorIndexes,
};

Expand All @@ -25,14 +26,125 @@ impl BuiltinIntrinsicConstructor for TemporalPlainTimeConstructor {
}

impl TemporalPlainTimeConstructor {
/// ### [4.1.1 Temporal.PlainTime](https://tc39.es/proposal-temporal/#sec-temporal.plaintime)
fn constructor<'gc>(
agent: &mut Agent,
_: Value,
_args: ArgumentsList,
_new_target: Option<Object>,
gc: GcScope<'gc, '_>,
args: ArgumentsList,
new_target: Option<Object>,
mut gc: GcScope<'gc, '_>,
) -> JsResult<'gc, Value<'gc>> {
Err(agent.todo("Temporal.PlainTime", gc.into_nogc()))
let hours = args.get(1).scope(agent, gc.nogc());
let minutes = args.get(2).scope(agent, gc.nogc());
let seconds = args.get(3).scope(agent, gc.nogc());
let milliseconds = args.get(4).scope(agent, gc.nogc());
let microseconds = args.get(5).scope(agent, gc.nogc());
let nanoseconds = args.get(6).scope(agent, gc.nogc());

let new_target = new_target.bind(gc.nogc());

// 1. If NewTarget is undefined, throw a TypeError exception.
let Some(new_target) = new_target else {
return Err(agent.throw_exception_with_static_message(
ExceptionType::TypeError,
"calling a builtin Temporal.PlainTime constructor without new is forbidden",
gc.into_nogc(),
));
};

let Ok(new_target) = Function::try_from(new_target) else {
unreachable!()
};
let new_target = new_target.scope(agent, gc.nogc());

// 2. If hour is undefined, set hour to 0; else set hour to ? ToIntegerWithTruncation(hour).
let h = if hours.get(agent).is_undefined() {
Ok(0)
} else {
u8::try_from(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Let's end all of the try_from's with an unwrap_or(u8/u16::MAX), and replace Ok(0)s with just 0.

to_integer_with_truncation(agent, hours.get(agent), gc.reborrow()).unbind()?,
)
};

// 3. If minute is undefined, set minute to 0; else set minute to ? ToIntegerWithTruncation(minute).
let m = if minutes.get(agent).is_undefined() {
Ok(0)
} else {
u8::try_from(
to_integer_with_truncation(agent, minutes.get(agent), gc.reborrow()).unbind()?,
)
};

// 4. If second is undefined, set second to 0; else set second to ? ToIntegerWithTruncation(second).
let s = if seconds.get(agent).is_undefined() {
Ok(0)
} else {
u8::try_from(
to_integer_with_truncation(agent, seconds.get(agent), gc.reborrow()).unbind()?,
)
};

// 5. If millisecond is undefined, set millisecond to 0; else set millisecond to ? ToIntegerWithTruncation(millisecond).
let ms = if milliseconds.get(agent).is_undefined() {
Ok(0)
} else {
u16::try_from(
to_integer_with_truncation(agent, milliseconds.get(agent), gc.reborrow())
.unbind()?,
)
};

// 6. If microsecond is undefined, set microsecond to 0; else set microsecond to ? ToIntegerWithTruncation(microsecond).
let mis = if microseconds.get(agent).is_undefined() {
Ok(0)
} else {
u16::try_from(
to_integer_with_truncation(agent, microseconds.get(agent), gc.reborrow())
.unbind()?,
)
};

// 7. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to ? ToIntegerWithTruncation(nanosecond).
let ns = if nanoseconds.get(agent).is_undefined() {
Ok(0)
} else {
u16::try_from(
to_integer_with_truncation(agent, nanoseconds.get(agent), gc.reborrow())
.unbind()?,
)
};

// 8. If IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.
// 9. Let time be CreateTimeRecord(hour, minute, second, millisecond, microsecond, nanosecond).
let time = if let (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: Here splitting into the else branch becomes unnecessary after adding unwrap_or(...) to each of the above else-branches.

Ok(hour),
Ok(minute),
Ok(second),
Ok(millisecond),
Ok(microsecond),
Ok(nanosecond),
) = (h, m, s, ms, mis, ns)
{
temporal_rs::PlainTime::try_new(
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
)
.map_err(|err| temporal_err_to_js_err(agent, err, gc.nogc()))
.unbind()?
} else {
return Err(agent.throw_exception_with_static_message(
ExceptionType::RangeError,
"not a valid time",
gc.into_nogc(),
));
};

// 10. Return ? CreateTemporalTime(time, NewTarget).
create_temporal_plain_time(agent, time, Some(new_target.get(agent)), gc).map(Value::from)
}

pub(crate) fn create_intrinsic(agent: &mut Agent, realm: Realm<'static>, _gc: NoGcScope) {
Expand Down
10 changes: 1 addition & 9 deletions tests/expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -3833,13 +3833,8 @@
"built-ins/Temporal/PlainTime/basic.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-cast.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-number.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation-invalid-key.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-string-critical-unknown-annotation.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-string-date-with-utc-offset.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-string-minus-sign.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-string-multiple-calendar.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-string-multiple-time-zone.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-string-no-implicit-midnight.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-string-time-designator-required-for-disambiguation.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/argument-string-time-separators.js": "FAIL",
Expand All @@ -3860,7 +3855,6 @@
"built-ins/Temporal/PlainTime/compare/prop-desc.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/use-internal-slots.js": "FAIL",
"built-ins/Temporal/PlainTime/compare/year-zero.js": "FAIL",
"built-ins/Temporal/PlainTime/constructor.js": "FAIL",
"built-ins/Temporal/PlainTime/from/argument-object-leap-second.js": "FAIL",
"built-ins/Temporal/PlainTime/from/argument-object.js": "FAIL",
"built-ins/Temporal/PlainTime/from/argument-plaindatetime.js": "FAIL",
Expand Down Expand Up @@ -3906,7 +3900,6 @@
"built-ins/Temporal/PlainTime/from/prop-desc.js": "FAIL",
"built-ins/Temporal/PlainTime/from/subclassing-ignored.js": "FAIL",
"built-ins/Temporal/PlainTime/from/year-zero.js": "FAIL",
"built-ins/Temporal/PlainTime/get-prototype-from-constructor-throws.js": "FAIL",
"built-ins/Temporal/PlainTime/hour-undefined.js": "FAIL",
"built-ins/Temporal/PlainTime/infinity-throws-rangeerror.js": "FAIL",
"built-ins/Temporal/PlainTime/microsecond-undefined.js": "FAIL",
Expand Down Expand Up @@ -3988,7 +3981,6 @@
"built-ins/Temporal/PlainTime/prototype/round/name.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/round/not-a-constructor.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/round/options-read-before-algorithmic-validation.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/round/options-wrong-type.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/round/prop-desc.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js": "FAIL",
Expand Down Expand Up @@ -4256,7 +4248,6 @@
"built-ins/Temporal/PlainTime/prototype/with/length.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/with/name.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/with/not-a-constructor.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/with/options-invalid.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/with/options-object.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/with/options-read-before-algorithmic-validation.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/with/options-undefined.js": "FAIL",
Expand Down Expand Up @@ -6680,6 +6671,7 @@
"staging/sm/ArrayBuffer/slice-species.js": "FAIL",
"staging/sm/AsyncGenerators/for-await-of-error.js": "CRASH",
"staging/sm/BigInt/Number-conversion-rounding.js": "FAIL",
"staging/sm/BigInt/large-bit-length.js": "TIMEOUT",
"staging/sm/Date/dst-offset-caching-1-of-8.js": "TIMEOUT",
"staging/sm/Date/dst-offset-caching-2-of-8.js": "TIMEOUT",
"staging/sm/Date/dst-offset-caching-3-of-8.js": "TIMEOUT",
Expand Down
6 changes: 3 additions & 3 deletions tests/metrics.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"results": {
"crash": 52,
"fail": 6967,
"pass": 40333,
"fail": 6960,
"pass": 40339,
"skip": 3326,
"timeout": 18,
"timeout": 19,
"unresolved": 37
},
"total": 50733
Expand Down
Loading