diff --git a/nova_vm/src/ecmascript/builtins/temporal/plain_time.rs b/nova_vm/src/ecmascript/builtins/temporal/plain_time.rs index ebfe4c452..29ad116e7 100644 --- a/nova_vm/src/ecmascript/builtins/temporal/plain_time.rs +++ b/nova_vm/src/ecmascript/builtins/temporal/plain_time.rs @@ -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, @@ -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, + 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(), + ) +} diff --git a/nova_vm/src/ecmascript/builtins/temporal/plain_time/plain_time_constructor.rs b/nova_vm/src/ecmascript/builtins/temporal/plain_time/plain_time_constructor.rs index dfdaa712f..896d42612 100644 --- a/nova_vm/src/ecmascript/builtins/temporal/plain_time/plain_time_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/temporal/plain_time/plain_time_constructor.rs @@ -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, }; @@ -25,14 +26,115 @@ 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, - gc: GcScope<'gc, '_>, + args: ArgumentsList, + new_target: Option, + 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 hour = if hours.get(agent).is_undefined() { + 0 + } else { + u8::try_from( + to_integer_with_truncation(agent, hours.get(agent), gc.reborrow()).unbind()?, + ) + .unwrap_or(u8::MAX) + }; + + // 3. If minute is undefined, set minute to 0; else set minute to ? ToIntegerWithTruncation(minute). + let minute = if minutes.get(agent).is_undefined() { + 0 + } else { + u8::try_from( + to_integer_with_truncation(agent, minutes.get(agent), gc.reborrow()).unbind()?, + ) + .unwrap_or(u8::MAX) + }; + + // 4. If second is undefined, set second to 0; else set second to ? ToIntegerWithTruncation(second). + let second = if seconds.get(agent).is_undefined() { + 0 + } else { + u8::try_from( + to_integer_with_truncation(agent, seconds.get(agent), gc.reborrow()).unbind()?, + ) + .unwrap_or(u8::MAX) + }; + + // 5. If millisecond is undefined, set millisecond to 0; else set millisecond to ? ToIntegerWithTruncation(millisecond). + let millisecond = if milliseconds.get(agent).is_undefined() { + 0 + } else { + u16::try_from( + to_integer_with_truncation(agent, milliseconds.get(agent), gc.reborrow()) + .unbind()?, + ) + .unwrap_or(u16::MAX) + }; + + // 6. If microsecond is undefined, set microsecond to 0; else set microsecond to ? ToIntegerWithTruncation(microsecond). + let microsecond = if microseconds.get(agent).is_undefined() { + 0 + } else { + u16::try_from( + to_integer_with_truncation(agent, microseconds.get(agent), gc.reborrow()) + .unbind()?, + ) + .unwrap_or(u16::MAX) + }; + + // 7. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to ? ToIntegerWithTruncation(nanosecond). + let nanosecond = if nanoseconds.get(agent).is_undefined() { + 0 + } else { + u16::try_from( + to_integer_with_truncation(agent, nanoseconds.get(agent), gc.reborrow()) + .unbind()?, + ) + .unwrap_or(u16::MAX) + }; + + // 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 = temporal_rs::PlainTime::try_new( + hour, + minute, + second, + millisecond, + microsecond, + nanosecond, + ) + .map_err(|err| temporal_err_to_js_err(agent, err, gc.nogc())) + .unbind()?; + + // 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) { diff --git a/tests/expectations.json b/tests/expectations.json index 9fde238da..a3d0d1bb1 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -3826,13 +3826,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", @@ -3853,7 +3848,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", @@ -3899,15 +3893,12 @@ "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", "built-ins/Temporal/PlainTime/millisecond-undefined.js": "FAIL", "built-ins/Temporal/PlainTime/minute-undefined.js": "FAIL", "built-ins/Temporal/PlainTime/nanosecond-undefined.js": "FAIL", "built-ins/Temporal/PlainTime/negative-infinity-throws-rangeerror.js": "FAIL", - "built-ins/Temporal/PlainTime/negative-zero.js": "FAIL", "built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js": "FAIL", "built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js": "FAIL", "built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js": "FAIL", @@ -3973,7 +3964,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", @@ -4241,7 +4231,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", diff --git a/tests/metrics.json b/tests/metrics.json index d6b67a54f..03b9dac85 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,8 +1,8 @@ { "results": { "crash": 52, - "fail": 6931, - "pass": 40369, + "fail": 6920, + "pass": 40380, "skip": 3326, "timeout": 18, "unresolved": 37