From f5ba6958ed61f42d686171b35fc96c259aa877cc Mon Sep 17 00:00:00 2001 From: Kevin Ness <46825870+nekevss@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:45:20 -0600 Subject: [PATCH] Patch Temporal.PlainTime and Temporal.Duration constructors (#4078) * Add JsValue::map_or and update PlainTime and Duration constructors * Fix PlainDateTime constructor as well --- .../src/builtins/temporal/duration/mod.rs | 96 +++++++++---------- .../builtins/temporal/plain_date_time/mod.rs | 12 +-- .../src/builtins/temporal/plain_time/mod.rs | 36 +++---- core/engine/src/value/mod.rs | 40 +++++++- 4 files changed, 99 insertions(+), 85 deletions(-) diff --git a/core/engine/src/builtins/temporal/duration/mod.rs b/core/engine/src/builtins/temporal/duration/mod.rs index 379e4f33100..20d61326a10 100644 --- a/core/engine/src/builtins/temporal/duration/mod.rs +++ b/core/engine/src/builtins/temporal/duration/mod.rs @@ -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). @@ -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 { - 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 { - Err(JsNativeError::range() + Err(JsNativeError::error() .with_message("not yet implemented.") .into()) } diff --git a/core/engine/src/builtins/temporal/plain_date_time/mod.rs b/core/engine/src/builtins/temporal/plain_date_time/mod.rs index f7409a1b951..811e0ce2ca1 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -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))?; diff --git a/core/engine/src/builtins/temporal/plain_time/mod.rs b/core/engine/src/builtins/temporal/plain_time/mod.rs index d453a5831ab..31acfb735de 100644 --- a/core/engine/src/builtins/temporal/plain_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_time/mod.rs @@ -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)?; diff --git a/core/engine/src/value/mod.rs b/core/engine/src/value/mod.rs index d44b12ee9d0..c767c1b8463 100644 --- a/core/engine/src/value/mod.rs +++ b/core/engine/src/value/mod.rs @@ -1054,7 +1054,7 @@ impl JsValue { } } - /// Maps a `JsValue` into a `Option` where T is the result of an + /// Maps a `JsValue` into `Option` where T is the result of an /// operation on a defined value. If the value is `JsValue::undefined`, /// then `JsValue::map` will return None. /// @@ -1075,7 +1075,6 @@ impl JsValue { /// assert_eq!(undefined_result, None); /// /// ``` - /// #[inline] #[must_use] pub fn map(&self, f: F) -> Option @@ -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(&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.