Skip to content

Commit

Permalink
Patch Temporal.PlainTime and Temporal.Duration constructors (#4078)
Browse files Browse the repository at this point in the history
* Add JsValue::map_or and update PlainTime and Duration constructors

* Fix PlainDateTime constructor as well
  • Loading branch information
nekevss authored Dec 11, 2024
1 parent 934546e commit f5ba695
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 85 deletions.
96 changes: 43 additions & 53 deletions core/engine/src/builtins/temporal/duration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,76 +221,66 @@ impl BuiltInConstructor for Duration {
}

// 2. If years is undefined, let y be 0; else let y be ? ToIntegerIfIntegral(years).
let years = FiniteF64::from(
args.first()
.map_or(Ok(0), |y| to_integer_if_integral(y, context))?,
);
let years = args
.get_or_undefined(0)
.map_or(Ok(0), |y| to_integer_if_integral(y, context))?;

// 3. If months is undefined, let mo be 0; else let mo be ? ToIntegerIfIntegral(months).
let months = FiniteF64::from(
args.get(1)
.map_or(Ok(0), |mo| to_integer_if_integral(mo, context))?,
);
let months = args
.get_or_undefined(1)
.map_or(Ok(0), |mo| to_integer_if_integral(mo, context))?;

// 4. If weeks is undefined, let w be 0; else let w be ? ToIntegerIfIntegral(weeks).
let weeks = FiniteF64::from(
args.get(2)
.map_or(Ok(0), |wk| to_integer_if_integral(wk, context))?,
);
let weeks = args
.get_or_undefined(2)
.map_or(Ok(0), |wk| to_integer_if_integral(wk, context))?;

// 5. If days is undefined, let d be 0; else let d be ? ToIntegerIfIntegral(days).
let days = FiniteF64::from(
args.get(3)
.map_or(Ok(0), |d| to_integer_if_integral(d, context))?,
);
let days = args
.get_or_undefined(3)
.map_or(Ok(0), |d| to_integer_if_integral(d, context))?;

// 6. If hours is undefined, let h be 0; else let h be ? ToIntegerIfIntegral(hours).
let hours = FiniteF64::from(
args.get(4)
.map_or(Ok(0), |h| to_integer_if_integral(h, context))?,
);
let hours = args
.get_or_undefined(4)
.map_or(Ok(0), |h| to_integer_if_integral(h, context))?;

// 7. If minutes is undefined, let m be 0; else let m be ? ToIntegerIfIntegral(minutes).
let minutes = FiniteF64::from(
args.get(5)
.map_or(Ok(0), |m| to_integer_if_integral(m, context))?,
);
let minutes = args
.get_or_undefined(5)
.map_or(Ok(0), |m| to_integer_if_integral(m, context))?;

// 8. If seconds is undefined, let s be 0; else let s be ? ToIntegerIfIntegral(seconds).
let seconds = FiniteF64::from(
args.get(6)
.map_or(Ok(0), |s| to_integer_if_integral(s, context))?,
);
let seconds = args
.get_or_undefined(6)
.map_or(Ok(0), |s| to_integer_if_integral(s, context))?;

// 9. If milliseconds is undefined, let ms be 0; else let ms be ? ToIntegerIfIntegral(milliseconds).
let milliseconds = FiniteF64::from(
args.get(7)
.map_or(Ok(0), |ms| to_integer_if_integral(ms, context))?,
);
let milliseconds = args
.get_or_undefined(7)
.map_or(Ok(0), |ms| to_integer_if_integral(ms, context))?;

// 10. If microseconds is undefined, let mis be 0; else let mis be ? ToIntegerIfIntegral(microseconds).
let microseconds = FiniteF64::from(
args.get(8)
.map_or(Ok(0), |mis| to_integer_if_integral(mis, context))?,
);
let microseconds = args
.get_or_undefined(8)
.map_or(Ok(0), |mis| to_integer_if_integral(mis, context))?;

// 11. If nanoseconds is undefined, let ns be 0; else let ns be ? ToIntegerIfIntegral(nanoseconds).
let nanoseconds = FiniteF64::from(
args.get(9)
.map_or(Ok(0), |ns| to_integer_if_integral(ns, context))?,
);
let nanoseconds = args
.get_or_undefined(9)
.map_or(Ok(0), |ns| to_integer_if_integral(ns, context))?;

let record = InnerDuration::new(
years,
months,
weeks,
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
years.into(),
months.into(),
weeks.into(),
days.into(),
hours.into(),
minutes.into(),
seconds.into(),
milliseconds.into(),
microseconds.into(),
nanoseconds.into(),
)?;

// 12. Return ? CreateTemporalDuration(y, mo, w, d, h, m, s, ms, mis, ns, NewTarget).
Expand Down Expand Up @@ -786,21 +776,21 @@ impl Duration {

// TODO: Implement the rest of the new `Temporal.Duration.prototype.total`

Err(JsNativeError::range()
Err(JsNativeError::error()
.with_message("not yet implemented.")
.into())
}

/// 7.3.22 `Temporal.Duration.prototype.toString ( [ options ] )`
pub(crate) fn to_string(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
Err(JsNativeError::range()
Err(JsNativeError::error()
.with_message("not yet implemented.")
.into())
}

/// 7.3.23 `Temporal.Duration.prototype.toJSON ( )`
pub(crate) fn to_json(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
Err(JsNativeError::range()
Err(JsNativeError::error()
.with_message("not yet implemented.")
.into())
}
Expand Down
12 changes: 6 additions & 6 deletions core/engine/src/builtins/temporal/plain_date_time/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,27 +314,27 @@ impl BuiltInConstructor for PlainDateTime {
let iso_day = to_integer_with_truncation(args.get_or_undefined(2), context)?;
// 5. If hour is undefined, set hour to 0; else set hour to ? ToIntegerWithTruncation(hour).
let hour = args
.get(3)
.get_or_undefined(3)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 6. If minute is undefined, set minute to 0; else set minute to ? ToIntegerWithTruncation(minute).
let minute = args
.get(4)
.get_or_undefined(4)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 7. If second is undefined, set second to 0; else set second to ? ToIntegerWithTruncation(second).
let second = args
.get(5)
.get_or_undefined(5)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 8. If millisecond is undefined, set millisecond to 0; else set millisecond to ? ToIntegerWithTruncation(millisecond).
let millisecond = args
.get(6)
.get_or_undefined(6)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 9. If microsecond is undefined, set microsecond to 0; else set microsecond to ? ToIntegerWithTruncation(microsecond).
let microsecond = args
.get(7)
.get_or_undefined(7)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 10. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to ? ToIntegerWithTruncation(nanosecond).
let nanosecond = args
.get(8)
.get_or_undefined(8)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 11. Let calendar be ? ToTemporalCalendarSlotValue(calendarLike, "iso8601").
let calendar_slot = to_temporal_calendar_slot_value(args.get_or_undefined(9))?;
Expand Down
36 changes: 12 additions & 24 deletions core/engine/src/builtins/temporal/plain_time/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,40 +150,28 @@ impl BuiltInConstructor for PlainTime {

// 2. If hour is undefined, set hour to 0; else set hour to ? ToIntegerWithTruncation(hour).
let hour = args
.first()
.map(|v| to_integer_with_truncation(v, context))
.transpose()?
.unwrap_or(0);
.get_or_undefined(0)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 3. If minute is undefined, set minute to 0; else set minute to ? ToIntegerWithTruncation(minute).
let minute = args
.get(1)
.map(|v| to_integer_with_truncation(v, context))
.transpose()?
.unwrap_or(0);
.get_or_undefined(1)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 4. If second is undefined, set second to 0; else set second to ? ToIntegerWithTruncation(second).
let second = args
.get(2)
.map(|v| to_integer_with_truncation(v, context))
.transpose()?
.unwrap_or(0);
.get_or_undefined(2)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 5. If millisecond is undefined, set millisecond to 0; else set millisecond to ? ToIntegerWithTruncation(millisecond).
let millisecond = args
.get(3)
.map(|v| to_integer_with_truncation(v, context))
.transpose()?
.unwrap_or(0);
.get_or_undefined(3)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 6. If microsecond is undefined, set microsecond to 0; else set microsecond to ? ToIntegerWithTruncation(microsecond).
let microsecond = args
.get(4)
.map(|v| to_integer_with_truncation(v, context))
.transpose()?
.unwrap_or(0);
.get_or_undefined(4)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;
// 7. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to ? ToIntegerWithTruncation(nanosecond).
let nanosecond = args
.get(5)
.map(|v| to_integer_with_truncation(v, context))
.transpose()?
.unwrap_or(0);
.get_or_undefined(5)
.map_or(Ok(0), |v| to_integer_with_truncation(v, context))?;

let inner =
PlainTimeInner::new(hour, minute, second, millisecond, microsecond, nanosecond)?;
Expand Down
40 changes: 38 additions & 2 deletions core/engine/src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1054,7 +1054,7 @@ impl JsValue {
}
}

/// Maps a `JsValue` into a `Option<T>` where T is the result of an
/// Maps a `JsValue` into `Option<T>` where T is the result of an
/// operation on a defined value. If the value is `JsValue::undefined`,
/// then `JsValue::map` will return None.
///
Expand All @@ -1075,7 +1075,6 @@ impl JsValue {
/// assert_eq!(undefined_result, None);
///
/// ```
///
#[inline]
#[must_use]
pub fn map<T, F>(&self, f: F) -> Option<T>
Expand All @@ -1088,6 +1087,43 @@ impl JsValue {
Some(f(self))
}

/// Maps a `JsValue` into `T` where T is the result of an
/// operation on a defined value. If the value is `JsValue::undefined`,
/// then `JsValue::map` will return the provided default value.
///
/// # Example
///
/// ```
/// use boa_engine::{JsValue, Context};
///
/// let mut context = Context::default();
///
/// let defined_value = JsValue::from(5);
/// let undefined = JsValue::undefined();
///
/// let defined_result = defined_value
/// .map_or(Ok(JsValue::Boolean(true)), |v| v.add(&JsValue::from(5), &mut context))
/// .unwrap();
/// let undefined_result = undefined
/// .map_or(Ok(JsValue::Boolean(true)), |v| v.add(&JsValue::from(5), &mut context))
/// .unwrap();
///
/// assert_eq!(defined_result, JsValue::Integer(10));
/// assert_eq!(undefined_result, JsValue::Boolean(true));
///
/// ```
#[inline]
#[must_use]
pub fn map_or<T, F>(&self, default: T, f: F) -> T
where
F: FnOnce(&JsValue) -> T,
{
if self.is_undefined() {
return default;
}
f(self)
}

/// Abstract operation `IsArray ( argument )`
///
/// Check if a value is an array.
Expand Down

0 comments on commit f5ba695

Please sign in to comment.