tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PlainTime.cpp (44667B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "builtin/temporal/PlainTime.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 
     11 #include <algorithm>
     12 #include <cmath>
     13 #include <cstdlib>
     14 
     15 #include "jsnum.h"
     16 #include "jspubtd.h"
     17 #include "NamespaceImports.h"
     18 
     19 #include "builtin/intl/DateTimeFormat.h"
     20 #include "builtin/temporal/Duration.h"
     21 #include "builtin/temporal/PlainDateTime.h"
     22 #include "builtin/temporal/Temporal.h"
     23 #include "builtin/temporal/TemporalParser.h"
     24 #include "builtin/temporal/TemporalRoundingMode.h"
     25 #include "builtin/temporal/TemporalTypes.h"
     26 #include "builtin/temporal/TemporalUnit.h"
     27 #include "builtin/temporal/TimeZone.h"
     28 #include "builtin/temporal/ToString.h"
     29 #include "builtin/temporal/ZonedDateTime.h"
     30 #include "gc/AllocKind.h"
     31 #include "gc/Barrier.h"
     32 #include "js/CallArgs.h"
     33 #include "js/CallNonGenericMethod.h"
     34 #include "js/Class.h"
     35 #include "js/ErrorReport.h"
     36 #include "js/friend/ErrorMessages.h"
     37 #include "js/PropertyDescriptor.h"
     38 #include "js/PropertySpec.h"
     39 #include "js/RootingAPI.h"
     40 #include "js/Value.h"
     41 #include "vm/BytecodeUtil.h"
     42 #include "vm/GlobalObject.h"
     43 #include "vm/JSAtomState.h"
     44 #include "vm/JSContext.h"
     45 #include "vm/JSObject.h"
     46 #include "vm/PlainObject.h"
     47 
     48 #include "vm/JSObject-inl.h"
     49 #include "vm/NativeObject-inl.h"
     50 #include "vm/ObjectOperations-inl.h"
     51 
     52 using namespace js;
     53 using namespace js::temporal;
     54 
     55 static inline bool IsPlainTime(Handle<Value> v) {
     56  return v.isObject() && v.toObject().is<PlainTimeObject>();
     57 }
     58 
     59 #ifdef DEBUG
     60 /**
     61 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
     62 */
     63 template <typename T>
     64 static bool IsValidTime(T hour, T minute, T second, T millisecond,
     65                        T microsecond, T nanosecond) {
     66  static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, double>);
     67 
     68  MOZ_ASSERT(IsInteger(hour));
     69  MOZ_ASSERT(IsInteger(minute));
     70  MOZ_ASSERT(IsInteger(second));
     71  MOZ_ASSERT(IsInteger(millisecond));
     72  MOZ_ASSERT(IsInteger(microsecond));
     73  MOZ_ASSERT(IsInteger(nanosecond));
     74 
     75  // Step 1.
     76  if (hour < 0 || hour > 23) {
     77    return false;
     78  }
     79 
     80  // Step 2.
     81  if (minute < 0 || minute > 59) {
     82    return false;
     83  }
     84 
     85  // Step 3.
     86  if (second < 0 || second > 59) {
     87    return false;
     88  }
     89 
     90  // Step 4.
     91  if (millisecond < 0 || millisecond > 999) {
     92    return false;
     93  }
     94 
     95  // Step 5.
     96  if (microsecond < 0 || microsecond > 999) {
     97    return false;
     98  }
     99 
    100  // Step 6.
    101  if (nanosecond < 0 || nanosecond > 999) {
    102    return false;
    103  }
    104 
    105  // Step 7.
    106  return true;
    107 }
    108 
    109 /**
    110 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
    111 */
    112 bool js::temporal::IsValidTime(const Time& time) {
    113  const auto& [hour, minute, second, millisecond, microsecond, nanosecond] =
    114      time;
    115  return ::IsValidTime(hour, minute, second, millisecond, microsecond,
    116                       nanosecond);
    117 }
    118 
    119 /**
    120 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
    121 */
    122 bool js::temporal::IsValidTime(double hour, double minute, double second,
    123                               double millisecond, double microsecond,
    124                               double nanosecond) {
    125  return ::IsValidTime(hour, minute, second, millisecond, microsecond,
    126                       nanosecond);
    127 }
    128 #endif
    129 
    130 static void ReportInvalidTimeValue(JSContext* cx, const char* name, int32_t min,
    131                                   int32_t max, double num) {
    132  Int32ToCStringBuf minCbuf;
    133  const char* minStr = Int32ToCString(&minCbuf, min);
    134 
    135  Int32ToCStringBuf maxCbuf;
    136  const char* maxStr = Int32ToCString(&maxCbuf, max);
    137 
    138  ToCStringBuf numCbuf;
    139  const char* numStr = NumberToCString(&numCbuf, num);
    140 
    141  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    142                            JSMSG_TEMPORAL_PLAIN_TIME_INVALID_VALUE, name,
    143                            minStr, maxStr, numStr);
    144 }
    145 
    146 static inline bool ThrowIfInvalidTimeValue(JSContext* cx, const char* name,
    147                                           int32_t min, int32_t max,
    148                                           double num) {
    149  if (min <= num && num <= max) {
    150    return true;
    151  }
    152  ReportInvalidTimeValue(cx, name, min, max, num);
    153  return false;
    154 }
    155 
    156 /**
    157 * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond )
    158 */
    159 bool js::temporal::ThrowIfInvalidTime(JSContext* cx, double hour, double minute,
    160                                      double second, double millisecond,
    161                                      double microsecond, double nanosecond) {
    162  // Step 1.
    163  MOZ_ASSERT(IsInteger(hour));
    164  MOZ_ASSERT(IsInteger(minute));
    165  MOZ_ASSERT(IsInteger(second));
    166  MOZ_ASSERT(IsInteger(millisecond));
    167  MOZ_ASSERT(IsInteger(microsecond));
    168  MOZ_ASSERT(IsInteger(nanosecond));
    169 
    170  // Step 2.
    171  if (!ThrowIfInvalidTimeValue(cx, "hour", 0, 23, hour)) {
    172    return false;
    173  }
    174 
    175  // Step 3.
    176  if (!ThrowIfInvalidTimeValue(cx, "minute", 0, 59, minute)) {
    177    return false;
    178  }
    179 
    180  // Step 4.
    181  if (!ThrowIfInvalidTimeValue(cx, "second", 0, 59, second)) {
    182    return false;
    183  }
    184 
    185  // Step 5.
    186  if (!ThrowIfInvalidTimeValue(cx, "millisecond", 0, 999, millisecond)) {
    187    return false;
    188  }
    189 
    190  // Step 6.
    191  if (!ThrowIfInvalidTimeValue(cx, "microsecond", 0, 999, microsecond)) {
    192    return false;
    193  }
    194 
    195  // Step 7.
    196  if (!ThrowIfInvalidTimeValue(cx, "nanosecond", 0, 999, nanosecond)) {
    197    return false;
    198  }
    199 
    200  // Step 8.
    201  return true;
    202 }
    203 
    204 /**
    205 * RegulateTime ( hour, minute, second, millisecond, microsecond, nanosecond,
    206 * overflow )
    207 */
    208 bool js::temporal::RegulateTime(JSContext* cx, const TemporalTimeLike& time,
    209                                TemporalOverflow overflow, Time* result) {
    210  auto [hour, minute, second, millisecond, microsecond, nanosecond] = time;
    211  MOZ_ASSERT(IsInteger(hour));
    212  MOZ_ASSERT(IsInteger(minute));
    213  MOZ_ASSERT(IsInteger(second));
    214  MOZ_ASSERT(IsInteger(millisecond));
    215  MOZ_ASSERT(IsInteger(microsecond));
    216  MOZ_ASSERT(IsInteger(nanosecond));
    217 
    218  // Steps 1-2.
    219  if (overflow == TemporalOverflow::Constrain) {
    220    // Step 1.a.
    221    hour = std::clamp(hour, 0.0, 23.0);
    222 
    223    // Step 1.b.
    224    minute = std::clamp(minute, 0.0, 59.0);
    225 
    226    // Step 1.c.
    227    second = std::clamp(second, 0.0, 59.0);
    228 
    229    // Step 1.d.
    230    millisecond = std::clamp(millisecond, 0.0, 999.0);
    231 
    232    // Step 1.e.
    233    microsecond = std::clamp(microsecond, 0.0, 999.0);
    234 
    235    // Step 1.f.
    236    nanosecond = std::clamp(nanosecond, 0.0, 999.0);
    237  } else {
    238    // Step 2.a.
    239    MOZ_ASSERT(overflow == TemporalOverflow::Reject);
    240 
    241    // Step 2.b.
    242    if (!ThrowIfInvalidTime(cx, hour, minute, second, millisecond, microsecond,
    243                            nanosecond)) {
    244      return false;
    245    }
    246  }
    247 
    248  // Step 3.
    249  *result = {
    250      int32_t(hour),        int32_t(minute),      int32_t(second),
    251      int32_t(millisecond), int32_t(microsecond), int32_t(nanosecond),
    252  };
    253  return true;
    254 }
    255 
    256 /**
    257 * CreateTemporalTime ( time [ , newTarget ] )
    258 */
    259 static PlainTimeObject* CreateTemporalTime(JSContext* cx, const CallArgs& args,
    260                                           const Time& time) {
    261  MOZ_ASSERT(IsValidTime(time));
    262 
    263  // Steps 1-2.
    264  Rooted<JSObject*> proto(cx);
    265  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainTime,
    266                                          &proto)) {
    267    return nullptr;
    268  }
    269 
    270  auto* object = NewObjectWithClassProto<PlainTimeObject>(cx, proto);
    271  if (!object) {
    272    return nullptr;
    273  }
    274 
    275  // Step 3.
    276  auto packedTime = PackedTime::pack(time);
    277  object->initFixedSlot(
    278      PlainTimeObject::PACKED_TIME_SLOT,
    279      DoubleValue(mozilla::BitwiseCast<double>(packedTime.value)));
    280 
    281  // Step 4.
    282  return object;
    283 }
    284 
    285 /**
    286 * CreateTemporalTime ( time [ , newTarget ] )
    287 */
    288 PlainTimeObject* js::temporal::CreateTemporalTime(JSContext* cx,
    289                                                  const Time& time) {
    290  MOZ_ASSERT(IsValidTime(time));
    291 
    292  // Steps 1-2.
    293  auto* object = NewBuiltinClassInstance<PlainTimeObject>(cx);
    294  if (!object) {
    295    return nullptr;
    296  }
    297 
    298  // Step 3.
    299  auto packedTime = PackedTime::pack(time);
    300  object->initFixedSlot(
    301      PlainTimeObject::PACKED_TIME_SLOT,
    302      DoubleValue(mozilla::BitwiseCast<double>(packedTime.value)));
    303 
    304  // Step 4.
    305  return object;
    306 }
    307 
    308 /**
    309 * BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond )
    310 */
    311 template <typename IntT>
    312 static TimeRecord BalanceTime(IntT hour, IntT minute, IntT second,
    313                              IntT millisecond, IntT microsecond,
    314                              IntT nanosecond) {
    315  // Combined floor'ed division and modulo operation.
    316  auto divmod = [](IntT dividend, int32_t divisor, int32_t* remainder) {
    317    MOZ_ASSERT(divisor > 0);
    318 
    319    IntT quotient = dividend / divisor;
    320    *remainder = dividend % divisor;
    321 
    322    // The remainder is negative, add the divisor and simulate a floor instead
    323    // of trunc division.
    324    if (*remainder < 0) {
    325      *remainder += divisor;
    326      quotient -= 1;
    327    }
    328 
    329    return quotient;
    330  };
    331 
    332  Time time = {};
    333 
    334  // Steps 1-2.
    335  microsecond += divmod(nanosecond, 1000, &time.nanosecond);
    336 
    337  // Steps 3-4.
    338  millisecond += divmod(microsecond, 1000, &time.microsecond);
    339 
    340  // Steps 5-6.
    341  second += divmod(millisecond, 1000, &time.millisecond);
    342 
    343  // Steps 7-8.
    344  minute += divmod(second, 60, &time.second);
    345 
    346  // Steps 9-10.
    347  hour += divmod(minute, 60, &time.minute);
    348 
    349  // Steps 11-12.
    350  int64_t days = divmod(hour, 24, &time.hour);
    351 
    352  // Step 13.
    353  MOZ_ASSERT(IsValidTime(time));
    354  return {days, time};
    355 }
    356 
    357 /**
    358 * BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond )
    359 */
    360 static TimeRecord BalanceTime(int32_t hour, int32_t minute, int32_t second,
    361                              int32_t millisecond, int32_t microsecond,
    362                              int32_t nanosecond) {
    363  MOZ_ASSERT(-24 < hour && hour < 2 * 24);
    364  MOZ_ASSERT(-60 < minute && minute < 2 * 60);
    365  MOZ_ASSERT(-60 < second && second < 2 * 60);
    366  MOZ_ASSERT(-1000 < millisecond && millisecond < 2 * 1000);
    367  MOZ_ASSERT(-1000 < microsecond && microsecond < 2 * 1000);
    368  MOZ_ASSERT(-1000 < nanosecond && nanosecond < 2 * 1000);
    369 
    370  return BalanceTime<int32_t>(hour, minute, second, millisecond, microsecond,
    371                              nanosecond);
    372 }
    373 
    374 /**
    375 * BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond )
    376 */
    377 TimeRecord js::temporal::BalanceTime(const Time& time, int64_t nanoseconds) {
    378  MOZ_ASSERT(IsValidTime(time));
    379  MOZ_ASSERT(std::abs(nanoseconds) <= ToNanoseconds(TemporalUnit::Day));
    380 
    381  return ::BalanceTime<int64_t>(time.hour, time.minute, time.second,
    382                                time.millisecond, time.microsecond,
    383                                time.nanosecond + nanoseconds);
    384 }
    385 
    386 /**
    387 * TimeDurationFromComponents ( hours, minutes, seconds, milliseconds,
    388 * microseconds, nanoseconds )
    389 */
    390 static TimeDuration TimeDurationFromComponents(int32_t hours, int32_t minutes,
    391                                               int32_t seconds,
    392                                               int32_t milliseconds,
    393                                               int32_t microseconds,
    394                                               int32_t nanoseconds) {
    395  MOZ_ASSERT(std::abs(hours) <= 23);
    396  MOZ_ASSERT(std::abs(minutes) <= 59);
    397  MOZ_ASSERT(std::abs(seconds) <= 59);
    398  MOZ_ASSERT(std::abs(milliseconds) <= 999);
    399  MOZ_ASSERT(std::abs(microseconds) <= 999);
    400  MOZ_ASSERT(std::abs(nanoseconds) <= 999);
    401 
    402  // Steps 1-5.
    403  int64_t nanos = int64_t(hours);
    404  nanos *= 60;
    405  nanos += int64_t(minutes);
    406  nanos *= 60;
    407  nanos += int64_t(seconds);
    408  nanos *= 1000;
    409  nanos += int64_t(milliseconds);
    410  nanos *= 1000;
    411  nanos += int64_t(microseconds);
    412  nanos *= 1000;
    413  nanos += int64_t(nanoseconds);
    414  MOZ_ASSERT(std::abs(nanos) < ToNanoseconds(TemporalUnit::Day));
    415 
    416  auto timeDuration = TimeDuration::fromNanoseconds(nanos);
    417 
    418  // Step 6.
    419  MOZ_ASSERT(IsValidTimeDuration(timeDuration));
    420 
    421  // Step 7.
    422  return timeDuration;
    423 }
    424 
    425 /**
    426 * DifferenceTime ( time1, time2 )
    427 */
    428 TimeDuration js::temporal::DifferenceTime(const Time& time1,
    429                                          const Time& time2) {
    430  MOZ_ASSERT(IsValidTime(time1));
    431  MOZ_ASSERT(IsValidTime(time2));
    432 
    433  // Step 1.
    434  int32_t hours = time2.hour - time1.hour;
    435 
    436  // Step 2.
    437  int32_t minutes = time2.minute - time1.minute;
    438 
    439  // Step 3.
    440  int32_t seconds = time2.second - time1.second;
    441 
    442  // Step 4.
    443  int32_t milliseconds = time2.millisecond - time1.millisecond;
    444 
    445  // Step 5.
    446  int32_t microseconds = time2.microsecond - time1.microsecond;
    447 
    448  // Step 6.
    449  int32_t nanoseconds = time2.nanosecond - time1.nanosecond;
    450 
    451  // Step 7.
    452  auto result = ::TimeDurationFromComponents(
    453      hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
    454 
    455  // Step 8.
    456  MOZ_ASSERT(result.abs() < TimeDuration::fromDays(1));
    457 
    458  // Step 9.
    459  return result;
    460 }
    461 
    462 /**
    463 * ToTemporalTimeRecord ( temporalTimeLike [ , completeness ] )
    464 */
    465 static bool ToTemporalTimeRecord(JSContext* cx,
    466                                 Handle<JSObject*> temporalTimeLike,
    467                                 TemporalTimeLike* result) {
    468  // Steps 1-3. (Not applicable in our implementation.)
    469 
    470  // Step 4.
    471  bool any = false;
    472 
    473  Rooted<Value> value(cx);
    474  auto getTimeProperty = [&](Handle<PropertyName*> property, const char* name,
    475                             double* num) {
    476    if (!GetProperty(cx, temporalTimeLike, temporalTimeLike, property,
    477                     &value)) {
    478      return false;
    479    }
    480 
    481    if (!value.isUndefined()) {
    482      any = true;
    483 
    484      if (!ToIntegerWithTruncation(cx, value, name, num)) {
    485        return false;
    486      }
    487    }
    488    return true;
    489  };
    490 
    491  // Steps 5-6.
    492  if (!getTimeProperty(cx->names().hour, "hour", &result->hour)) {
    493    return false;
    494  }
    495 
    496  // Steps 7-8.
    497  if (!getTimeProperty(cx->names().microsecond, "microsecond",
    498                       &result->microsecond)) {
    499    return false;
    500  }
    501 
    502  // Steps 9-10.
    503  if (!getTimeProperty(cx->names().millisecond, "millisecond",
    504                       &result->millisecond)) {
    505    return false;
    506  }
    507 
    508  // Steps 11-12.
    509  if (!getTimeProperty(cx->names().minute, "minute", &result->minute)) {
    510    return false;
    511  }
    512 
    513  // Steps 13-14.
    514  if (!getTimeProperty(cx->names().nanosecond, "nanosecond",
    515                       &result->nanosecond)) {
    516    return false;
    517  }
    518 
    519  // Steps 15-16.
    520  if (!getTimeProperty(cx->names().second, "second", &result->second)) {
    521    return false;
    522  }
    523 
    524  // Step 17.
    525  if (!any) {
    526    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    527                              JSMSG_TEMPORAL_PLAIN_TIME_MISSING_UNIT);
    528    return false;
    529  }
    530 
    531  // Step 18.
    532  return true;
    533 }
    534 
    535 struct TimeOptions {
    536  TemporalOverflow overflow = TemporalOverflow::Constrain;
    537 };
    538 
    539 /**
    540 * ToTemporalTime ( item [ , options ] )
    541 */
    542 static bool ToTemporalTimeOptions(JSContext* cx, Handle<Value> options,
    543                                  TimeOptions* result) {
    544  if (options.isUndefined()) {
    545    *result = {};
    546    return true;
    547  }
    548 
    549  // NOTE: |options| are only passed from `Temporal.PlainTime.from`.
    550 
    551  Rooted<JSObject*> resolvedOptions(
    552      cx, RequireObjectArg(cx, "options", "from", options));
    553  if (!resolvedOptions) {
    554    return false;
    555  }
    556 
    557  auto overflow = TemporalOverflow::Constrain;
    558  if (!GetTemporalOverflowOption(cx, resolvedOptions, &overflow)) {
    559    return false;
    560  }
    561 
    562  *result = {overflow};
    563  return true;
    564 }
    565 
    566 /**
    567 * ToTemporalTime ( item [ , options ] )
    568 */
    569 static bool ToTemporalTime(JSContext* cx, Handle<JSObject*> item,
    570                           Handle<Value> options, Time* result) {
    571  // Step 2.a.
    572  if (auto* plainTime = item->maybeUnwrapIf<PlainTimeObject>()) {
    573    auto time = plainTime->time();
    574 
    575    // Steps 2.a.i-ii.
    576    TimeOptions ignoredOptions;
    577    if (!ToTemporalTimeOptions(cx, options, &ignoredOptions)) {
    578      return false;
    579    }
    580 
    581    // Step 2.a.iii.
    582    *result = time;
    583    return true;
    584  }
    585 
    586  // Step 2.b.
    587  if (auto* dateTime = item->maybeUnwrapIf<PlainDateTimeObject>()) {
    588    auto time = dateTime->time();
    589 
    590    // Steps 2.b.i-ii.
    591    TimeOptions ignoredOptions;
    592    if (!ToTemporalTimeOptions(cx, options, &ignoredOptions)) {
    593      return false;
    594    }
    595 
    596    // Step 2.b.iii.
    597    *result = time;
    598    return true;
    599  }
    600 
    601  // Step 2.c.
    602  if (auto* zonedDateTime = item->maybeUnwrapIf<ZonedDateTimeObject>()) {
    603    auto epochNs = zonedDateTime->epochNanoseconds();
    604    Rooted<TimeZoneValue> timeZone(cx, zonedDateTime->timeZone());
    605 
    606    if (!timeZone.wrap(cx)) {
    607      return false;
    608    }
    609 
    610    // Steps 2.c.i.
    611    ISODateTime dateTime;
    612    if (!GetISODateTimeFor(cx, timeZone, epochNs, &dateTime)) {
    613      return false;
    614    }
    615 
    616    // Steps 2.c.ii-iii.
    617    TimeOptions ignoredOptions;
    618    if (!ToTemporalTimeOptions(cx, options, &ignoredOptions)) {
    619      return false;
    620    }
    621 
    622    // Step 2.c.iv.
    623    *result = dateTime.time;
    624    return true;
    625  }
    626 
    627  // Step 2.d.
    628  TemporalTimeLike timeResult{};
    629  if (!ToTemporalTimeRecord(cx, item, &timeResult)) {
    630    return false;
    631  }
    632 
    633  // Steps 2.e-f.
    634  TimeOptions resolvedOptions;
    635  if (!ToTemporalTimeOptions(cx, options, &resolvedOptions)) {
    636    return false;
    637  }
    638  auto [overflow] = resolvedOptions;
    639 
    640  // Step 2.g and 4.
    641  return RegulateTime(cx, timeResult, overflow, result);
    642 }
    643 
    644 /**
    645 * ToTemporalTime ( item [ , options ] )
    646 */
    647 static bool ToTemporalTime(JSContext* cx, Handle<Value> item,
    648                           Handle<Value> options, Time* result) {
    649  // Step 1. (Not applicable in our implementation.)
    650 
    651  // Step 2.
    652  if (item.isObject()) {
    653    Rooted<JSObject*> itemObj(cx, &item.toObject());
    654    return ToTemporalTime(cx, itemObj, options, result);
    655  }
    656 
    657  // Step 3.a.
    658  if (!item.isString()) {
    659    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
    660                     nullptr, "not a string");
    661    return false;
    662  }
    663  Rooted<JSString*> string(cx, item.toString());
    664 
    665  // Steps 3.b-e.
    666  if (!ParseTemporalTimeString(cx, string, result)) {
    667    return false;
    668  }
    669  MOZ_ASSERT(IsValidTime(*result));
    670 
    671  // Steps 3.f-g.
    672  TimeOptions ignoredOptions;
    673  if (!ToTemporalTimeOptions(cx, options, &ignoredOptions)) {
    674    return false;
    675  }
    676 
    677  // Step 4.
    678  return true;
    679 }
    680 
    681 /**
    682 * ToTemporalTime ( item [ , options ] )
    683 */
    684 bool js::temporal::ToTemporalTime(JSContext* cx, Handle<Value> item,
    685                                  Time* result) {
    686  return ToTemporalTime(cx, item, UndefinedHandleValue, result);
    687 }
    688 
    689 /**
    690 * CompareTimeRecord ( time1, time2 )
    691 */
    692 int32_t js::temporal::CompareTimeRecord(const Time& one, const Time& two) {
    693  // Steps 1-2.
    694  if (int32_t diff = one.hour - two.hour) {
    695    return diff < 0 ? -1 : 1;
    696  }
    697 
    698  // Steps 3-4.
    699  if (int32_t diff = one.minute - two.minute) {
    700    return diff < 0 ? -1 : 1;
    701  }
    702 
    703  // Steps 5-6.
    704  if (int32_t diff = one.second - two.second) {
    705    return diff < 0 ? -1 : 1;
    706  }
    707 
    708  // Steps 7-8.
    709  if (int32_t diff = one.millisecond - two.millisecond) {
    710    return diff < 0 ? -1 : 1;
    711  }
    712 
    713  // Steps 9-10.
    714  if (int32_t diff = one.microsecond - two.microsecond) {
    715    return diff < 0 ? -1 : 1;
    716  }
    717 
    718  // Steps 11-12.
    719  if (int32_t diff = one.nanosecond - two.nanosecond) {
    720    return diff < 0 ? -1 : 1;
    721  }
    722 
    723  // Step 13.
    724  return 0;
    725 }
    726 
    727 static int64_t TimeToNanos(const Time& time) {
    728  // No overflow possible because the input is a valid time.
    729  MOZ_ASSERT(IsValidTime(time));
    730 
    731  int64_t hour = time.hour;
    732  int64_t minute = time.minute;
    733  int64_t second = time.second;
    734  int64_t millisecond = time.millisecond;
    735  int64_t microsecond = time.microsecond;
    736  int64_t nanosecond = time.nanosecond;
    737 
    738  int64_t millis = ((hour * 60 + minute) * 60 + second) * 1000 + millisecond;
    739  return (millis * 1000 + microsecond) * 1000 + nanosecond;
    740 }
    741 
    742 /**
    743 * RoundTime ( time, increment, unit, roundingMode )
    744 */
    745 TimeRecord js::temporal::RoundTime(const Time& time, Increment increment,
    746                                   TemporalUnit unit,
    747                                   TemporalRoundingMode roundingMode) {
    748  MOZ_ASSERT(IsValidTime(time));
    749  MOZ_ASSERT(unit >= TemporalUnit::Day);
    750  MOZ_ASSERT_IF(unit > TemporalUnit::Day,
    751                increment <= MaximumTemporalDurationRoundingIncrement(unit));
    752  MOZ_ASSERT_IF(unit == TemporalUnit::Day, increment == Increment{1});
    753 
    754  int32_t days = 0;
    755  auto [hour, minute, second, millisecond, microsecond, nanosecond] = time;
    756 
    757  // Steps 1-6.
    758  Time quantity;
    759  int32_t* result;
    760  switch (unit) {
    761    case TemporalUnit::Day:
    762      quantity = time;
    763      result = &days;
    764      break;
    765    case TemporalUnit::Hour:
    766      quantity = time;
    767      result = &hour;
    768      minute = 0;
    769      second = 0;
    770      millisecond = 0;
    771      microsecond = 0;
    772      nanosecond = 0;
    773      break;
    774    case TemporalUnit::Minute:
    775      quantity = {0, minute, second, millisecond, microsecond, nanosecond};
    776      result = &minute;
    777      second = 0;
    778      millisecond = 0;
    779      microsecond = 0;
    780      nanosecond = 0;
    781      break;
    782    case TemporalUnit::Second:
    783      quantity = {0, 0, second, millisecond, microsecond, nanosecond};
    784      result = &second;
    785      millisecond = 0;
    786      microsecond = 0;
    787      nanosecond = 0;
    788      break;
    789    case TemporalUnit::Millisecond:
    790      quantity = {0, 0, 0, millisecond, microsecond, nanosecond};
    791      result = &millisecond;
    792      microsecond = 0;
    793      nanosecond = 0;
    794      break;
    795    case TemporalUnit::Microsecond:
    796      quantity = {0, 0, 0, 0, microsecond, nanosecond};
    797      result = &microsecond;
    798      nanosecond = 0;
    799      break;
    800    case TemporalUnit::Nanosecond:
    801      quantity = {0, 0, 0, 0, 0, nanosecond};
    802      result = &nanosecond;
    803      break;
    804 
    805    case TemporalUnit::Unset:
    806    case TemporalUnit::Auto:
    807    case TemporalUnit::Year:
    808    case TemporalUnit::Month:
    809    case TemporalUnit::Week:
    810      MOZ_CRASH("unexpected temporal unit");
    811  }
    812 
    813  int64_t quantityNs = TimeToNanos(quantity);
    814  MOZ_ASSERT(0 <= quantityNs && quantityNs < ToNanoseconds(TemporalUnit::Day));
    815 
    816  // Step 7.
    817  int64_t unitLength = ToNanoseconds(unit);
    818  int64_t incrementNs = increment.value() * unitLength;
    819  MOZ_ASSERT(incrementNs <= ToNanoseconds(TemporalUnit::Day),
    820             "incrementNs doesn't overflow time resolution");
    821 
    822  // Step 8.
    823  int64_t r = RoundNumberToIncrement(quantityNs, incrementNs, roundingMode) /
    824              unitLength;
    825  MOZ_ASSERT(r == int64_t(int32_t(r)),
    826             "can't overflow when inputs are all in range");
    827 
    828  *result = int32_t(r);
    829 
    830  // Step 9.
    831  if (unit == TemporalUnit::Day) {
    832    return {int64_t(days), {0, 0, 0, 0, 0, 0}};
    833  }
    834 
    835  // Steps 10-16.
    836  return ::BalanceTime(hour, minute, second, millisecond, microsecond,
    837                       nanosecond);
    838 }
    839 
    840 /**
    841 * AddTime ( time, timeDuration )
    842 */
    843 TimeRecord js::temporal::AddTime(const Time& time,
    844                                 const TimeDuration& duration) {
    845  MOZ_ASSERT(IsValidTime(time));
    846  MOZ_ASSERT(IsValidTimeDuration(duration));
    847 
    848  auto [seconds, nanoseconds] = duration.denormalize();
    849  MOZ_ASSERT(std::abs(nanoseconds) <= 999'999'999);
    850 
    851  // Steps 1-2.
    852  return ::BalanceTime<int64_t>(time.hour, time.minute, time.second + seconds,
    853                                time.millisecond, time.microsecond,
    854                                time.nanosecond + nanoseconds);
    855 }
    856 
    857 /**
    858 * DifferenceTemporalPlainTime ( operation, temporalTime, other, options )
    859 */
    860 static bool DifferenceTemporalPlainTime(JSContext* cx,
    861                                        TemporalDifference operation,
    862                                        const CallArgs& args) {
    863  auto temporalTime = args.thisv().toObject().as<PlainTimeObject>().time();
    864 
    865  // Step 1.
    866  Time other;
    867  if (!ToTemporalTime(cx, args.get(0), &other)) {
    868    return false;
    869  }
    870 
    871  // Steps 2-3.
    872  DifferenceSettings settings;
    873  if (args.hasDefined(1)) {
    874    // Step 2.
    875    Rooted<JSObject*> options(
    876        cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
    877    if (!options) {
    878      return false;
    879    }
    880 
    881    // Step 3.
    882    if (!GetDifferenceSettings(cx, operation, options, TemporalUnitGroup::Time,
    883                               TemporalUnit::Nanosecond, TemporalUnit::Hour,
    884                               &settings)) {
    885      return false;
    886    }
    887  } else {
    888    // Steps 2-3.
    889    settings = {
    890        TemporalUnit::Nanosecond,
    891        TemporalUnit::Hour,
    892        TemporalRoundingMode::Trunc,
    893        Increment{1},
    894    };
    895  }
    896 
    897  // Step 4.
    898  auto timeDuration = DifferenceTime(temporalTime, other);
    899 
    900  // Step 5.
    901  timeDuration =
    902      RoundTimeDuration(timeDuration, settings.roundingIncrement,
    903                        settings.smallestUnit, settings.roundingMode);
    904 
    905  // Steps 6-7.
    906  Duration duration;
    907  if (!TemporalDurationFromInternal(cx, timeDuration, settings.largestUnit,
    908                                    &duration)) {
    909    return false;
    910  }
    911 
    912  // Step 8.
    913  if (operation == TemporalDifference::Since) {
    914    duration = duration.negate();
    915  }
    916 
    917  // Step 9.
    918  auto* result = CreateTemporalDuration(cx, duration);
    919  if (!result) {
    920    return false;
    921  }
    922 
    923  args.rval().setObject(*result);
    924  return true;
    925 }
    926 
    927 /**
    928 * AddDurationToTime ( operation, temporalTime, temporalDurationLike )
    929 */
    930 static bool AddDurationToTime(JSContext* cx, TemporalAddDuration operation,
    931                              const CallArgs& args) {
    932  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
    933  auto time = temporalTime->time();
    934 
    935  // Step 1.
    936  Duration duration;
    937  if (!ToTemporalDuration(cx, args.get(0), &duration)) {
    938    return false;
    939  }
    940 
    941  // Step 2.
    942  if (operation == TemporalAddDuration::Subtract) {
    943    duration = duration.negate();
    944  }
    945 
    946  // Step 3. (Inlined ToInternalDurationRecord)
    947  auto timeDuration = TimeDurationFromComponents(duration);
    948 
    949  // Step 4.
    950  auto result = AddTime(time, timeDuration);
    951  MOZ_ASSERT(IsValidTime(result.time));
    952 
    953  // Step 5.
    954  auto* obj = CreateTemporalTime(cx, result.time);
    955  if (!obj) {
    956    return false;
    957  }
    958 
    959  args.rval().setObject(*obj);
    960  return true;
    961 }
    962 
    963 /**
    964 * Temporal.PlainTime ( [ hour [ , minute [ , second [ , millisecond [ ,
    965 * microsecond [ , nanosecond ] ] ] ] ] ] )
    966 */
    967 static bool PlainTimeConstructor(JSContext* cx, unsigned argc, Value* vp) {
    968  CallArgs args = CallArgsFromVp(argc, vp);
    969 
    970  // Step 1.
    971  if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainTime")) {
    972    return false;
    973  }
    974 
    975  // Step 2.
    976  double hour = 0;
    977  if (args.hasDefined(0)) {
    978    if (!ToIntegerWithTruncation(cx, args[0], "hour", &hour)) {
    979      return false;
    980    }
    981  }
    982 
    983  // Step 3.
    984  double minute = 0;
    985  if (args.hasDefined(1)) {
    986    if (!ToIntegerWithTruncation(cx, args[1], "minute", &minute)) {
    987      return false;
    988    }
    989  }
    990 
    991  // Step 4.
    992  double second = 0;
    993  if (args.hasDefined(2)) {
    994    if (!ToIntegerWithTruncation(cx, args[2], "second", &second)) {
    995      return false;
    996    }
    997  }
    998 
    999  // Step 5.
   1000  double millisecond = 0;
   1001  if (args.hasDefined(3)) {
   1002    if (!ToIntegerWithTruncation(cx, args[3], "millisecond", &millisecond)) {
   1003      return false;
   1004    }
   1005  }
   1006 
   1007  // Step 6.
   1008  double microsecond = 0;
   1009  if (args.hasDefined(4)) {
   1010    if (!ToIntegerWithTruncation(cx, args[4], "microsecond", &microsecond)) {
   1011      return false;
   1012    }
   1013  }
   1014 
   1015  // Step 7.
   1016  double nanosecond = 0;
   1017  if (args.hasDefined(5)) {
   1018    if (!ToIntegerWithTruncation(cx, args[5], "nanosecond", &nanosecond)) {
   1019      return false;
   1020    }
   1021  }
   1022 
   1023  // Steps 8-9.
   1024  Time time;
   1025  if (!RegulateTime(cx,
   1026                    {
   1027                        hour,
   1028                        minute,
   1029                        second,
   1030                        millisecond,
   1031                        microsecond,
   1032                        nanosecond,
   1033                    },
   1034                    TemporalOverflow::Reject, &time)) {
   1035    return false;
   1036  }
   1037 
   1038  // Step 10.
   1039  auto* temporalTime = CreateTemporalTime(cx, args, time);
   1040  if (!temporalTime) {
   1041    return false;
   1042  }
   1043 
   1044  args.rval().setObject(*temporalTime);
   1045  return true;
   1046 }
   1047 
   1048 /**
   1049 * Temporal.PlainTime.from ( item [ , options ] )
   1050 */
   1051 static bool PlainTime_from(JSContext* cx, unsigned argc, Value* vp) {
   1052  CallArgs args = CallArgsFromVp(argc, vp);
   1053 
   1054  // Step 1.
   1055  Time result;
   1056  if (!ToTemporalTime(cx, args.get(0), args.get(1), &result)) {
   1057    return false;
   1058  }
   1059  MOZ_ASSERT(IsValidTime(result));
   1060 
   1061  auto* obj = temporal::CreateTemporalTime(cx, result);
   1062  if (!obj) {
   1063    return false;
   1064  }
   1065 
   1066  args.rval().setObject(*obj);
   1067  return true;
   1068 }
   1069 
   1070 /**
   1071 * Temporal.PlainTime.compare ( one, two )
   1072 */
   1073 static bool PlainTime_compare(JSContext* cx, unsigned argc, Value* vp) {
   1074  CallArgs args = CallArgsFromVp(argc, vp);
   1075 
   1076  // Step 1.
   1077  Time one;
   1078  if (!ToTemporalTime(cx, args.get(0), &one)) {
   1079    return false;
   1080  }
   1081 
   1082  // Step 2.
   1083  Time two;
   1084  if (!ToTemporalTime(cx, args.get(1), &two)) {
   1085    return false;
   1086  }
   1087 
   1088  // Step 3.
   1089  args.rval().setInt32(CompareTimeRecord(one, two));
   1090  return true;
   1091 }
   1092 
   1093 /**
   1094 * get Temporal.PlainTime.prototype.hour
   1095 */
   1096 static bool PlainTime_hour(JSContext* cx, const CallArgs& args) {
   1097  // Step 3.
   1098  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
   1099  args.rval().setInt32(temporalTime->time().hour);
   1100  return true;
   1101 }
   1102 
   1103 /**
   1104 * get Temporal.PlainTime.prototype.hour
   1105 */
   1106 static bool PlainTime_hour(JSContext* cx, unsigned argc, Value* vp) {
   1107  // Steps 1-2.
   1108  CallArgs args = CallArgsFromVp(argc, vp);
   1109  return CallNonGenericMethod<IsPlainTime, PlainTime_hour>(cx, args);
   1110 }
   1111 
   1112 /**
   1113 * get Temporal.PlainTime.prototype.minute
   1114 */
   1115 static bool PlainTime_minute(JSContext* cx, const CallArgs& args) {
   1116  // Step 3.
   1117  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
   1118  args.rval().setInt32(temporalTime->time().minute);
   1119  return true;
   1120 }
   1121 
   1122 /**
   1123 * get Temporal.PlainTime.prototype.minute
   1124 */
   1125 static bool PlainTime_minute(JSContext* cx, unsigned argc, Value* vp) {
   1126  // Steps 1-2.
   1127  CallArgs args = CallArgsFromVp(argc, vp);
   1128  return CallNonGenericMethod<IsPlainTime, PlainTime_minute>(cx, args);
   1129 }
   1130 
   1131 /**
   1132 * get Temporal.PlainTime.prototype.second
   1133 */
   1134 static bool PlainTime_second(JSContext* cx, const CallArgs& args) {
   1135  // Step 3.
   1136  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
   1137  args.rval().setInt32(temporalTime->time().second);
   1138  return true;
   1139 }
   1140 
   1141 /**
   1142 * get Temporal.PlainTime.prototype.second
   1143 */
   1144 static bool PlainTime_second(JSContext* cx, unsigned argc, Value* vp) {
   1145  // Steps 1-2.
   1146  CallArgs args = CallArgsFromVp(argc, vp);
   1147  return CallNonGenericMethod<IsPlainTime, PlainTime_second>(cx, args);
   1148 }
   1149 
   1150 /**
   1151 * get Temporal.PlainTime.prototype.millisecond
   1152 */
   1153 static bool PlainTime_millisecond(JSContext* cx, const CallArgs& args) {
   1154  // Step 3.
   1155  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
   1156  args.rval().setInt32(temporalTime->time().millisecond);
   1157  return true;
   1158 }
   1159 
   1160 /**
   1161 * get Temporal.PlainTime.prototype.millisecond
   1162 */
   1163 static bool PlainTime_millisecond(JSContext* cx, unsigned argc, Value* vp) {
   1164  // Steps 1-2.
   1165  CallArgs args = CallArgsFromVp(argc, vp);
   1166  return CallNonGenericMethod<IsPlainTime, PlainTime_millisecond>(cx, args);
   1167 }
   1168 
   1169 /**
   1170 * get Temporal.PlainTime.prototype.microsecond
   1171 */
   1172 static bool PlainTime_microsecond(JSContext* cx, const CallArgs& args) {
   1173  // Step 3.
   1174  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
   1175  args.rval().setInt32(temporalTime->time().microsecond);
   1176  return true;
   1177 }
   1178 
   1179 /**
   1180 * get Temporal.PlainTime.prototype.microsecond
   1181 */
   1182 static bool PlainTime_microsecond(JSContext* cx, unsigned argc, Value* vp) {
   1183  // Steps 1-2.
   1184  CallArgs args = CallArgsFromVp(argc, vp);
   1185  return CallNonGenericMethod<IsPlainTime, PlainTime_microsecond>(cx, args);
   1186 }
   1187 
   1188 /**
   1189 * get Temporal.PlainTime.prototype.nanosecond
   1190 */
   1191 static bool PlainTime_nanosecond(JSContext* cx, const CallArgs& args) {
   1192  // Step 3.
   1193  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
   1194  args.rval().setInt32(temporalTime->time().nanosecond);
   1195  return true;
   1196 }
   1197 
   1198 /**
   1199 * get Temporal.PlainTime.prototype.nanosecond
   1200 */
   1201 static bool PlainTime_nanosecond(JSContext* cx, unsigned argc, Value* vp) {
   1202  // Steps 1-2.
   1203  CallArgs args = CallArgsFromVp(argc, vp);
   1204  return CallNonGenericMethod<IsPlainTime, PlainTime_nanosecond>(cx, args);
   1205 }
   1206 
   1207 /**
   1208 * Temporal.PlainTime.prototype.add ( temporalDurationLike )
   1209 */
   1210 static bool PlainTime_add(JSContext* cx, const CallArgs& args) {
   1211  // Step 3.
   1212  return AddDurationToTime(cx, TemporalAddDuration::Add, args);
   1213 }
   1214 
   1215 /**
   1216 * Temporal.PlainTime.prototype.add ( temporalDurationLike )
   1217 */
   1218 static bool PlainTime_add(JSContext* cx, unsigned argc, Value* vp) {
   1219  // Steps 1-2.
   1220  CallArgs args = CallArgsFromVp(argc, vp);
   1221  return CallNonGenericMethod<IsPlainTime, PlainTime_add>(cx, args);
   1222 }
   1223 
   1224 /**
   1225 * Temporal.PlainTime.prototype.subtract ( temporalDurationLike )
   1226 */
   1227 static bool PlainTime_subtract(JSContext* cx, const CallArgs& args) {
   1228  // Step 3.
   1229  return AddDurationToTime(cx, TemporalAddDuration::Subtract, args);
   1230 }
   1231 
   1232 /**
   1233 * Temporal.PlainTime.prototype.subtract ( temporalDurationLike )
   1234 */
   1235 static bool PlainTime_subtract(JSContext* cx, unsigned argc, Value* vp) {
   1236  // Steps 1-2.
   1237  CallArgs args = CallArgsFromVp(argc, vp);
   1238  return CallNonGenericMethod<IsPlainTime, PlainTime_subtract>(cx, args);
   1239 }
   1240 
   1241 /**
   1242 * Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] )
   1243 */
   1244 static bool PlainTime_with(JSContext* cx, const CallArgs& args) {
   1245  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
   1246  auto time = temporalTime->time();
   1247 
   1248  // Step 3.
   1249  Rooted<JSObject*> temporalTimeLike(
   1250      cx, RequireObjectArg(cx, "temporalTimeLike", "with", args.get(0)));
   1251  if (!temporalTimeLike) {
   1252    return false;
   1253  }
   1254  if (!ThrowIfTemporalLikeObject(cx, temporalTimeLike)) {
   1255    return false;
   1256  }
   1257 
   1258  // Steps 4-16.
   1259  TemporalTimeLike partialTime = {
   1260      double(time.hour),        double(time.minute),
   1261      double(time.second),      double(time.millisecond),
   1262      double(time.microsecond), double(time.nanosecond),
   1263  };
   1264  if (!::ToTemporalTimeRecord(cx, temporalTimeLike, &partialTime)) {
   1265    return false;
   1266  }
   1267 
   1268  // Steps 17-18
   1269  auto overflow = TemporalOverflow::Constrain;
   1270  if (args.hasDefined(1)) {
   1271    // Step 17.
   1272    Rooted<JSObject*> options(cx,
   1273                              RequireObjectArg(cx, "options", "with", args[1]));
   1274    if (!options) {
   1275      return false;
   1276    }
   1277 
   1278    // Step 18.
   1279    if (!GetTemporalOverflowOption(cx, options, &overflow)) {
   1280      return false;
   1281    }
   1282  }
   1283 
   1284  // Step 19.
   1285  Time result;
   1286  if (!RegulateTime(cx, partialTime, overflow, &result)) {
   1287    return false;
   1288  }
   1289 
   1290  // Step 20.
   1291  auto* obj = CreateTemporalTime(cx, result);
   1292  if (!obj) {
   1293    return false;
   1294  }
   1295 
   1296  args.rval().setObject(*obj);
   1297  return true;
   1298 }
   1299 
   1300 /**
   1301 * Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] )
   1302 */
   1303 static bool PlainTime_with(JSContext* cx, unsigned argc, Value* vp) {
   1304  // Steps 1-2.
   1305  CallArgs args = CallArgsFromVp(argc, vp);
   1306  return CallNonGenericMethod<IsPlainTime, PlainTime_with>(cx, args);
   1307 }
   1308 
   1309 /**
   1310 * Temporal.PlainTime.prototype.until ( other [ , options ] )
   1311 */
   1312 static bool PlainTime_until(JSContext* cx, const CallArgs& args) {
   1313  // Step 3.
   1314  return DifferenceTemporalPlainTime(cx, TemporalDifference::Until, args);
   1315 }
   1316 
   1317 /**
   1318 * Temporal.PlainTime.prototype.until ( other [ , options ] )
   1319 */
   1320 static bool PlainTime_until(JSContext* cx, unsigned argc, Value* vp) {
   1321  // Steps 1-2.
   1322  CallArgs args = CallArgsFromVp(argc, vp);
   1323  return CallNonGenericMethod<IsPlainTime, PlainTime_until>(cx, args);
   1324 }
   1325 
   1326 /**
   1327 * Temporal.PlainTime.prototype.since ( other [ , options ] )
   1328 */
   1329 static bool PlainTime_since(JSContext* cx, const CallArgs& args) {
   1330  // Step 3.
   1331  return DifferenceTemporalPlainTime(cx, TemporalDifference::Since, args);
   1332 }
   1333 
   1334 /**
   1335 * Temporal.PlainTime.prototype.since ( other [ , options ] )
   1336 */
   1337 static bool PlainTime_since(JSContext* cx, unsigned argc, Value* vp) {
   1338  // Steps 1-2.
   1339  CallArgs args = CallArgsFromVp(argc, vp);
   1340  return CallNonGenericMethod<IsPlainTime, PlainTime_since>(cx, args);
   1341 }
   1342 
   1343 /**
   1344 * Temporal.PlainTime.prototype.round ( roundTo )
   1345 */
   1346 static bool PlainTime_round(JSContext* cx, const CallArgs& args) {
   1347  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
   1348  auto time = temporalTime->time();
   1349 
   1350  // Steps 3-13.
   1351  auto smallestUnit = TemporalUnit::Unset;
   1352  auto roundingMode = TemporalRoundingMode::HalfExpand;
   1353  auto roundingIncrement = Increment{1};
   1354  if (args.get(0).isString()) {
   1355    // Step 4. (Not applicable in our implementation.)
   1356 
   1357    // Step 9.
   1358    Rooted<JSString*> paramString(cx, args[0].toString());
   1359    if (!GetTemporalUnitValuedOption(
   1360            cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) {
   1361      return false;
   1362    }
   1363 
   1364    // Step 10.
   1365    if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
   1366                                   smallestUnit, TemporalUnitGroup::Time)) {
   1367      return false;
   1368    }
   1369 
   1370    // Steps 6-8 and 11-13. (Implicit)
   1371  } else {
   1372    // Steps 3 and 5.
   1373    Rooted<JSObject*> options(
   1374        cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
   1375    if (!options) {
   1376      return false;
   1377    }
   1378 
   1379    // Steps 6-7.
   1380    if (!GetRoundingIncrementOption(cx, options, &roundingIncrement)) {
   1381      return false;
   1382    }
   1383 
   1384    // Step 8.
   1385    if (!GetRoundingModeOption(cx, options, &roundingMode)) {
   1386      return false;
   1387    }
   1388 
   1389    // Step 9.
   1390    if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
   1391                                     &smallestUnit)) {
   1392      return false;
   1393    }
   1394 
   1395    if (smallestUnit == TemporalUnit::Unset) {
   1396      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1397                                JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
   1398      return false;
   1399    }
   1400 
   1401    // Step 10.
   1402    if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
   1403                                   smallestUnit, TemporalUnitGroup::Time)) {
   1404      return false;
   1405    }
   1406 
   1407    // Steps 11-12.
   1408    auto maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit);
   1409 
   1410    // Step 13.
   1411    if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
   1412                                           false)) {
   1413      return false;
   1414    }
   1415  }
   1416 
   1417  // Step 14.
   1418  auto result = RoundTime(time, roundingIncrement, smallestUnit, roundingMode);
   1419 
   1420  // Step 15.
   1421  auto* obj = CreateTemporalTime(cx, result.time);
   1422  if (!obj) {
   1423    return false;
   1424  }
   1425 
   1426  args.rval().setObject(*obj);
   1427  return true;
   1428 }
   1429 
   1430 /**
   1431 * Temporal.PlainTime.prototype.round ( roundTo )
   1432 */
   1433 static bool PlainTime_round(JSContext* cx, unsigned argc, Value* vp) {
   1434  // Steps 1-2.
   1435  CallArgs args = CallArgsFromVp(argc, vp);
   1436  return CallNonGenericMethod<IsPlainTime, PlainTime_round>(cx, args);
   1437 }
   1438 
   1439 /**
   1440 * Temporal.PlainTime.prototype.equals ( other )
   1441 */
   1442 static bool PlainTime_equals(JSContext* cx, const CallArgs& args) {
   1443  auto temporalTime = args.thisv().toObject().as<PlainTimeObject>().time();
   1444 
   1445  // Step 3.
   1446  Time other;
   1447  if (!ToTemporalTime(cx, args.get(0), &other)) {
   1448    return false;
   1449  }
   1450 
   1451  // Steps 4-5.
   1452  args.rval().setBoolean(temporalTime == other);
   1453  return true;
   1454 }
   1455 
   1456 /**
   1457 * Temporal.PlainTime.prototype.equals ( other )
   1458 */
   1459 static bool PlainTime_equals(JSContext* cx, unsigned argc, Value* vp) {
   1460  // Steps 1-2.
   1461  CallArgs args = CallArgsFromVp(argc, vp);
   1462  return CallNonGenericMethod<IsPlainTime, PlainTime_equals>(cx, args);
   1463 }
   1464 
   1465 /**
   1466 * Temporal.PlainTime.prototype.toString ( [ options ] )
   1467 */
   1468 static bool PlainTime_toString(JSContext* cx, const CallArgs& args) {
   1469  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
   1470  auto time = temporalTime->time();
   1471 
   1472  SecondsStringPrecision precision = {Precision::Auto(),
   1473                                      TemporalUnit::Nanosecond, Increment{1}};
   1474  auto roundingMode = TemporalRoundingMode::Trunc;
   1475  if (args.hasDefined(0)) {
   1476    // Step 3.
   1477    Rooted<JSObject*> options(
   1478        cx, RequireObjectArg(cx, "options", "toString", args[0]));
   1479    if (!options) {
   1480      return false;
   1481    }
   1482 
   1483    // Steps 4-5.
   1484    auto digits = Precision::Auto();
   1485    if (!GetTemporalFractionalSecondDigitsOption(cx, options, &digits)) {
   1486      return false;
   1487    }
   1488 
   1489    // Step 6.
   1490    if (!GetRoundingModeOption(cx, options, &roundingMode)) {
   1491      return false;
   1492    }
   1493 
   1494    // Step 7.
   1495    auto smallestUnit = TemporalUnit::Unset;
   1496    if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
   1497                                     &smallestUnit)) {
   1498      return false;
   1499    }
   1500 
   1501    // Step 8.
   1502    if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
   1503                                   smallestUnit, TemporalUnitGroup::Time)) {
   1504      return false;
   1505    }
   1506 
   1507    // Step 9.
   1508    if (smallestUnit == TemporalUnit::Hour) {
   1509      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1510                                JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
   1511                                "smallestUnit");
   1512      return false;
   1513    }
   1514 
   1515    // Step 10.
   1516    precision = ToSecondsStringPrecision(smallestUnit, digits);
   1517  }
   1518 
   1519  // Step 11.
   1520  auto roundedTime =
   1521      RoundTime(time, precision.increment, precision.unit, roundingMode);
   1522 
   1523  // Step 12.
   1524  JSString* str = TimeRecordToString(cx, roundedTime.time, precision.precision);
   1525  if (!str) {
   1526    return false;
   1527  }
   1528 
   1529  args.rval().setString(str);
   1530  return true;
   1531 }
   1532 
   1533 /**
   1534 * Temporal.PlainTime.prototype.toString ( [ options ] )
   1535 */
   1536 static bool PlainTime_toString(JSContext* cx, unsigned argc, Value* vp) {
   1537  // Steps 1-2.
   1538  CallArgs args = CallArgsFromVp(argc, vp);
   1539  return CallNonGenericMethod<IsPlainTime, PlainTime_toString>(cx, args);
   1540 }
   1541 
   1542 /**
   1543 * Temporal.PlainTime.prototype.toLocaleString ( [ locales [ , options ] ] )
   1544 */
   1545 static bool PlainTime_toLocaleString(JSContext* cx, const CallArgs& args) {
   1546  // Steps 3-4.
   1547  return intl::TemporalObjectToLocaleString(cx, args,
   1548                                            intl::DateTimeFormatKind::Time);
   1549 }
   1550 
   1551 /**
   1552 * Temporal.PlainTime.prototype.toLocaleString ( [ locales [ , options ] ] )
   1553 */
   1554 static bool PlainTime_toLocaleString(JSContext* cx, unsigned argc, Value* vp) {
   1555  // Steps 1-2.
   1556  CallArgs args = CallArgsFromVp(argc, vp);
   1557  return CallNonGenericMethod<IsPlainTime, PlainTime_toLocaleString>(cx, args);
   1558 }
   1559 
   1560 /**
   1561 * Temporal.PlainTime.prototype.toJSON ( )
   1562 */
   1563 static bool PlainTime_toJSON(JSContext* cx, const CallArgs& args) {
   1564  auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>();
   1565  auto time = temporalTime->time();
   1566 
   1567  // Step 3.
   1568  JSString* str = TimeRecordToString(cx, time, Precision::Auto());
   1569  if (!str) {
   1570    return false;
   1571  }
   1572 
   1573  args.rval().setString(str);
   1574  return true;
   1575 }
   1576 
   1577 /**
   1578 * Temporal.PlainTime.prototype.toJSON ( )
   1579 */
   1580 static bool PlainTime_toJSON(JSContext* cx, unsigned argc, Value* vp) {
   1581  // Steps 1-2.
   1582  CallArgs args = CallArgsFromVp(argc, vp);
   1583  return CallNonGenericMethod<IsPlainTime, PlainTime_toJSON>(cx, args);
   1584 }
   1585 
   1586 /**
   1587 * Temporal.PlainTime.prototype.valueOf ( )
   1588 */
   1589 static bool PlainTime_valueOf(JSContext* cx, unsigned argc, Value* vp) {
   1590  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
   1591                            "PlainTime", "primitive type");
   1592  return false;
   1593 }
   1594 
   1595 const JSClass PlainTimeObject::class_ = {
   1596    "Temporal.PlainTime",
   1597    JSCLASS_HAS_RESERVED_SLOTS(PlainTimeObject::SLOT_COUNT) |
   1598        JSCLASS_HAS_CACHED_PROTO(JSProto_PlainTime),
   1599    JS_NULL_CLASS_OPS,
   1600    &PlainTimeObject::classSpec_,
   1601 };
   1602 
   1603 const JSClass& PlainTimeObject::protoClass_ = PlainObject::class_;
   1604 
   1605 static const JSFunctionSpec PlainTime_methods[] = {
   1606    JS_FN("from", PlainTime_from, 1, 0),
   1607    JS_FN("compare", PlainTime_compare, 2, 0),
   1608    JS_FS_END,
   1609 };
   1610 
   1611 static const JSFunctionSpec PlainTime_prototype_methods[] = {
   1612    JS_FN("add", PlainTime_add, 1, 0),
   1613    JS_FN("subtract", PlainTime_subtract, 1, 0),
   1614    JS_FN("with", PlainTime_with, 1, 0),
   1615    JS_FN("until", PlainTime_until, 1, 0),
   1616    JS_FN("since", PlainTime_since, 1, 0),
   1617    JS_FN("round", PlainTime_round, 1, 0),
   1618    JS_FN("equals", PlainTime_equals, 1, 0),
   1619    JS_FN("toString", PlainTime_toString, 0, 0),
   1620    JS_FN("toLocaleString", PlainTime_toLocaleString, 0, 0),
   1621    JS_FN("toJSON", PlainTime_toJSON, 0, 0),
   1622    JS_FN("valueOf", PlainTime_valueOf, 0, 0),
   1623    JS_FS_END,
   1624 };
   1625 
   1626 static const JSPropertySpec PlainTime_prototype_properties[] = {
   1627    JS_PSG("hour", PlainTime_hour, 0),
   1628    JS_PSG("minute", PlainTime_minute, 0),
   1629    JS_PSG("second", PlainTime_second, 0),
   1630    JS_PSG("millisecond", PlainTime_millisecond, 0),
   1631    JS_PSG("microsecond", PlainTime_microsecond, 0),
   1632    JS_PSG("nanosecond", PlainTime_nanosecond, 0),
   1633    JS_STRING_SYM_PS(toStringTag, "Temporal.PlainTime", JSPROP_READONLY),
   1634    JS_PS_END,
   1635 };
   1636 
   1637 const ClassSpec PlainTimeObject::classSpec_ = {
   1638    GenericCreateConstructor<PlainTimeConstructor, 0, gc::AllocKind::FUNCTION>,
   1639    GenericCreatePrototype<PlainTimeObject>,
   1640    PlainTime_methods,
   1641    nullptr,
   1642    PlainTime_prototype_methods,
   1643    PlainTime_prototype_properties,
   1644    nullptr,
   1645    ClassSpec::DontDefineConstructor,
   1646 };