tor-browser

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

Instant.cpp (37438B)


      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/Instant.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/Casting.h"
     11 
     12 #include <algorithm>
     13 #include <array>
     14 #include <cstdlib>
     15 #include <stddef.h>
     16 #include <stdint.h>
     17 
     18 #include "jsnum.h"
     19 #include "jspubtd.h"
     20 #include "NamespaceImports.h"
     21 
     22 #include "builtin/intl/DateTimeFormat.h"
     23 #include "builtin/temporal/Calendar.h"
     24 #include "builtin/temporal/Duration.h"
     25 #include "builtin/temporal/Int96.h"
     26 #include "builtin/temporal/PlainDateTime.h"
     27 #include "builtin/temporal/Temporal.h"
     28 #include "builtin/temporal/TemporalParser.h"
     29 #include "builtin/temporal/TemporalRoundingMode.h"
     30 #include "builtin/temporal/TemporalTypes.h"
     31 #include "builtin/temporal/TemporalUnit.h"
     32 #include "builtin/temporal/TimeZone.h"
     33 #include "builtin/temporal/ToString.h"
     34 #include "builtin/temporal/ZonedDateTime.h"
     35 #include "gc/AllocKind.h"
     36 #include "gc/Barrier.h"
     37 #include "js/CallArgs.h"
     38 #include "js/CallNonGenericMethod.h"
     39 #include "js/Class.h"
     40 #include "js/Conversions.h"
     41 #include "js/ErrorReport.h"
     42 #include "js/friend/ErrorMessages.h"
     43 #include "js/PropertyDescriptor.h"
     44 #include "js/PropertySpec.h"
     45 #include "js/RootingAPI.h"
     46 #include "js/TypeDecls.h"
     47 #include "js/Value.h"
     48 #include "vm/BigIntType.h"
     49 #include "vm/BytecodeUtil.h"
     50 #include "vm/GlobalObject.h"
     51 #include "vm/Int128.h"
     52 #include "vm/JSAtomState.h"
     53 #include "vm/JSContext.h"
     54 #include "vm/JSObject.h"
     55 #include "vm/PlainObject.h"
     56 
     57 #include "vm/JSObject-inl.h"
     58 #include "vm/NativeObject-inl.h"
     59 #include "vm/ObjectOperations-inl.h"
     60 
     61 using namespace js;
     62 using namespace js::temporal;
     63 
     64 static inline bool IsInstant(Handle<Value> v) {
     65  return v.isObject() && v.toObject().is<InstantObject>();
     66 }
     67 
     68 /**
     69 * Check if the absolute value is less-or-equal to the given limit.
     70 */
     71 template <const auto& digits>
     72 static bool AbsoluteValueIsLessOrEqual(const BigInt* bigInt) {
     73  size_t length = bigInt->digitLength();
     74 
     75  // Fewer digits than the limit, so definitely in range.
     76  if (length < std::size(digits)) {
     77    return true;
     78  }
     79 
     80  // More digits than the limit, so definitely out of range.
     81  if (length > std::size(digits)) {
     82    return false;
     83  }
     84 
     85  // Compare each digit when the input has the same number of digits.
     86  size_t index = std::size(digits);
     87  for (auto digit : digits) {
     88    auto d = bigInt->digit(--index);
     89    if (d < digit) {
     90      return true;
     91    }
     92    if (d > digit) {
     93      return false;
     94    }
     95  }
     96  return true;
     97 }
     98 
     99 static constexpr auto NanosecondsMaxInstant() {
    100  static_assert(BigInt::DigitBits == 64 || BigInt::DigitBits == 32);
    101 
    102  // ±8.64 × 10^21 is the nanoseconds from epoch limit.
    103  // 8.64 × 10^21 is 86_40000_00000_00000_00000 or 0x1d4_60162f51_6f000000.
    104  // Return the BigInt digits of that number for fast BigInt comparisons.
    105  if constexpr (BigInt::DigitBits == 64) {
    106    return std::array{
    107        BigInt::Digit(0x1d4),
    108        BigInt::Digit(0x6016'2f51'6f00'0000),
    109    };
    110  } else {
    111    return std::array{
    112        BigInt::Digit(0x1d4),
    113        BigInt::Digit(0x6016'2f51),
    114        BigInt::Digit(0x6f00'0000),
    115    };
    116  }
    117 }
    118 
    119 // Can't be defined in IsValidEpochNanoseconds when compiling with GCC 8.
    120 static constexpr auto EpochLimitBigIntDigits = NanosecondsMaxInstant();
    121 
    122 /**
    123 * IsValidEpochNanoseconds ( epochNanoseconds )
    124 */
    125 bool js::temporal::IsValidEpochNanoseconds(const BigInt* epochNanoseconds) {
    126  // Steps 1-2.
    127  return AbsoluteValueIsLessOrEqual<EpochLimitBigIntDigits>(epochNanoseconds);
    128 }
    129 
    130 static bool IsValidEpochMilliseconds(double epochMilliseconds) {
    131  MOZ_ASSERT(IsInteger(epochMilliseconds));
    132 
    133  constexpr int64_t MillisecondsMaxInstant =
    134      EpochNanoseconds::max().toMilliseconds();
    135  return std::abs(epochMilliseconds) <= double(MillisecondsMaxInstant);
    136 }
    137 
    138 /**
    139 * IsValidEpochNanoseconds ( epochNanoseconds )
    140 */
    141 bool js::temporal::IsValidEpochNanoseconds(
    142    const EpochNanoseconds& epochNanoseconds) {
    143  MOZ_ASSERT(0 <= epochNanoseconds.nanoseconds &&
    144             epochNanoseconds.nanoseconds <= 999'999'999);
    145 
    146  // Steps 1-2.
    147  return EpochNanoseconds::min() <= epochNanoseconds &&
    148         epochNanoseconds <= EpochNanoseconds::max();
    149 }
    150 
    151 #ifdef DEBUG
    152 /**
    153 * Validates a nanoseconds amount is at most as large as the difference
    154 * between two valid epoch nanoseconds.
    155 */
    156 bool js::temporal::IsValidEpochDuration(const EpochDuration& duration) {
    157  MOZ_ASSERT(0 <= duration.nanoseconds && duration.nanoseconds <= 999'999'999);
    158 
    159  // Steps 1-2.
    160  return EpochDuration::min() <= duration && duration <= EpochDuration::max();
    161 }
    162 #endif
    163 
    164 /**
    165 * Return the BigInt as a 96-bit integer. The BigInt digits must not consist of
    166 * more than 96-bits.
    167 */
    168 static Int96 ToInt96(const BigInt* ns) {
    169  static_assert(BigInt::DigitBits == 64 || BigInt::DigitBits == 32);
    170 
    171  auto digits = ns->digits();
    172  if constexpr (BigInt::DigitBits == 64) {
    173    BigInt::Digit x = 0, y = 0;
    174    switch (digits.size()) {
    175      case 2:
    176        y = digits[1];
    177        [[fallthrough]];
    178      case 1:
    179        x = digits[0];
    180        [[fallthrough]];
    181      case 0:
    182        break;
    183      default:
    184        MOZ_ASSERT_UNREACHABLE("unexpected digit length");
    185    }
    186    return Int96{
    187        Int96::Digits{Int96::Digit(x), Int96::Digit(x >> 32), Int96::Digit(y)},
    188        ns->isNegative()};
    189  } else {
    190    BigInt::Digit x = 0, y = 0, z = 0;
    191    switch (digits.size()) {
    192      case 3:
    193        z = digits[2];
    194        [[fallthrough]];
    195      case 2:
    196        y = digits[1];
    197        [[fallthrough]];
    198      case 1:
    199        x = digits[0];
    200        [[fallthrough]];
    201      case 0:
    202        break;
    203      default:
    204        MOZ_ASSERT_UNREACHABLE("unexpected digit length");
    205    }
    206    return Int96{
    207        Int96::Digits{Int96::Digit(x), Int96::Digit(y), Int96::Digit(z)},
    208        ns->isNegative()};
    209  }
    210 }
    211 
    212 EpochNanoseconds js::temporal::ToEpochNanoseconds(
    213    const BigInt* epochNanoseconds) {
    214  MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
    215 
    216  auto [seconds, nanos] =
    217      ToInt96(epochNanoseconds) / ToNanoseconds(TemporalUnit::Second);
    218  return {{seconds, nanos}};
    219 }
    220 
    221 static BigInt* CreateBigInt(JSContext* cx,
    222                            const std::array<uint32_t, 3>& digits,
    223                            bool negative) {
    224  static_assert(BigInt::DigitBits == 64 || BigInt::DigitBits == 32);
    225 
    226  if constexpr (BigInt::DigitBits == 64) {
    227    uint64_t x = (uint64_t(digits[1]) << 32) | digits[0];
    228    uint64_t y = digits[2];
    229 
    230    size_t length = y ? 2 : x ? 1 : 0;
    231    auto* result = BigInt::createUninitialized(cx, length, negative);
    232    if (!result) {
    233      return nullptr;
    234    }
    235    if (y) {
    236      result->setDigit(1, y);
    237    }
    238    if (x) {
    239      result->setDigit(0, x);
    240    }
    241    return result;
    242  } else {
    243    size_t length = digits[2] ? 3 : digits[1] ? 2 : digits[0] ? 1 : 0;
    244    auto* result = BigInt::createUninitialized(cx, length, negative);
    245    if (!result) {
    246      return nullptr;
    247    }
    248    while (length--) {
    249      result->setDigit(length, digits[length]);
    250    }
    251    return result;
    252  }
    253 }
    254 
    255 static auto ToBigIntDigits(uint64_t seconds, uint32_t nanoseconds) {
    256  // Multiplies two uint32_t values and returns the lower 32-bits. The higher
    257  // 32-bits are stored in |high|.
    258  auto digitMul = [](uint32_t a, uint32_t b, uint32_t* high) {
    259    uint64_t result = static_cast<uint64_t>(a) * static_cast<uint64_t>(b);
    260    *high = result >> 32;
    261    return static_cast<uint32_t>(result);
    262  };
    263 
    264  // Adds two uint32_t values and returns the result. Overflow is added to the
    265  // out-param |carry|.
    266  auto digitAdd = [](uint32_t a, uint32_t b, uint32_t* carry) {
    267    uint32_t result = a + b;
    268    *carry += static_cast<uint32_t>(result < a);
    269    return result;
    270  };
    271 
    272  constexpr uint32_t secToNanos = ToNanoseconds(TemporalUnit::Second);
    273 
    274  // uint32_t digits stored in the same order as BigInt digits, i.e. the least
    275  // significant digit is stored at index zero.
    276  std::array<uint32_t, 2> multiplicand = {uint32_t(seconds),
    277                                          uint32_t(seconds >> 32)};
    278  std::array<uint32_t, 3> accumulator = {nanoseconds, 0, 0};
    279 
    280  // This code follows the implementation of |BigInt::multiplyAccumulate()|.
    281 
    282  uint32_t carry = 0;
    283  {
    284    uint32_t high = 0;
    285    uint32_t low = digitMul(secToNanos, multiplicand[0], &high);
    286 
    287    uint32_t newCarry = 0;
    288    accumulator[0] = digitAdd(accumulator[0], low, &newCarry);
    289    accumulator[1] = digitAdd(high, newCarry, &carry);
    290  }
    291  {
    292    uint32_t high = 0;
    293    uint32_t low = digitMul(secToNanos, multiplicand[1], &high);
    294 
    295    uint32_t newCarry = 0;
    296    accumulator[1] = digitAdd(accumulator[1], low, &carry);
    297    accumulator[2] = digitAdd(high, carry, &newCarry);
    298    MOZ_ASSERT(newCarry == 0);
    299  }
    300 
    301  return accumulator;
    302 }
    303 
    304 BigInt* js::temporal::ToBigInt(JSContext* cx,
    305                               const EpochNanoseconds& epochNanoseconds) {
    306  MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
    307 
    308  auto [seconds, nanoseconds] = epochNanoseconds.abs();
    309  auto digits = ToBigIntDigits(uint64_t(seconds), uint32_t(nanoseconds));
    310  return CreateBigInt(cx, digits, epochNanoseconds.seconds < 0);
    311 }
    312 
    313 /**
    314 * GetUTCEpochNanoseconds ( isoDateTime )
    315 */
    316 EpochNanoseconds js::temporal::GetUTCEpochNanoseconds(
    317    const ISODateTime& isoDateTime) {
    318  MOZ_ASSERT(ISODateTimeWithinLimits(isoDateTime));
    319 
    320  const auto& [date, time] = isoDateTime;
    321 
    322  // Steps 1-4.
    323  int64_t ms = MakeDate(isoDateTime);
    324 
    325  // Propagate the input range to the compiler.
    326  int32_t nanos =
    327      std::clamp(time.microsecond * 1'000 + time.nanosecond, 0, 999'999);
    328 
    329  // Step 5.
    330  //
    331  // The returned epoch nanoseconds value can exceed ±8.64 × 10^21, because it
    332  // includes the local time zone offset.
    333  return EpochNanoseconds::fromMilliseconds(ms) + EpochDuration{{0, nanos}};
    334 }
    335 
    336 /**
    337 * CompareEpochNanoseconds ( epochNanosecondsOne, epochNanosecondsTwo )
    338 */
    339 static int32_t CompareEpochNanoseconds(
    340    const EpochNanoseconds& epochNanosecondsOne,
    341    const EpochNanoseconds& epochNanosecondsTwo) {
    342  // Step 1.
    343  if (epochNanosecondsOne > epochNanosecondsTwo) {
    344    return 1;
    345  }
    346 
    347  // Step 2.
    348  if (epochNanosecondsOne < epochNanosecondsTwo) {
    349    return -1;
    350  }
    351 
    352  // Step 3.
    353  return 0;
    354 }
    355 
    356 /**
    357 * CreateTemporalInstant ( epochNanoseconds [ , newTarget ] )
    358 */
    359 InstantObject* js::temporal::CreateTemporalInstant(
    360    JSContext* cx, const EpochNanoseconds& epochNanoseconds) {
    361  // Step 1.
    362  MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
    363 
    364  // Steps 2-3.
    365  auto* object = NewBuiltinClassInstance<InstantObject>(cx);
    366  if (!object) {
    367    return nullptr;
    368  }
    369 
    370  // Step 4.
    371  object->initFixedSlot(InstantObject::SECONDS_SLOT,
    372                        NumberValue(epochNanoseconds.seconds));
    373  object->initFixedSlot(InstantObject::NANOSECONDS_SLOT,
    374                        Int32Value(epochNanoseconds.nanoseconds));
    375 
    376  // Step 5.
    377  return object;
    378 }
    379 
    380 /**
    381 * CreateTemporalInstant ( epochNanoseconds [ , newTarget ] )
    382 */
    383 static InstantObject* CreateTemporalInstant(JSContext* cx, const CallArgs& args,
    384                                            Handle<BigInt*> epochNanoseconds) {
    385  // Step 1.
    386  MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
    387 
    388  // Steps 2-3.
    389  Rooted<JSObject*> proto(cx);
    390  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Instant, &proto)) {
    391    return nullptr;
    392  }
    393 
    394  auto* object = NewObjectWithClassProto<InstantObject>(cx, proto);
    395  if (!object) {
    396    return nullptr;
    397  }
    398 
    399  // Step 4.
    400  auto epochNs = ToEpochNanoseconds(epochNanoseconds);
    401  object->initFixedSlot(InstantObject::SECONDS_SLOT,
    402                        NumberValue(epochNs.seconds));
    403  object->initFixedSlot(InstantObject::NANOSECONDS_SLOT,
    404                        Int32Value(epochNs.nanoseconds));
    405 
    406  // Step 5.
    407  return object;
    408 }
    409 
    410 /**
    411 * ToTemporalInstant ( item )
    412 */
    413 static bool ToTemporalInstant(JSContext* cx, Handle<Value> item,
    414                              EpochNanoseconds* result) {
    415  // Step 1.
    416  Rooted<Value> primitiveValue(cx, item);
    417  if (item.isObject()) {
    418    JSObject* itemObj = &item.toObject();
    419 
    420    // Step 1.a.
    421    if (auto* instant = itemObj->maybeUnwrapIf<InstantObject>()) {
    422      *result = instant->epochNanoseconds();
    423      return true;
    424    }
    425    if (auto* zonedDateTime = itemObj->maybeUnwrapIf<ZonedDateTimeObject>()) {
    426      *result = zonedDateTime->epochNanoseconds();
    427      return true;
    428    }
    429 
    430    // Steps 1.b-c.
    431    if (!ToPrimitive(cx, JSTYPE_STRING, &primitiveValue)) {
    432      return false;
    433    }
    434  }
    435 
    436  // Step 2.
    437  if (!primitiveValue.isString()) {
    438    // The value is always on the stack, so JSDVG_SEARCH_STACK can be used for
    439    // better error reporting.
    440    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK,
    441                     primitiveValue, nullptr, "not a string");
    442    return false;
    443  }
    444  Rooted<JSString*> string(cx, primitiveValue.toString());
    445 
    446  // Steps 3-7.
    447  ISODateTime dateTime;
    448  int64_t offset;
    449  if (!ParseTemporalInstantString(cx, string, &dateTime, &offset)) {
    450    return false;
    451  }
    452  MOZ_ASSERT(std::abs(offset) < ToNanoseconds(TemporalUnit::Day));
    453 
    454  // Steps 8-9.
    455  //
    456  // Modified to call ISODateTimeWithinLimits instead of BalanceISODateTime.
    457  if (!ISODateTimeWithinLimits(dateTime)) {
    458    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    459                              JSMSG_TEMPORAL_INSTANT_INVALID);
    460    return false;
    461  }
    462 
    463  // Step 10.
    464  auto epochNanoseconds =
    465      GetUTCEpochNanoseconds(dateTime) - EpochDuration::fromNanoseconds(offset);
    466 
    467  // Step 11.
    468  if (!IsValidEpochNanoseconds(epochNanoseconds)) {
    469    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    470                              JSMSG_TEMPORAL_INSTANT_INVALID);
    471    return false;
    472  }
    473 
    474  // Step 12.
    475  *result = epochNanoseconds;
    476  return true;
    477 }
    478 
    479 /**
    480 * AddInstant ( epochNanoseconds, hours, minutes, seconds, milliseconds,
    481 * microseconds, nanoseconds )
    482 */
    483 bool js::temporal::AddInstant(JSContext* cx,
    484                              const EpochNanoseconds& epochNanoseconds,
    485                              const TimeDuration& duration,
    486                              EpochNanoseconds* result) {
    487  MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds));
    488  MOZ_ASSERT(IsValidTimeDuration(duration));
    489 
    490  // Step 1. (Inlined AddTimeDurationToEpochNanoseconds)
    491  auto r = epochNanoseconds + duration.to<EpochDuration>();
    492 
    493  // Step 2.
    494  if (!IsValidEpochNanoseconds(r)) {
    495    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    496                              JSMSG_TEMPORAL_INSTANT_INVALID);
    497    return false;
    498  }
    499 
    500  // Step 3.
    501  *result = r;
    502  return true;
    503 }
    504 
    505 /**
    506 * DifferenceInstant ( ns1, ns2, roundingIncrement, smallestUnit, roundingMode )
    507 */
    508 TimeDuration js::temporal::DifferenceInstant(
    509    const EpochNanoseconds& ns1, const EpochNanoseconds& ns2,
    510    Increment roundingIncrement, TemporalUnit smallestUnit,
    511    TemporalRoundingMode roundingMode) {
    512  MOZ_ASSERT(IsValidEpochNanoseconds(ns1));
    513  MOZ_ASSERT(IsValidEpochNanoseconds(ns2));
    514  MOZ_ASSERT(smallestUnit > TemporalUnit::Day);
    515  MOZ_ASSERT(roundingIncrement <=
    516             MaximumTemporalDurationRoundingIncrement(smallestUnit));
    517 
    518  // Step 1.
    519  auto diff = TimeDurationFromEpochNanosecondsDifference(ns2, ns1);
    520  MOZ_ASSERT(IsValidEpochDuration(diff.to<EpochDuration>()));
    521 
    522  // Steps 2-3.
    523  return RoundTimeDuration(diff, roundingIncrement, smallestUnit, roundingMode);
    524 }
    525 
    526 /**
    527 * RoundNumberToIncrementAsIfPositive ( x, increment, roundingMode )
    528 */
    529 static EpochNanoseconds RoundNumberToIncrementAsIfPositive(
    530    const EpochNanoseconds& x, int64_t increment,
    531    TemporalRoundingMode roundingMode) {
    532  MOZ_ASSERT(IsValidEpochNanoseconds(x));
    533  MOZ_ASSERT(increment > 0);
    534  MOZ_ASSERT(increment <= ToNanoseconds(TemporalUnit::Day));
    535 
    536  // This operation is equivalent to adjusting the rounding mode through
    537  // |ToPositiveRoundingMode| and then calling |RoundNumberToIncrement|.
    538  auto rounded = RoundNumberToIncrement(x.toNanoseconds(), Int128{increment},
    539                                        ToPositiveRoundingMode(roundingMode));
    540  return EpochNanoseconds::fromNanoseconds(rounded);
    541 }
    542 
    543 /**
    544 * RoundTemporalInstant ( ns, increment, unit, roundingMode )
    545 */
    546 EpochNanoseconds js::temporal::RoundTemporalInstant(
    547    const EpochNanoseconds& ns, Increment increment, TemporalUnit unit,
    548    TemporalRoundingMode roundingMode) {
    549  MOZ_ASSERT(IsValidEpochNanoseconds(ns));
    550  MOZ_ASSERT(increment >= Increment::min());
    551  MOZ_ASSERT(uint64_t(increment.value()) <= ToNanoseconds(TemporalUnit::Day));
    552  MOZ_ASSERT(unit > TemporalUnit::Day);
    553 
    554  // Step 1.
    555  int64_t unitLength = ToNanoseconds(unit);
    556 
    557  // Step 2.
    558  int64_t incrementNs = increment.value() * unitLength;
    559  MOZ_ASSERT(incrementNs <= ToNanoseconds(TemporalUnit::Day),
    560             "incrementNs doesn't overflow epoch nanoseconds resolution");
    561 
    562  // Step 3.
    563  return RoundNumberToIncrementAsIfPositive(ns, incrementNs, roundingMode);
    564 }
    565 
    566 /**
    567 * DifferenceTemporalInstant ( operation, instant, other, options )
    568 */
    569 static bool DifferenceTemporalInstant(JSContext* cx,
    570                                      TemporalDifference operation,
    571                                      const CallArgs& args) {
    572  auto epochNs = args.thisv().toObject().as<InstantObject>().epochNanoseconds();
    573 
    574  // Step 1.
    575  EpochNanoseconds other;
    576  if (!ToTemporalInstant(cx, args.get(0), &other)) {
    577    return false;
    578  }
    579 
    580  // Steps 2-3.
    581  DifferenceSettings settings;
    582  if (args.hasDefined(1)) {
    583    // Step 2.
    584    Rooted<JSObject*> options(
    585        cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
    586    if (!options) {
    587      return false;
    588    }
    589 
    590    // Step 3.
    591    if (!GetDifferenceSettings(cx, operation, options, TemporalUnitGroup::Time,
    592                               TemporalUnit::Nanosecond, TemporalUnit::Second,
    593                               &settings)) {
    594      return false;
    595    }
    596  } else {
    597    // Steps 2-3.
    598    settings = {
    599        TemporalUnit::Nanosecond,
    600        TemporalUnit::Second,
    601        TemporalRoundingMode::Trunc,
    602        Increment{1},
    603    };
    604  }
    605 
    606  // Steps 4.
    607  auto timeDuration =
    608      DifferenceInstant(epochNs, other, settings.roundingIncrement,
    609                        settings.smallestUnit, settings.roundingMode);
    610 
    611  // Step 5.
    612  Duration duration;
    613  if (!TemporalDurationFromInternal(cx, timeDuration, settings.largestUnit,
    614                                    &duration)) {
    615    return false;
    616  }
    617 
    618  // Step 6.
    619  if (operation == TemporalDifference::Since) {
    620    duration = duration.negate();
    621  }
    622 
    623  // Step 7.
    624  auto* obj = CreateTemporalDuration(cx, duration);
    625  if (!obj) {
    626    return false;
    627  }
    628 
    629  args.rval().setObject(*obj);
    630  return true;
    631 }
    632 
    633 /**
    634 * AddDurationToInstant ( operation, instant, temporalDurationLike )
    635 */
    636 static bool AddDurationToInstant(JSContext* cx, TemporalAddDuration operation,
    637                                 const CallArgs& args) {
    638  auto* instant = &args.thisv().toObject().as<InstantObject>();
    639  auto epochNanoseconds = instant->epochNanoseconds();
    640 
    641  // Step 1.
    642  Duration duration;
    643  if (!ToTemporalDuration(cx, args.get(0), &duration)) {
    644    return false;
    645  }
    646 
    647  // Step 2.
    648  if (operation == TemporalAddDuration::Subtract) {
    649    duration = duration.negate();
    650  }
    651 
    652  // Steps 3-4. (Inlined DefaultTemporalLargestUnit and TemporalUnitCategory.)
    653  if (duration.years != 0 || duration.months != 0 || duration.weeks != 0 ||
    654      duration.days != 0) {
    655    const char* part = duration.years != 0    ? "years"
    656                       : duration.months != 0 ? "months"
    657                       : duration.weeks != 0  ? "weeks"
    658                                              : "days";
    659    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    660                              JSMSG_TEMPORAL_INSTANT_BAD_DURATION, part);
    661    return false;
    662  }
    663 
    664  // Step 5. (Inlined ToInternalDurationRecordWith24HourDays.)
    665  auto timeDuration = TimeDurationFromComponents(duration);
    666 
    667  // Step 6.
    668  EpochNanoseconds ns;
    669  if (!AddInstant(cx, epochNanoseconds, timeDuration, &ns)) {
    670    return false;
    671  }
    672 
    673  // Step 7.
    674  auto* result = CreateTemporalInstant(cx, ns);
    675  if (!result) {
    676    return false;
    677  }
    678 
    679  args.rval().setObject(*result);
    680  return true;
    681 }
    682 
    683 /**
    684 * Temporal.Instant ( epochNanoseconds )
    685 */
    686 static bool InstantConstructor(JSContext* cx, unsigned argc, Value* vp) {
    687  CallArgs args = CallArgsFromVp(argc, vp);
    688 
    689  // Step 1.
    690  if (!ThrowIfNotConstructing(cx, args, "Temporal.Instant")) {
    691    return false;
    692  }
    693 
    694  // Step 2.
    695  Rooted<BigInt*> epochNanoseconds(cx, js::ToBigInt(cx, args.get(0)));
    696  if (!epochNanoseconds) {
    697    return false;
    698  }
    699 
    700  // Step 3.
    701  if (!IsValidEpochNanoseconds(epochNanoseconds)) {
    702    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    703                              JSMSG_TEMPORAL_INSTANT_INVALID);
    704    return false;
    705  }
    706 
    707  // Step 4.
    708  auto* result = CreateTemporalInstant(cx, args, epochNanoseconds);
    709  if (!result) {
    710    return false;
    711  }
    712 
    713  args.rval().setObject(*result);
    714  return true;
    715 }
    716 
    717 /**
    718 * Temporal.Instant.from ( item )
    719 */
    720 static bool Instant_from(JSContext* cx, unsigned argc, Value* vp) {
    721  CallArgs args = CallArgsFromVp(argc, vp);
    722 
    723  // Step 1.
    724  EpochNanoseconds epochNs;
    725  if (!ToTemporalInstant(cx, args.get(0), &epochNs)) {
    726    return false;
    727  }
    728 
    729  auto* result = CreateTemporalInstant(cx, epochNs);
    730  if (!result) {
    731    return false;
    732  }
    733  args.rval().setObject(*result);
    734  return true;
    735 }
    736 
    737 /**
    738 * Temporal.Instant.fromEpochMilliseconds ( epochMilliseconds )
    739 */
    740 static bool Instant_fromEpochMilliseconds(JSContext* cx, unsigned argc,
    741                                          Value* vp) {
    742  CallArgs args = CallArgsFromVp(argc, vp);
    743 
    744  // Step 1.
    745  double epochMilliseconds;
    746  if (!JS::ToNumber(cx, args.get(0), &epochMilliseconds)) {
    747    return false;
    748  }
    749 
    750  // Step 2.
    751  //
    752  // NumberToBigInt throws a RangeError for non-integral numbers.
    753  if (!IsInteger(epochMilliseconds)) {
    754    ToCStringBuf cbuf;
    755    const char* str = NumberToCString(&cbuf, epochMilliseconds);
    756 
    757    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    758                              JSMSG_TEMPORAL_INSTANT_NONINTEGER, str);
    759    return false;
    760  }
    761 
    762  // Step 3. (Not applicable)
    763 
    764  // Step 4.
    765  if (!IsValidEpochMilliseconds(epochMilliseconds)) {
    766    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    767                              JSMSG_TEMPORAL_INSTANT_INVALID);
    768    return false;
    769  }
    770 
    771  // Step 5.
    772  int64_t milliseconds = mozilla::AssertedCast<int64_t>(epochMilliseconds);
    773  auto* result = CreateTemporalInstant(
    774      cx, EpochNanoseconds::fromMilliseconds(milliseconds));
    775  if (!result) {
    776    return false;
    777  }
    778  args.rval().setObject(*result);
    779  return true;
    780 }
    781 
    782 /**
    783 * Temporal.Instant.fromEpochNanoseconds ( epochNanoseconds )
    784 */
    785 static bool Instant_fromEpochNanoseconds(JSContext* cx, unsigned argc,
    786                                         Value* vp) {
    787  CallArgs args = CallArgsFromVp(argc, vp);
    788 
    789  // Step 1.
    790  Rooted<BigInt*> epochNanoseconds(cx, js::ToBigInt(cx, args.get(0)));
    791  if (!epochNanoseconds) {
    792    return false;
    793  }
    794 
    795  // Step 2.
    796  if (!IsValidEpochNanoseconds(epochNanoseconds)) {
    797    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    798                              JSMSG_TEMPORAL_INSTANT_INVALID);
    799    return false;
    800  }
    801 
    802  // Step 3.
    803  auto* result =
    804      CreateTemporalInstant(cx, ToEpochNanoseconds(epochNanoseconds));
    805  if (!result) {
    806    return false;
    807  }
    808  args.rval().setObject(*result);
    809  return true;
    810 }
    811 
    812 /**
    813 * Temporal.Instant.compare ( one, two )
    814 */
    815 static bool Instant_compare(JSContext* cx, unsigned argc, Value* vp) {
    816  CallArgs args = CallArgsFromVp(argc, vp);
    817 
    818  // Step 1.
    819  EpochNanoseconds one;
    820  if (!ToTemporalInstant(cx, args.get(0), &one)) {
    821    return false;
    822  }
    823 
    824  // Step 2.
    825  EpochNanoseconds two;
    826  if (!ToTemporalInstant(cx, args.get(1), &two)) {
    827    return false;
    828  }
    829 
    830  // Step 3.
    831  args.rval().setInt32(CompareEpochNanoseconds(one, two));
    832  return true;
    833 }
    834 
    835 /**
    836 * get Temporal.Instant.prototype.epochMilliseconds
    837 */
    838 static bool Instant_epochMilliseconds(JSContext* cx, const CallArgs& args) {
    839  // Step 3.
    840  auto epochNs = args.thisv().toObject().as<InstantObject>().epochNanoseconds();
    841 
    842  // Step 4-5.
    843  args.rval().setNumber(epochNs.floorToMilliseconds());
    844  return true;
    845 }
    846 
    847 /**
    848 * get Temporal.Instant.prototype.epochMilliseconds
    849 */
    850 static bool Instant_epochMilliseconds(JSContext* cx, unsigned argc, Value* vp) {
    851  // Steps 1-2.
    852  CallArgs args = CallArgsFromVp(argc, vp);
    853  return CallNonGenericMethod<IsInstant, Instant_epochMilliseconds>(cx, args);
    854 }
    855 
    856 /**
    857 * get Temporal.Instant.prototype.epochNanoseconds
    858 */
    859 static bool Instant_epochNanoseconds(JSContext* cx, const CallArgs& args) {
    860  // Step 3.
    861  auto epochNs = args.thisv().toObject().as<InstantObject>().epochNanoseconds();
    862  auto* nanoseconds = ToBigInt(cx, epochNs);
    863  if (!nanoseconds) {
    864    return false;
    865  }
    866 
    867  args.rval().setBigInt(nanoseconds);
    868  return true;
    869 }
    870 
    871 /**
    872 * get Temporal.Instant.prototype.epochNanoseconds
    873 */
    874 static bool Instant_epochNanoseconds(JSContext* cx, unsigned argc, Value* vp) {
    875  // Steps 1-2.
    876  CallArgs args = CallArgsFromVp(argc, vp);
    877  return CallNonGenericMethod<IsInstant, Instant_epochNanoseconds>(cx, args);
    878 }
    879 
    880 /**
    881 * Temporal.Instant.prototype.add ( temporalDurationLike )
    882 */
    883 static bool Instant_add(JSContext* cx, const CallArgs& args) {
    884  // Step 3.
    885  return AddDurationToInstant(cx, TemporalAddDuration::Add, args);
    886 }
    887 
    888 /**
    889 * Temporal.Instant.prototype.add ( temporalDurationLike )
    890 */
    891 static bool Instant_add(JSContext* cx, unsigned argc, Value* vp) {
    892  // Steps 1-2.
    893  CallArgs args = CallArgsFromVp(argc, vp);
    894  return CallNonGenericMethod<IsInstant, Instant_add>(cx, args);
    895 }
    896 
    897 /**
    898 * Temporal.Instant.prototype.subtract ( temporalDurationLike )
    899 */
    900 static bool Instant_subtract(JSContext* cx, const CallArgs& args) {
    901  // Step 3.
    902  return AddDurationToInstant(cx, TemporalAddDuration::Subtract, args);
    903 }
    904 
    905 /**
    906 * Temporal.Instant.prototype.subtract ( temporalDurationLike )
    907 */
    908 static bool Instant_subtract(JSContext* cx, unsigned argc, Value* vp) {
    909  // Steps 1-2.
    910  CallArgs args = CallArgsFromVp(argc, vp);
    911  return CallNonGenericMethod<IsInstant, Instant_subtract>(cx, args);
    912 }
    913 
    914 /**
    915 * Temporal.Instant.prototype.until ( other [ , options ] )
    916 */
    917 static bool Instant_until(JSContext* cx, const CallArgs& args) {
    918  // Step 3.
    919  return DifferenceTemporalInstant(cx, TemporalDifference::Until, args);
    920 }
    921 
    922 /**
    923 * Temporal.Instant.prototype.until ( other [ , options ] )
    924 */
    925 static bool Instant_until(JSContext* cx, unsigned argc, Value* vp) {
    926  // Steps 1-2.
    927  CallArgs args = CallArgsFromVp(argc, vp);
    928  return CallNonGenericMethod<IsInstant, Instant_until>(cx, args);
    929 }
    930 
    931 /**
    932 * Temporal.Instant.prototype.since ( other [ , options ] )
    933 */
    934 static bool Instant_since(JSContext* cx, const CallArgs& args) {
    935  // Step 3.
    936  return DifferenceTemporalInstant(cx, TemporalDifference::Since, args);
    937 }
    938 
    939 /**
    940 * Temporal.Instant.prototype.since ( other [ , options ] )
    941 */
    942 static bool Instant_since(JSContext* cx, unsigned argc, Value* vp) {
    943  // Steps 1-2.
    944  CallArgs args = CallArgsFromVp(argc, vp);
    945  return CallNonGenericMethod<IsInstant, Instant_since>(cx, args);
    946 }
    947 
    948 /**
    949 * Temporal.Instant.prototype.round ( roundTo )
    950 */
    951 static bool Instant_round(JSContext* cx, const CallArgs& args) {
    952  auto epochNs = args.thisv().toObject().as<InstantObject>().epochNanoseconds();
    953 
    954  // Steps 3-17.
    955  auto smallestUnit = TemporalUnit::Unset;
    956  auto roundingMode = TemporalRoundingMode::HalfExpand;
    957  auto roundingIncrement = Increment{1};
    958  if (args.get(0).isString()) {
    959    // Steps 4 and 6-8. (Not applicable in our implementation.)
    960 
    961    // Step 9.
    962    Rooted<JSString*> paramString(cx, args[0].toString());
    963    if (!GetTemporalUnitValuedOption(
    964            cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) {
    965      return false;
    966    }
    967 
    968    // Step 10.
    969    if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
    970                                   smallestUnit, TemporalUnitGroup::Time)) {
    971      return false;
    972    }
    973 
    974    // Steps 11-17. (Not applicable in our implementation.)
    975  } else {
    976    // Steps 3 and 5.
    977    Rooted<JSObject*> options(
    978        cx, RequireObjectArg(cx, "roundTo", "round", args.get(0)));
    979    if (!options) {
    980      return false;
    981    }
    982 
    983    // Steps 6-7.
    984    if (!GetRoundingIncrementOption(cx, options, &roundingIncrement)) {
    985      return false;
    986    }
    987 
    988    // Step 8.
    989    if (!GetRoundingModeOption(cx, options, &roundingMode)) {
    990      return false;
    991    }
    992 
    993    // Step 9.
    994    if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
    995                                     &smallestUnit)) {
    996      return false;
    997    }
    998 
    999    if (smallestUnit == TemporalUnit::Unset) {
   1000      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1001                                JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit");
   1002      return false;
   1003    }
   1004 
   1005    // Step 10.
   1006    if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
   1007                                   smallestUnit, TemporalUnitGroup::Time)) {
   1008      return false;
   1009    }
   1010 
   1011    // Steps 11-16.
   1012    int64_t maximum = UnitsPerDay(smallestUnit);
   1013 
   1014    // Step 17.
   1015    if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum,
   1016                                           true)) {
   1017      return false;
   1018    }
   1019  }
   1020 
   1021  // Step 18.
   1022  auto roundedNs = RoundTemporalInstant(epochNs, roundingIncrement,
   1023                                        smallestUnit, roundingMode);
   1024 
   1025  // Step 19.
   1026  auto* result = CreateTemporalInstant(cx, roundedNs);
   1027  if (!result) {
   1028    return false;
   1029  }
   1030  args.rval().setObject(*result);
   1031  return true;
   1032 }
   1033 
   1034 /**
   1035 * Temporal.Instant.prototype.round ( options )
   1036 */
   1037 static bool Instant_round(JSContext* cx, unsigned argc, Value* vp) {
   1038  // Steps 1-2.
   1039  CallArgs args = CallArgsFromVp(argc, vp);
   1040  return CallNonGenericMethod<IsInstant, Instant_round>(cx, args);
   1041 }
   1042 
   1043 /**
   1044 * Temporal.Instant.prototype.equals ( other )
   1045 */
   1046 static bool Instant_equals(JSContext* cx, const CallArgs& args) {
   1047  auto epochNs = args.thisv().toObject().as<InstantObject>().epochNanoseconds();
   1048 
   1049  // Step 3.
   1050  EpochNanoseconds other;
   1051  if (!ToTemporalInstant(cx, args.get(0), &other)) {
   1052    return false;
   1053  }
   1054 
   1055  // Steps 4-5.
   1056  args.rval().setBoolean(epochNs == other);
   1057  return true;
   1058 }
   1059 
   1060 /**
   1061 * Temporal.Instant.prototype.equals ( other )
   1062 */
   1063 static bool Instant_equals(JSContext* cx, unsigned argc, Value* vp) {
   1064  // Steps 1-2.
   1065  CallArgs args = CallArgsFromVp(argc, vp);
   1066  return CallNonGenericMethod<IsInstant, Instant_equals>(cx, args);
   1067 }
   1068 
   1069 /**
   1070 * Temporal.Instant.prototype.toString ( [ options ] )
   1071 */
   1072 static bool Instant_toString(JSContext* cx, const CallArgs& args) {
   1073  auto epochNs = args.thisv().toObject().as<InstantObject>().epochNanoseconds();
   1074 
   1075  Rooted<TimeZoneValue> timeZone(cx);
   1076  auto roundingMode = TemporalRoundingMode::Trunc;
   1077  SecondsStringPrecision precision = {Precision::Auto(),
   1078                                      TemporalUnit::Nanosecond, Increment{1}};
   1079  if (args.hasDefined(0)) {
   1080    // Step 3.
   1081    Rooted<JSObject*> options(
   1082        cx, RequireObjectArg(cx, "options", "toString", args[0]));
   1083    if (!options) {
   1084      return false;
   1085    }
   1086 
   1087    // Steps 4-5.
   1088    auto digits = Precision::Auto();
   1089    if (!GetTemporalFractionalSecondDigitsOption(cx, options, &digits)) {
   1090      return false;
   1091    }
   1092 
   1093    // Step 6.
   1094    if (!GetRoundingModeOption(cx, options, &roundingMode)) {
   1095      return false;
   1096    }
   1097 
   1098    // Step 7.
   1099    auto smallestUnit = TemporalUnit::Unset;
   1100    if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit,
   1101                                     &smallestUnit)) {
   1102      return false;
   1103    }
   1104 
   1105    // Step 8.
   1106    Rooted<Value> timeZoneValue(cx);
   1107    if (!GetProperty(cx, options, options, cx->names().timeZone,
   1108                     &timeZoneValue)) {
   1109      return false;
   1110    }
   1111 
   1112    // Step 9.
   1113    if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit,
   1114                                   smallestUnit, TemporalUnitGroup::Time)) {
   1115      return false;
   1116    }
   1117 
   1118    // Step 10.
   1119    if (smallestUnit == TemporalUnit::Hour) {
   1120      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1121                                JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour",
   1122                                "smallestUnit");
   1123      return false;
   1124    }
   1125 
   1126    // Step 11.
   1127    if (!timeZoneValue.isUndefined()) {
   1128      if (!ToTemporalTimeZone(cx, timeZoneValue, &timeZone)) {
   1129        return false;
   1130      }
   1131    }
   1132 
   1133    // Step 12.
   1134    precision = ToSecondsStringPrecision(smallestUnit, digits);
   1135  }
   1136 
   1137  // Steps 13-14.
   1138  auto roundedNs = RoundTemporalInstant(epochNs, precision.increment,
   1139                                        precision.unit, roundingMode);
   1140 
   1141  // Step 15.
   1142  JSString* str =
   1143      TemporalInstantToString(cx, roundedNs, timeZone, precision.precision);
   1144  if (!str) {
   1145    return false;
   1146  }
   1147 
   1148  args.rval().setString(str);
   1149  return true;
   1150 }
   1151 
   1152 /**
   1153 * Temporal.Instant.prototype.toString ( [ options ] )
   1154 */
   1155 static bool Instant_toString(JSContext* cx, unsigned argc, Value* vp) {
   1156  // Steps 1-2.
   1157  CallArgs args = CallArgsFromVp(argc, vp);
   1158  return CallNonGenericMethod<IsInstant, Instant_toString>(cx, args);
   1159 }
   1160 
   1161 /**
   1162 * Temporal.Instant.prototype.toLocaleString ( [ locales [ , options ] ] )
   1163 */
   1164 static bool Instant_toLocaleString(JSContext* cx, const CallArgs& args) {
   1165  // Steps 3-4.
   1166  return intl::TemporalObjectToLocaleString(cx, args,
   1167                                            intl::DateTimeFormatKind::All);
   1168 }
   1169 
   1170 /**
   1171 * Temporal.Instant.prototype.toLocaleString ( [ locales [ , options ] ] )
   1172 */
   1173 static bool Instant_toLocaleString(JSContext* cx, unsigned argc, Value* vp) {
   1174  // Steps 1-2.
   1175  CallArgs args = CallArgsFromVp(argc, vp);
   1176  return CallNonGenericMethod<IsInstant, Instant_toLocaleString>(cx, args);
   1177 }
   1178 
   1179 /**
   1180 * Temporal.Instant.prototype.toJSON ( )
   1181 */
   1182 static bool Instant_toJSON(JSContext* cx, const CallArgs& args) {
   1183  auto epochNs = args.thisv().toObject().as<InstantObject>().epochNanoseconds();
   1184 
   1185  // Step 3.
   1186  Rooted<TimeZoneValue> timeZone(cx);
   1187  JSString* str =
   1188      TemporalInstantToString(cx, epochNs, timeZone, Precision::Auto());
   1189  if (!str) {
   1190    return false;
   1191  }
   1192 
   1193  args.rval().setString(str);
   1194  return true;
   1195 }
   1196 
   1197 /**
   1198 * Temporal.Instant.prototype.toJSON ( )
   1199 */
   1200 static bool Instant_toJSON(JSContext* cx, unsigned argc, Value* vp) {
   1201  // Steps 1-2.
   1202  CallArgs args = CallArgsFromVp(argc, vp);
   1203  return CallNonGenericMethod<IsInstant, Instant_toJSON>(cx, args);
   1204 }
   1205 
   1206 /**
   1207 * Temporal.Instant.prototype.valueOf ( )
   1208 */
   1209 static bool Instant_valueOf(JSContext* cx, unsigned argc, Value* vp) {
   1210  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
   1211                            "Instant", "primitive type");
   1212  return false;
   1213 }
   1214 
   1215 /**
   1216 * Temporal.Instant.prototype.toZonedDateTimeISO ( item )
   1217 */
   1218 static bool Instant_toZonedDateTimeISO(JSContext* cx, const CallArgs& args) {
   1219  auto epochNs = args.thisv().toObject().as<InstantObject>().epochNanoseconds();
   1220 
   1221  // Step 3.
   1222  Rooted<TimeZoneValue> timeZone(cx);
   1223  if (!ToTemporalTimeZone(cx, args.get(0), &timeZone)) {
   1224    return false;
   1225  }
   1226 
   1227  // Step 4.
   1228  Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601));
   1229  auto* result = CreateTemporalZonedDateTime(cx, epochNs, timeZone, calendar);
   1230  if (!result) {
   1231    return false;
   1232  }
   1233 
   1234  args.rval().setObject(*result);
   1235  return true;
   1236 }
   1237 
   1238 /**
   1239 * Temporal.Instant.prototype.toZonedDateTimeISO ( item )
   1240 */
   1241 static bool Instant_toZonedDateTimeISO(JSContext* cx, unsigned argc,
   1242                                       Value* vp) {
   1243  // Steps 1-2.
   1244  CallArgs args = CallArgsFromVp(argc, vp);
   1245  return CallNonGenericMethod<IsInstant, Instant_toZonedDateTimeISO>(cx, args);
   1246 }
   1247 
   1248 const JSClass InstantObject::class_ = {
   1249    "Temporal.Instant",
   1250    JSCLASS_HAS_RESERVED_SLOTS(InstantObject::SLOT_COUNT) |
   1251        JSCLASS_HAS_CACHED_PROTO(JSProto_Instant),
   1252    JS_NULL_CLASS_OPS,
   1253    &InstantObject::classSpec_,
   1254 };
   1255 
   1256 const JSClass& InstantObject::protoClass_ = PlainObject::class_;
   1257 
   1258 static const JSFunctionSpec Instant_methods[] = {
   1259    JS_FN("from", Instant_from, 1, 0),
   1260    JS_FN("fromEpochMilliseconds", Instant_fromEpochMilliseconds, 1, 0),
   1261    JS_FN("fromEpochNanoseconds", Instant_fromEpochNanoseconds, 1, 0),
   1262    JS_FN("compare", Instant_compare, 2, 0),
   1263    JS_FS_END,
   1264 };
   1265 
   1266 static const JSFunctionSpec Instant_prototype_methods[] = {
   1267    JS_FN("add", Instant_add, 1, 0),
   1268    JS_FN("subtract", Instant_subtract, 1, 0),
   1269    JS_FN("until", Instant_until, 1, 0),
   1270    JS_FN("since", Instant_since, 1, 0),
   1271    JS_FN("round", Instant_round, 1, 0),
   1272    JS_FN("equals", Instant_equals, 1, 0),
   1273    JS_FN("toString", Instant_toString, 0, 0),
   1274    JS_FN("toLocaleString", Instant_toLocaleString, 0, 0),
   1275    JS_FN("toJSON", Instant_toJSON, 0, 0),
   1276    JS_FN("valueOf", Instant_valueOf, 0, 0),
   1277    JS_FN("toZonedDateTimeISO", Instant_toZonedDateTimeISO, 1, 0),
   1278    JS_FS_END,
   1279 };
   1280 
   1281 static const JSPropertySpec Instant_prototype_properties[] = {
   1282    JS_PSG("epochMilliseconds", Instant_epochMilliseconds, 0),
   1283    JS_PSG("epochNanoseconds", Instant_epochNanoseconds, 0),
   1284    JS_STRING_SYM_PS(toStringTag, "Temporal.Instant", JSPROP_READONLY),
   1285    JS_PS_END,
   1286 };
   1287 
   1288 const ClassSpec InstantObject::classSpec_ = {
   1289    GenericCreateConstructor<InstantConstructor, 1, gc::AllocKind::FUNCTION>,
   1290    GenericCreatePrototype<InstantObject>,
   1291    Instant_methods,
   1292    nullptr,
   1293    Instant_prototype_methods,
   1294    Instant_prototype_properties,
   1295    nullptr,
   1296    ClassSpec::DontDefineConstructor,
   1297 };