Skip to content
Closed
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
2 changes: 2 additions & 0 deletions nova_vm/src/builtin_strings
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ get description
#[cfg(feature = "regexp")]get ignoreCase
#[cfg(feature = "array-buffer")]get length
#[cfg(feature = "array-buffer")]get maxByteLength
#[cfg(feature = "temporal")]get minute
#[cfg(feature = "regexp")]get multiline
#[cfg(feature = "array-buffer")]get resizable
get size
Expand Down Expand Up @@ -297,6 +298,7 @@ MAX_VALUE
message
#[cfg(feature = "temporal")]microseconds
#[cfg(feature = "temporal")]milliseconds
#[cfg(feature = "temporal")]minute
#[cfg(feature = "temporal")]minutes
#[cfg(feature = "temporal")]months
#[cfg(feature = "math")]min
Expand Down
25 changes: 21 additions & 4 deletions nova_vm/src/ecmascript/builtins/temporal/plain_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ pub(crate) use plain_time_prototype::*;

use crate::{
ecmascript::{
Agent, InternalMethods, InternalSlots, OrdinaryObject, ProtoIntrinsics, object_handle,
Agent, ExceptionType, InternalMethods, InternalSlots, JsResult, OrdinaryObject,
ProtoIntrinsics, Value, object_handle,
},
engine::Bindable,
engine::{Bindable, NoGcScope},
heap::{
ArenaAccess, ArenaAccessMut, BaseIndex, CompactionLists, CreateHeapData, Heap,
HeapMarkAndSweep, HeapSweepWeakReference, WorkQueues, arena_vec_access,
Expand All @@ -33,8 +34,8 @@ arena_vec_access!(
);

impl TemporalPlainTime<'_> {
pub(crate) fn _inner_plain_time(self, agent: &Agent) -> &temporal_rs::PlainTime {
&self.unbind().get(agent)._plain_time
pub(crate) fn inner_plain_time(self, agent: &Agent) -> &temporal_rs::PlainTime {
&self.unbind().get(agent).plain_time
}
}

Expand Down Expand Up @@ -77,3 +78,19 @@ impl<'a> CreateHeapData<PlainTimeRecord<'a>, TemporalPlainTime<'a>> for Heap {
TemporalPlainTime(BaseIndex::last(&self.plain_times))
}
}

#[inline(always)]
fn require_internal_slot_temporal_plain_time<'a>(
agent: &mut Agent,
value: Value,
gc: NoGcScope<'a, '_>,
) -> JsResult<'a, TemporalPlainTime<'a>> {
match value {
Value::PlainTime(plain_time) => Ok(plain_time.bind(gc)),
_ => Err(agent.throw_exception_with_static_message(
ExceptionType::TypeError,
"Object is not a Temporal PlainTime",
gc,
)),
}
}
8 changes: 4 additions & 4 deletions nova_vm/src/ecmascript/builtins/temporal/plain_time/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ use crate::{
#[derive(Debug, Clone, Copy)]
pub struct PlainTimeRecord<'a> {
pub(crate) object_index: Option<OrdinaryObject<'a>>,
pub(crate) _plain_time: temporal_rs::PlainTime,
pub(crate) plain_time: temporal_rs::PlainTime,
}

impl PlainTimeRecord<'_> {
pub fn default() -> Self {
Self {
object_index: None,
_plain_time: temporal_rs::PlainTime::try_new(0, 0, 0, 0, 0, 0).unwrap(),
plain_time: temporal_rs::PlainTime::try_new(0, 0, 0, 0, 0, 0).unwrap(),
}
}
}
Expand All @@ -30,15 +30,15 @@ impl HeapMarkAndSweep for PlainTimeRecord<'static> {
fn mark_values(&self, queues: &mut WorkQueues) {
let Self {
object_index,
_plain_time: _,
plain_time: _,
} = self;

object_index.mark_values(queues);
}
fn sweep_values(&mut self, compactions: &CompactionLists) {
let Self {
object_index,
_plain_time: _,
plain_time: _,
} = self;

object_index.sweep_values(compactions);
Expand Down
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, temporal_err_to_js_err,
to_integer_with_truncation,
},
engine::{GcScope, NoGcScope},
engine::{Bindable as _, GcScope, NoGcScope, Scopable},
heap::IntrinsicConstructorIndexes,
};

Expand All @@ -28,11 +29,120 @@ impl TemporalPlainTimeConstructor {
fn constructor<'gc>(
agent: &mut Agent,
_: Value,
_args: ArgumentsList,
_new_target: Option<Object>,
args: ArgumentsList,
new_target: Option<Object>,
gc: GcScope<'gc, '_>,
) -> JsResult<'gc, Value<'gc>> {
Err(agent.todo("Temporal.PlainTime", gc.into_nogc()))
let years = args.get(1).scope(agent, gc.nogc());
let months = args.get(2).scope(agent, gc.nogc());
let weeks = args.get(3).scope(agent, gc.nogc());
let days = args.get(4).scope(agent, gc.nogc());
let hours = args.get(5).scope(agent, gc.nogc());
let minutes = args.get(6).scope(agent, gc.nogc());
let seconds = args.get(7).scope(agent, gc.nogc());
let milliseconds = args.get(8).scope(agent, gc.nogc());
let microseconds = args.get(9).scope(agent, gc.nogc());
let nanoseconds = args.get(10).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(
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 (
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))
.unbind()?
} else {
todo!() // TODO: create range error
};

// 10. Return ? CreateTemporalTime(time, NewTarget).
}

pub(crate) fn create_intrinsic(agent: &mut Agent, realm: Realm<'static>, _gc: NoGcScope) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,54 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::{
ecmascript::{Agent, BUILTIN_STRING_MEMORY, Realm, builders::OrdinaryObjectBuilder},
engine::NoGcScope,
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,
},
engine::{GcScope, NoGcScope},
heap::WellKnownSymbols,
};

pub(crate) struct TemporalPlainTimePrototype;

struct TemporalPlainTimePrototypeGetMinute;
impl Builtin for TemporalPlainTimePrototypeGetMinute {
const NAME: String<'static> = BUILTIN_STRING_MEMORY.get_minute;
const KEY: Option<PropertyKey<'static>> = Some(BUILTIN_STRING_MEMORY.minute.to_property_key());
const LENGTH: u8 = 0;
const BEHAVIOUR: Behaviour = Behaviour::Regular(TemporalPlainTimePrototype::get_minute);
}
impl BuiltinGetter for TemporalPlainTimePrototypeGetMinute {}

impl TemporalPlainTimePrototype {
pub fn create_intrinsic(agent: &mut Agent, realm: Realm<'static>, _: NoGcScope) {
/// ### [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>(
agent: &mut Agent,
this_value: Value,
_: ArgumentsList,
gc: GcScope<'gc, '_>,
) -> JsResult<'gc, Value<'gc>> {
let gc = gc.into_nogc();
// 1. Let plainTime be the this value.
// 2. Perform ? RequireInternalSlot(plainTime, [[InitializedTemporalTime]]).
let plain_time = require_internal_slot_temporal_plain_time(agent, this_value, gc)?;
// 3. Return 𝔽(plainTime.[[Time]].[[Minute]]).
let value = plain_time.inner_plain_time(agent).minute();
Ok(value.into())
}

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(2)
.with_property_capacity(3)
.with_prototype(object_prototype)
.with_constructor_property(plain_time_constructor)
.with_builtin_function_getter_property::<TemporalPlainTimePrototypeGetMinute>()
.with_property(|builder| {
builder
.with_key(WellKnownSymbols::ToStringTag.into())
Expand Down
2 changes: 0 additions & 2 deletions tests/expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -3980,8 +3980,6 @@
"built-ins/Temporal/PlainTime/prototype/microsecond/prop-desc.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/millisecond/branding.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/millisecond/prop-desc.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/minute/branding.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/minute/prop-desc.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/nanosecond/branding.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/nanosecond/prop-desc.js": "FAIL",
"built-ins/Temporal/PlainTime/prototype/round/branding.js": "FAIL",
Expand Down
4 changes: 2 additions & 2 deletions tests/metrics.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"results": {
"crash": 52,
"fail": 6971,
"pass": 40329,
"fail": 6969,
"pass": 40331,
"skip": 3326,
"timeout": 18,
"unresolved": 37
Expand Down
Loading