Changeset 282125 in webkit


Ignore:
Timestamp:
Sep 7, 2021 7:20:36 PM (11 months ago)
Author:
ysuzuki@apple.com
Message:

[JSC] Implement Temporal.PlainTime
https://bugs.webkit.org/show_bug.cgi?id=229892

Reviewed by Darin Adler.

JSTests:

  • stress/temporal-calendar.js:
  • stress/temporal-duration.js:
  • stress/temporal-plaintime.js: Added.

(shouldBe):
(shouldThrow):
(shouldBe.String.Temporal.PlainTime.from):
(let.time.Temporal.PlainTime.from.shouldBe):
(let.text.of.failures.shouldThrow):
(print):
(shouldBe.Temporal.PlainTime.from):
(new.Temporal.PlainTime.valueOf):
(shouldBe.String.time.until.Temporal.PlainTime.from):

  • stress/temporal-timezone.js:

(let.text.of.failures.shouldThrow): Deleted.

  • test262/config.yaml:

Source/JavaScriptCore:

This patch implements Temporal.PlainTime[1]. This is time representation which is not associated to
calendars and timezones. This is tuple of hour, minute, second, millisecond, microsecond, and nanosecond.

  1. We add full-fledged ISO8601 DateTime / Time parser, so that Temporal.PlainTime.from can extract time as specified.
  1. ISO8601::PlainTime is used for already-validated PlainTime data. When performing arithmetics, we first do that in ISO8601::Duration, and then we validate and convert it to PlainTime.

We also found several spec issues, and reported in [2,3,4].

[1]: https://tc39.es/proposal-temporal/#sec-temporal-plaintime-objects
[2]: https://github.com/tc39/proposal-temporal/issues/1803
[3]: https://github.com/tc39/proposal-temporal/issues/1804
[4]: https://github.com/tc39/proposal-temporal/issues/1805

  • CMakeLists.txt:
  • DerivedSources-input.xcfilelist:
  • DerivedSources-output.xcfilelist:
  • DerivedSources.make:
  • Sources.txt:
  • runtime/CommonIdentifiers.h:
  • runtime/ISO8601.cpp:

(JSC::ISO8601::parseTimeZoneName):
(JSC::ISO8601::parseDecimalInt32):
(JSC::ISO8601::parseDuration):
(JSC::ISO8601::parseTimeSpec):
(JSC::ISO8601::parseTimeZoneNumericUTCOffset):
(JSC::ISO8601::parseTimeZoneBracketedAnnotation):
(JSC::ISO8601::canBeTimeZone):
(JSC::ISO8601::parseTimeZone):
(JSC::ISO8601::parseTime):
(JSC::ISO8601::daysInMonth):
(JSC::ISO8601::parseDate):
(JSC::ISO8601::parseDateTime):
(JSC::ISO8601::formatTimeZoneOffsetString):
(JSC::ISO8601::temporalTimeToString):
(JSC::ISO8601::isValidDuration):

  • runtime/ISO8601.h:

(JSC::ISO8601::Duration::Duration):
(JSC::ISO8601::Duration::operator[]):
(JSC::ISO8601::Duration::operator[] const):
(JSC::ISO8601::Duration::begin const):
(JSC::ISO8601::Duration::end const):
(JSC::ISO8601::Duration::clear):
(JSC::ISO8601::Duration::operator- const):
(JSC::ISO8601::PlainTime::PlainTime):
(JSC::ISO8601::PlainTime::operator==):
(JSC::ISO8601::PlainDate::PlainDate):
(JSC::ISO8601::PlainDate::year const):
(JSC::ISO8601::PlainDate::month const):
(JSC::ISO8601::PlainDate::day const):

  • runtime/IntlObject.cpp:

(JSC::utcTimeZoneIDSlow):

  • runtime/IntlObject.h:

(JSC::utcTimeZoneID):

  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildrenImpl):

  • runtime/JSGlobalObject.h:

(JSC::JSGlobalObject::plainTimeStructure):

  • runtime/TemporalCalendarConstructor.cpp:

(JSC::TemporalCalendarConstructor::finishCreation):

  • runtime/TemporalDuration.cpp:

(JSC::TemporalDuration::tryCreateIfValid):
(JSC::TemporalDuration::fromNonDurationValue):
(JSC::TemporalDuration::toDuration):
(JSC::TemporalDuration::toDurationRecord):
(JSC::TemporalDuration::toString const):
(JSC::TemporalDuration::toString):
(JSC::isValidDuration): Deleted.
(JSC::TemporalDuration::fromObject): Deleted.

  • runtime/TemporalDuration.h:
  • runtime/TemporalNow.cpp:

(JSC::JSC_DEFINE_HOST_FUNCTION):

  • runtime/TemporalObject.cpp:

(JSC::createPlainTimeConstructor):
(JSC::secondsStringPrecision):
(JSC::toTemporalOverflow):

  • runtime/TemporalObject.h:
  • runtime/TemporalPlainTime.cpp: Added.

(JSC::TemporalPlainTime::create):
(JSC::TemporalPlainTime::createStructure):
(JSC::TemporalPlainTime::TemporalPlainTime):
(JSC::TemporalPlainTime::finishCreation):
(JSC::TemporalPlainTime::visitChildrenImpl):
(JSC::toPlainTime):
(JSC::TemporalPlainTime::tryCreateIfValid):
(JSC::nonNegativeModulo):
(JSC::balanceTime):
(JSC::roundTime):
(JSC::TemporalPlainTime::round const):
(JSC::TemporalPlainTime::toString const):
(JSC::propertyName):
(JSC::toTemporalTimeRecord):
(JSC::toPartialTime):
(JSC::constraintTime):
(JSC::regulateTime):
(JSC::toTemporalCalendarWithISODefault):
(JSC::getTemporalCalendarWithISODefault):
(JSC::TemporalPlainTime::from):
(JSC::TemporalPlainTime::compare):
(JSC::toLimitedTemporalDuration):
(JSC::addTime):
(JSC::TemporalPlainTime::add const):
(JSC::TemporalPlainTime::subtract const):
(JSC::TemporalPlainTime::with const):
(JSC::differenceTime):
(JSC::extractDifferenceOptions):
(JSC::TemporalPlainTime::until const):
(JSC::TemporalPlainTime::since const):

  • runtime/TemporalPlainTime.h: Added.
  • runtime/TemporalPlainTimeConstructor.cpp: Added.

(JSC::TemporalPlainTimeConstructor::create):
(JSC::TemporalPlainTimeConstructor::createStructure):
(JSC::TemporalPlainTimeConstructor::TemporalPlainTimeConstructor):
(JSC::TemporalPlainTimeConstructor::finishCreation):
(JSC::JSC_DEFINE_HOST_FUNCTION):

  • runtime/TemporalPlainTimeConstructor.h: Copied from Source/JavaScriptCore/runtime/TemporalTimeZone.h.
  • runtime/TemporalPlainTimePrototype.cpp: Added.

(JSC::TemporalPlainTimePrototype::create):
(JSC::TemporalPlainTimePrototype::createStructure):
(JSC::TemporalPlainTimePrototype::TemporalPlainTimePrototype):
(JSC::TemporalPlainTimePrototype::finishCreation):
(JSC::JSC_DEFINE_HOST_FUNCTION):
(JSC::JSC_DEFINE_CUSTOM_GETTER):

  • runtime/TemporalPlainTimePrototype.h: Copied from Source/JavaScriptCore/runtime/TemporalTimeZone.h.
  • runtime/TemporalTimeZone.cpp:

(JSC::TemporalTimeZone::from):
(JSC::TemporalTimeZone::idForTimeZoneName): Deleted.

  • runtime/TemporalTimeZone.h:
  • runtime/TemporalTimeZoneConstructor.cpp:

(JSC::TemporalTimeZoneConstructor::finishCreation):
(JSC::JSC_DEFINE_HOST_FUNCTION):

  • runtime/VM.cpp:
  • runtime/VM.h:

Source/WTF:

  • wtf/text/IntegerToStringConversion.h:
  • wtf/text/StringParsingBuffer.h:
Location:
trunk
Files:
5 added
32 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r282081 r282125  
     12021-09-07  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] Implement Temporal.PlainTime
     4        https://bugs.webkit.org/show_bug.cgi?id=229892
     5
     6        Reviewed by Darin Adler.
     7
     8        * stress/temporal-calendar.js:
     9        * stress/temporal-duration.js:
     10        * stress/temporal-plaintime.js: Added.
     11        (shouldBe):
     12        (shouldThrow):
     13        (shouldBe.String.Temporal.PlainTime.from):
     14        (let.time.Temporal.PlainTime.from.shouldBe):
     15        (let.text.of.failures.shouldThrow):
     16        (print):
     17        (shouldBe.Temporal.PlainTime.from):
     18        (new.Temporal.PlainTime.valueOf):
     19        (shouldBe.String.time.until.Temporal.PlainTime.from):
     20        * stress/temporal-timezone.js:
     21        (let.text.of.failures.shouldThrow): Deleted.
     22        * test262/config.yaml:
     23
    1242021-09-07  Yusuke Suzuki  <ysuzuki@apple.com>
    225
  • trunk/JSTests/stress/temporal-calendar.js

    r281788 r282125  
    1818        shouldBe(String(error), message);
    1919}
     20
     21shouldBe(Temporal.Calendar instanceof Function, true);
     22shouldBe(Temporal.Calendar.length, 0);
     23shouldBe(Object.getOwnPropertyDescriptor(Temporal.Calendar, 'prototype').writable, false);
     24shouldBe(Object.getOwnPropertyDescriptor(Temporal.Calendar, 'prototype').enumerable, false);
     25shouldBe(Object.getOwnPropertyDescriptor(Temporal.Calendar, 'prototype').configurable, false);
     26shouldBe(Temporal.Calendar.prototype.constructor, Temporal.Calendar);
    2027
    2128{
  • trunk/JSTests/stress/temporal-duration.js

    r281838 r282125  
    2828shouldBe(Object.getOwnPropertyDescriptor(Temporal.Duration, 'prototype').configurable, false);
    2929shouldThrow(() => Temporal.Duration(), TypeError);
     30shouldBe(Temporal.Duration.prototype.constructor, Temporal.Duration);
    3031
    3132shouldBe(new Temporal.Duration() instanceof Temporal.Duration, true);
  • trunk/JSTests/stress/temporal-timezone.js

    r282018 r282125  
    1919}
    2020
     21shouldBe(Temporal.TimeZone instanceof Function, true);
     22shouldBe(Temporal.TimeZone.length, 0);
     23shouldBe(Object.getOwnPropertyDescriptor(Temporal.TimeZone, 'prototype').writable, false);
     24shouldBe(Object.getOwnPropertyDescriptor(Temporal.TimeZone, 'prototype').enumerable, false);
     25shouldBe(Object.getOwnPropertyDescriptor(Temporal.TimeZone, 'prototype').configurable, false);
     26shouldBe(Temporal.TimeZone.prototype.constructor, Temporal.TimeZone);
     27
    2128let failures = [
    2229    "",
     
    2734    "+23:",
    2835    "20:00",
     36    "+2:00",
     37    "+32:00",
     38    "+23:001",
     39    "+23:99",
     40    "+23:990",
     41    "+23:59:99",
     42    "+23:59:001",
     43    "+23:59:999",
     44    "+23:59:59.",
     45    "+23:59:59,",
     46    "+23:59:59a",
     47    "+23:59:59.a",
    2948    "+20:0000",
    3049    "+20:60",
    3150    "+20:59:",
     51    "+20:59:60",
    3252    "2059:00",
    3353    "+2059:00",
     
    5373shouldBe(new Temporal.TimeZone("-01:59:30.000000001").id, `-01:59:30.000000001`);
    5474shouldBe(new Temporal.TimeZone("-01:59:30.123456789").id, `-01:59:30.123456789`);
     75shouldBe(new Temporal.TimeZone("-01:59:30.001002003").id, `-01:59:30.001002003`);
     76shouldBe(new Temporal.TimeZone("-01:59:30.000000000").id, `-01:59:30`);
     77shouldBe(new Temporal.TimeZone("-01:59:00.000000000").id, `-01:59`);
     78shouldBe(new Temporal.TimeZone("-01:00:00.000000000").id, `-01:00`);
    5579
    5680let tz = new Temporal.TimeZone("-01:59:30.123456789")
  • trunk/JSTests/test262/config.yaml

    r281838 r282125  
    2828    - test/built-ins/Temporal/PlainDateTime
    2929    - test/built-ins/Temporal/PlainMonthDay
    30     - test/built-ins/Temporal/PlainTime
     30    - test/built-ins/Temporal/PlainTime/prototype/toPlainDateTime
     31    - test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime
    3132    - test/built-ins/Temporal/PlainYearMonth
    3233    - test/built-ins/Temporal/TimeZone
  • trunk/Source/JavaScriptCore/CMakeLists.txt

    r282018 r282125  
    114114    runtime/TemporalNow.cpp
    115115    runtime/TemporalObject.cpp
     116    runtime/TemporalPlainTimeConstructor.cpp
     117    runtime/TemporalPlainTimePrototype.cpp
    116118    runtime/TemporalTimeZoneConstructor.cpp
    117119    runtime/TemporalTimeZonePrototype.cpp
  • trunk/Source/JavaScriptCore/ChangeLog

    r282081 r282125  
     12021-09-07  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] Implement Temporal.PlainTime
     4        https://bugs.webkit.org/show_bug.cgi?id=229892
     5
     6        Reviewed by Darin Adler.
     7
     8        This patch implements Temporal.PlainTime[1]. This is time representation which is not associated to
     9        calendars and timezones. This is tuple of hour, minute, second, millisecond, microsecond, and nanosecond.
     10
     11        1. We add full-fledged ISO8601 DateTime / Time parser, so that Temporal.PlainTime.from can extract
     12           time as specified.
     13
     14        2. ISO8601::PlainTime is used for already-validated PlainTime data. When performing arithmetics, we first
     15           do that in ISO8601::Duration, and then we validate and convert it to PlainTime.
     16
     17        We also found several spec issues, and reported in [2,3,4].
     18
     19        [1]: https://tc39.es/proposal-temporal/#sec-temporal-plaintime-objects
     20        [2]: https://github.com/tc39/proposal-temporal/issues/1803
     21        [3]: https://github.com/tc39/proposal-temporal/issues/1804
     22        [4]: https://github.com/tc39/proposal-temporal/issues/1805
     23
     24        * CMakeLists.txt:
     25        * DerivedSources-input.xcfilelist:
     26        * DerivedSources-output.xcfilelist:
     27        * DerivedSources.make:
     28        * Sources.txt:
     29        * runtime/CommonIdentifiers.h:
     30        * runtime/ISO8601.cpp:
     31        (JSC::ISO8601::parseTimeZoneName):
     32        (JSC::ISO8601::parseDecimalInt32):
     33        (JSC::ISO8601::parseDuration):
     34        (JSC::ISO8601::parseTimeSpec):
     35        (JSC::ISO8601::parseTimeZoneNumericUTCOffset):
     36        (JSC::ISO8601::parseTimeZoneBracketedAnnotation):
     37        (JSC::ISO8601::canBeTimeZone):
     38        (JSC::ISO8601::parseTimeZone):
     39        (JSC::ISO8601::parseTime):
     40        (JSC::ISO8601::daysInMonth):
     41        (JSC::ISO8601::parseDate):
     42        (JSC::ISO8601::parseDateTime):
     43        (JSC::ISO8601::formatTimeZoneOffsetString):
     44        (JSC::ISO8601::temporalTimeToString):
     45        (JSC::ISO8601::isValidDuration):
     46        * runtime/ISO8601.h:
     47        (JSC::ISO8601::Duration::Duration):
     48        (JSC::ISO8601::Duration::operator[]):
     49        (JSC::ISO8601::Duration::operator[] const):
     50        (JSC::ISO8601::Duration::begin const):
     51        (JSC::ISO8601::Duration::end const):
     52        (JSC::ISO8601::Duration::clear):
     53        (JSC::ISO8601::Duration::operator- const):
     54        (JSC::ISO8601::PlainTime::PlainTime):
     55        (JSC::ISO8601::PlainTime::operator==):
     56        (JSC::ISO8601::PlainDate::PlainDate):
     57        (JSC::ISO8601::PlainDate::year const):
     58        (JSC::ISO8601::PlainDate::month const):
     59        (JSC::ISO8601::PlainDate::day const):
     60        * runtime/IntlObject.cpp:
     61        (JSC::utcTimeZoneIDSlow):
     62        * runtime/IntlObject.h:
     63        (JSC::utcTimeZoneID):
     64        * runtime/JSGlobalObject.cpp:
     65        (JSC::JSGlobalObject::init):
     66        (JSC::JSGlobalObject::visitChildrenImpl):
     67        * runtime/JSGlobalObject.h:
     68        (JSC::JSGlobalObject::plainTimeStructure):
     69        * runtime/TemporalCalendarConstructor.cpp:
     70        (JSC::TemporalCalendarConstructor::finishCreation):
     71        * runtime/TemporalDuration.cpp:
     72        (JSC::TemporalDuration::tryCreateIfValid):
     73        (JSC::TemporalDuration::fromNonDurationValue):
     74        (JSC::TemporalDuration::toDuration):
     75        (JSC::TemporalDuration::toDurationRecord):
     76        (JSC::TemporalDuration::toString const):
     77        (JSC::TemporalDuration::toString):
     78        (JSC::isValidDuration): Deleted.
     79        (JSC::TemporalDuration::fromObject): Deleted.
     80        * runtime/TemporalDuration.h:
     81        * runtime/TemporalNow.cpp:
     82        (JSC::JSC_DEFINE_HOST_FUNCTION):
     83        * runtime/TemporalObject.cpp:
     84        (JSC::createPlainTimeConstructor):
     85        (JSC::secondsStringPrecision):
     86        (JSC::toTemporalOverflow):
     87        * runtime/TemporalObject.h:
     88        * runtime/TemporalPlainTime.cpp: Added.
     89        (JSC::TemporalPlainTime::create):
     90        (JSC::TemporalPlainTime::createStructure):
     91        (JSC::TemporalPlainTime::TemporalPlainTime):
     92        (JSC::TemporalPlainTime::finishCreation):
     93        (JSC::TemporalPlainTime::visitChildrenImpl):
     94        (JSC::toPlainTime):
     95        (JSC::TemporalPlainTime::tryCreateIfValid):
     96        (JSC::nonNegativeModulo):
     97        (JSC::balanceTime):
     98        (JSC::roundTime):
     99        (JSC::TemporalPlainTime::round const):
     100        (JSC::TemporalPlainTime::toString const):
     101        (JSC::propertyName):
     102        (JSC::toTemporalTimeRecord):
     103        (JSC::toPartialTime):
     104        (JSC::constraintTime):
     105        (JSC::regulateTime):
     106        (JSC::toTemporalCalendarWithISODefault):
     107        (JSC::getTemporalCalendarWithISODefault):
     108        (JSC::TemporalPlainTime::from):
     109        (JSC::TemporalPlainTime::compare):
     110        (JSC::toLimitedTemporalDuration):
     111        (JSC::addTime):
     112        (JSC::TemporalPlainTime::add const):
     113        (JSC::TemporalPlainTime::subtract const):
     114        (JSC::TemporalPlainTime::with const):
     115        (JSC::differenceTime):
     116        (JSC::extractDifferenceOptions):
     117        (JSC::TemporalPlainTime::until const):
     118        (JSC::TemporalPlainTime::since const):
     119        * runtime/TemporalPlainTime.h: Added.
     120        * runtime/TemporalPlainTimeConstructor.cpp: Added.
     121        (JSC::TemporalPlainTimeConstructor::create):
     122        (JSC::TemporalPlainTimeConstructor::createStructure):
     123        (JSC::TemporalPlainTimeConstructor::TemporalPlainTimeConstructor):
     124        (JSC::TemporalPlainTimeConstructor::finishCreation):
     125        (JSC::JSC_DEFINE_HOST_FUNCTION):
     126        * runtime/TemporalPlainTimeConstructor.h: Copied from Source/JavaScriptCore/runtime/TemporalTimeZone.h.
     127        * runtime/TemporalPlainTimePrototype.cpp: Added.
     128        (JSC::TemporalPlainTimePrototype::create):
     129        (JSC::TemporalPlainTimePrototype::createStructure):
     130        (JSC::TemporalPlainTimePrototype::TemporalPlainTimePrototype):
     131        (JSC::TemporalPlainTimePrototype::finishCreation):
     132        (JSC::JSC_DEFINE_HOST_FUNCTION):
     133        (JSC::JSC_DEFINE_CUSTOM_GETTER):
     134        * runtime/TemporalPlainTimePrototype.h: Copied from Source/JavaScriptCore/runtime/TemporalTimeZone.h.
     135        * runtime/TemporalTimeZone.cpp:
     136        (JSC::TemporalTimeZone::from):
     137        (JSC::TemporalTimeZone::idForTimeZoneName): Deleted.
     138        * runtime/TemporalTimeZone.h:
     139        * runtime/TemporalTimeZoneConstructor.cpp:
     140        (JSC::TemporalTimeZoneConstructor::finishCreation):
     141        (JSC::JSC_DEFINE_HOST_FUNCTION):
     142        * runtime/VM.cpp:
     143        * runtime/VM.h:
     144
    11452021-09-07  Yusuke Suzuki  <ysuzuki@apple.com>
    2146
  • trunk/Source/JavaScriptCore/DerivedSources-input.xcfilelist

    r282018 r282125  
    176176$(PROJECT_DIR)/runtime/TemporalNow.cpp
    177177$(PROJECT_DIR)/runtime/TemporalObject.cpp
     178$(PROJECT_DIR)/runtime/TemporalPlainTimeConstructor.cpp
     179$(PROJECT_DIR)/runtime/TemporalPlainTimePrototype.cpp
    178180$(PROJECT_DIR)/runtime/TemporalTimeZoneConstructor.cpp
    179181$(PROJECT_DIR)/runtime/TemporalTimeZonePrototype.cpp
  • trunk/Source/JavaScriptCore/DerivedSources-output.xcfilelist

    r282018 r282125  
    7373$(BUILT_PRODUCTS_DIR)/DerivedSources/JavaScriptCore/TemporalNow.lut.h
    7474$(BUILT_PRODUCTS_DIR)/DerivedSources/JavaScriptCore/TemporalObject.lut.h
     75$(BUILT_PRODUCTS_DIR)/DerivedSources/JavaScriptCore/TemporalPlainTimeConstructor.lut.h
     76$(BUILT_PRODUCTS_DIR)/DerivedSources/JavaScriptCore/TemporalPlainTimePrototype.lut.h
    7577$(BUILT_PRODUCTS_DIR)/DerivedSources/JavaScriptCore/TemporalTimeZoneConstructor.lut.h
    7678$(BUILT_PRODUCTS_DIR)/DerivedSources/JavaScriptCore/TemporalTimeZonePrototype.lut.h
  • trunk/Source/JavaScriptCore/DerivedSources.make

    r282018 r282125  
    198198    TemporalNow.lut.h \
    199199    TemporalObject.lut.h \
     200    TemporalPlainTimeConstructor.lut.h \
     201    TemporalPlainTimePrototype.lut.h \
    200202    TemporalTimeZoneConstructor.lut.h \
    201203    TemporalTimeZonePrototype.lut.h \
  • trunk/Source/JavaScriptCore/Sources.txt

    r282018 r282125  
    10201020runtime/TemporalNow.cpp
    10211021runtime/TemporalObject.cpp
     1022runtime/TemporalPlainTime.cpp
     1023runtime/TemporalPlainTimeConstructor.cpp
     1024runtime/TemporalPlainTimePrototype.cpp
    10221025runtime/TemporalTimeZone.cpp
    10231026runtime/TemporalTimeZoneConstructor.cpp
  • trunk/Source/JavaScriptCore/runtime/CommonIdentifiers.h

    r281838 r282125  
    150150    macro(inferredName) \
    151151    macro(input) \
     152    macro(isoHour) \
     153    macro(isoMicrosecond) \
     154    macro(isoMillisecond) \
     155    macro(isoMinute) \
     156    macro(isoNanosecond) \
     157    macro(isoSecond) \
    152158    macro(instructionCount) \
    153159    macro(isArray) \
     
    170176    macro(maximumSignificantDigits) \
    171177    macro(message) \
     178    macro(microsecond) \
    172179    macro(microseconds) \
     180    macro(millisecond) \
    173181    macro(milliseconds) \
    174182    macro(minimumFractionDigits) \
     
    182190    macro(multiline) \
    183191    macro(name) \
     192    macro(nanosecond) \
    184193    macro(nanoseconds) \
    185194    macro(next) \
     
    195204    macro(osrExitSites) \
    196205    macro(osrExits) \
     206    macro(overflow) \
    197207    macro(parse) \
    198208    macro(parseInt) \
     
    246256    macro(value) \
    247257    macro(valueOf) \
     258    macro(week) \
    248259    macro(weekday) \
    249260    macro(weeks) \
  • trunk/Source/JavaScriptCore/runtime/ISO8601.cpp

    r282018 r282125  
    11/*
    22 * Copyright (C) 2021 Sony Interactive Entertainment Inc.
     3 * Copyright (C) 2021 Apple Inc.
    34 *
    45 * Redistribution and use in source and binary forms, with or without
     
    2728#include "ISO8601.h"
    2829
     30#include "IntlObject.h"
    2931#include "ParseInt.h"
     32#include <wtf/DateMath.h>
    3033#include <wtf/text/StringParsingBuffer.h>
     34#include <wtf/unicode/CharacterNames.h>
    3135
    3236namespace JSC {
     
    3943static constexpr int64_t nsPerMicrosecond = 1000LL;
    4044
     45std::optional<TimeZoneID> parseTimeZoneName(StringView string)
     46{
     47    const auto& timeZones = intlAvailableTimeZones();
     48    for (unsigned index = 0; index < timeZones.size(); ++index) {
     49        if (equalIgnoringASCIICase(timeZones[index], string))
     50            return index;
     51    }
     52    return std::nullopt;
     53}
     54
    4155template<typename CharType>
    42 int32_t parseDecimalInt32(const CharType* characters, unsigned length)
     56static int32_t parseDecimalInt32(const CharType* characters, unsigned length)
    4357{
    4458    int32_t result = 0;
     
    120134    if (*buffer == '+')
    121135        buffer.advance();
    122     else if (*buffer == '-' || *buffer == 0x2212) {
     136    else if (*buffer == '-' || *buffer == minusSign) {
    123137        factor = -1;
    124138        buffer.advance();
     
    244258}
    245259
     260
     261enum class Second60Mode { Accept, Reject };
     262template<typename CharacterType>
     263static std::optional<PlainTime> parseTimeSpec(StringParsingBuffer<CharacterType>& buffer, Second60Mode second60Mode)
     264{
     265    // https://tc39.es/proposal-temporal/#prod-TimeSpec
     266    // TimeSpec :
     267    //     TimeHour
     268    //     TimeHour : TimeMinute
     269    //     TimeHour TimeMinute
     270    //     TimeHour : TimeMinute : TimeSecond TimeFraction[opt]
     271    //     TimeHour TimeMinute TimeSecond TimeFraction[opt]
     272    //
     273    //  TimeSecond can be 60. And if it is 60, we interpret it as 59.
     274    //  https://tc39.es/proposal-temporal/#sec-temporal-parseisodatetime
     275
     276    if (buffer.lengthRemaining() < 2)
     277        return std::nullopt;
     278
     279    unsigned hour = 0;
     280    ASSERT(buffer.lengthRemaining() >= 2);
     281    auto firstHourCharacter = *buffer;
     282    if (firstHourCharacter >= '0' && firstHourCharacter <= '2') {
     283        buffer.advance();
     284        auto secondHourCharacter = *buffer;
     285        if (!isASCIIDigit(secondHourCharacter))
     286            return std::nullopt;
     287        hour = (secondHourCharacter - '0') + 10 * (firstHourCharacter - '0');
     288        if (hour >= 24)
     289            return std::nullopt;
     290        buffer.advance();
     291    } else
     292        return std::nullopt;
     293
     294    if (buffer.atEnd())
     295        return PlainTime(hour, 0, 0, 0, 0, 0);
     296
     297    bool splitByColon = false;
     298    if (*buffer == ':') {
     299        splitByColon = true;
     300        buffer.advance();
     301    } else if (!(*buffer >= '0' && *buffer <= '5'))
     302        return PlainTime(hour, 0, 0, 0, 0, 0);
     303
     304    unsigned minute = 0;
     305    if (buffer.lengthRemaining() < 2)
     306        return std::nullopt;
     307    auto firstMinuteCharacter = *buffer;
     308    if (firstMinuteCharacter >= '0' && firstMinuteCharacter <= '5') {
     309        buffer.advance();
     310        auto secondMinuteCharacter = *buffer;
     311        if (!isASCIIDigit(secondMinuteCharacter))
     312            return std::nullopt;
     313        minute = (secondMinuteCharacter - '0') + 10 * (firstMinuteCharacter - '0');
     314        ASSERT(minute < 60);
     315        buffer.advance();
     316    } else
     317        return std::nullopt;
     318
     319    if (buffer.atEnd())
     320        return PlainTime(hour, minute, 0, 0, 0, 0);
     321
     322    if (splitByColon) {
     323        if (*buffer == ':')
     324            buffer.advance();
     325        else
     326            return PlainTime(hour, minute, 0, 0, 0, 0);
     327    } else if (!(*buffer >= '0' && (second60Mode == Second60Mode::Accept ? (*buffer <= '6') : (*buffer <= '5'))))
     328        return PlainTime(hour, minute, 0, 0, 0, 0);
     329
     330    unsigned second = 0;
     331    if (buffer.lengthRemaining() < 2)
     332        return std::nullopt;
     333    auto firstSecondCharacter = *buffer;
     334    if (firstSecondCharacter >= '0' && firstSecondCharacter <= '5') {
     335        buffer.advance();
     336        auto secondSecondCharacter = *buffer;
     337        if (!isASCIIDigit(secondSecondCharacter))
     338            return std::nullopt;
     339        second = (secondSecondCharacter - '0') + 10 * (firstSecondCharacter - '0');
     340        ASSERT(second < 60);
     341        buffer.advance();
     342    } else if (second60Mode == Second60Mode::Accept && firstSecondCharacter == '6') {
     343        buffer.advance();
     344        auto secondSecondCharacter = *buffer;
     345        if (secondSecondCharacter != '0')
     346            return std::nullopt;
     347        second = 59;
     348        buffer.advance();
     349    } else
     350        return std::nullopt;
     351
     352    if (buffer.atEnd())
     353        return PlainTime(hour, minute, second, 0, 0, 0);
     354
     355    if (*buffer != '.' && *buffer != ',')
     356        return PlainTime(hour, minute, second, 0, 0, 0);
     357    buffer.advance();
     358
     359    unsigned digits = 0;
     360    unsigned maxCount = std::min(buffer.lengthRemaining(), 9u);
     361    for (; digits < maxCount; ++digits) {
     362        if (!isASCIIDigit(buffer[digits]))
     363            break;
     364    }
     365    if (!digits)
     366        return std::nullopt;
     367
     368    Vector<LChar, 9> padded(9, '0');
     369    for (unsigned i = 0; i < digits; ++i)
     370        padded[i] = buffer[i];
     371    buffer.advanceBy(digits);
     372
     373    unsigned millisecond = parseDecimalInt32(padded.data(), 3);
     374    unsigned microsecond = parseDecimalInt32(padded.data() + 3, 3);
     375    unsigned nanosecond = parseDecimalInt32(padded.data() + 6, 3);
     376
     377    return PlainTime(hour, minute, second, millisecond, microsecond, nanosecond);
     378}
     379
    246380template<typename CharacterType>
    247381static std::optional<int64_t> parseTimeZoneNumericUTCOffset(StringParsingBuffer<CharacterType>& buffer)
     
    252386    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute
    253387    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour : TimeZoneUTCOffsetMinute : TimeZoneUTCOffsetSecond TimeZoneUTCOffsetFraction[opt]
    254     //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute TimeZoneUTCOffsetSecond[opt]
     388    //     TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute TimeZoneUTCOffsetSecond TimeZoneUTCOffsetFraction[opt]
     389    //
     390    //  This is the same to
     391    //     TimeZoneUTCOffsetSign TimeSpec
    255392    //
    256393    //  Maximum and minimum values are ±23:59:59.999999999 = ±86399999999999ns, which can be represented by int64_t / double's integer part.
     
    263400    if (*buffer == '+')
    264401        buffer.advance();
    265     else if (*buffer == '-' || *buffer == 0x2212) {
     402    else if (*buffer == '-' || *buffer == minusSign) {
    266403        factor = -1;
    267404        buffer.advance();
     
    269406        return std::nullopt;
    270407
    271     int64_t hour = 0;
    272     ASSERT(buffer.lengthRemaining() >= 2);
    273     auto firstHourCharacter = *buffer;
    274     if (firstHourCharacter >= '0' && firstHourCharacter <= '2') {
    275         buffer.advance();
    276         auto secondHourCharacter = *buffer;
    277         if (!isASCIIDigit(secondHourCharacter))
    278             return std::nullopt;
    279         hour = (secondHourCharacter - '0') + 10 * (firstHourCharacter - '0');
    280         if (hour >= 24)
     408    auto plainTime = parseTimeSpec(buffer, Second60Mode::Reject);
     409    if (!plainTime)
     410        return std::nullopt;
     411
     412    int64_t hour = plainTime->hour();
     413    int64_t minute = plainTime->minute();
     414    int64_t second = plainTime->second();
     415    int64_t millisecond = plainTime->millisecond();
     416    int64_t microsecond = plainTime->microsecond();
     417    int64_t nanosecond = plainTime->nanosecond();
     418
     419    return (nsPerHour * hour + nsPerMinute * minute + nsPerSecond * second + nsPerMillisecond * millisecond + nsPerMicrosecond * microsecond + nanosecond) * factor;
     420}
     421
     422std::optional<int64_t> parseTimeZoneNumericUTCOffset(StringView string)
     423{
     424    return readCharactersForParsing(string, [](auto buffer) -> std::optional<int64_t> {
     425        auto result = parseTimeZoneNumericUTCOffset(buffer);
     426        if (!buffer.atEnd())
     427            return std::nullopt;
     428        return result;
     429    });
     430}
     431
     432template<typename CharacterType>
     433static std::optional<Variant<Vector<LChar>, int64_t>> parseTimeZoneBracketedAnnotation(StringParsingBuffer<CharacterType>& buffer)
     434{
     435    // https://tc39.es/proposal-temporal/#prod-TimeZoneBracketedAnnotation
     436    // TimeZoneBracketedAnnotation :
     437    //     [ TimeZoneBracketedName ]
     438    //
     439    // TimeZoneBracketedName :
     440    //     TimeZoneIANAName
     441    //     Etc/GMT ASCIISign Hour
     442    //     TimeZoneUTCOffsetName
     443
     444    if (buffer.lengthRemaining() < 3)
     445        return std::nullopt;
     446
     447    if (*buffer != '[')
     448        return std::nullopt;
     449    buffer.advance();
     450
     451    switch (*buffer) {
     452    case '+':
     453    case '-':
     454    case minusSign: {
     455        // TimeZoneUTCOffsetName is the same to TimeZoneNumericUTCOffset.
     456        auto offset = parseTimeZoneNumericUTCOffset(buffer);
     457        if (!offset)
     458            return std::nullopt;
     459        if (buffer.atEnd())
     460            return std::nullopt;
     461        if (*buffer != ']')
     462            return std::nullopt;
     463        buffer.advance();
     464        return offset.value();
     465    }
     466    case 'E': {
     467        // "Etc/GMT+20" and "]" => length is 11.
     468        if (buffer.lengthRemaining() >= 11) {
     469            if (buffer[0] == 'E' && buffer[1] == 't' && buffer[2] == 'c' && buffer[3] == '/' && buffer[4] == 'G' && buffer[5] == 'M' && buffer[6] == 'T') {
     470                auto signCharacter = buffer[7];
     471                // Not including minusSign since it is ASCIISign.
     472                if (signCharacter == '+' || signCharacter == '-') {
     473                    // Etc/GMT+01 is UTC-01:00. This sign is intentionally inverted.
     474                    // https://en.wikipedia.org/wiki/Tz_database#Area
     475                    int64_t factor = signCharacter == '+' ? -1 : 1;
     476                    int64_t hour = 0;
     477                    auto firstHourCharacter = buffer[8];
     478                    if (firstHourCharacter >= '0' && firstHourCharacter <= '2') {
     479                        auto secondHourCharacter = buffer[9];
     480                        if (isASCIIDigit(secondHourCharacter)) {
     481                            hour = (secondHourCharacter - '0') + 10 * (firstHourCharacter - '0');
     482                            if (hour < 24 && buffer[10] == ']') {
     483                                buffer.advanceBy(11);
     484                                return nsPerHour * hour * factor;
     485                            }
     486                        }
     487                    }
     488                }
     489            }
     490        }
     491        FALLTHROUGH;
     492    }
     493    default: {
     494        // TZLeadingChar :
     495        //     Alpha
     496        //     .
     497        //     _
     498        //
     499        // TZChar :
     500        //     Alpha
     501        //     .
     502        //     -
     503        //     _
     504        //
     505        // TimeZoneIANANameComponent :
     506        //     TZLeadingChar TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] TZChar[opt] but not one of . or ..
     507        //
     508        // TimeZoneIANAName :
     509        //     TimeZoneIANANameComponent
     510        //     TimeZoneIANAName / TimeZoneIANANameComponent
     511
     512        unsigned nameLength = 0;
     513        {
     514            unsigned index = 0;
     515            for (; index < buffer.lengthRemaining(); ++index) {
     516                auto character = buffer[index];
     517                if (character == ']')
     518                    break;
     519                if (!isASCIIAlpha(character) && character != '.' && character != '_' && character != '-' && character != '/')
     520                    return std::nullopt;
     521            }
     522            if (!index)
     523                return std::nullopt;
     524            nameLength = index;
     525        }
     526
     527        auto isValidComponent = [&](unsigned start, unsigned end) {
     528            unsigned componentLength = end - start;
     529            if (!componentLength)
     530                return false;
     531            if (componentLength > 14)
     532                return false;
     533            if (componentLength == 1 && buffer[start] == '.')
     534                return false;
     535            if (componentLength == 2 && buffer[start] == '.' && buffer[start + 1] == '.')
     536                return false;
     537            return true;
     538        };
     539
     540        unsigned currentNameComponentStartIndex = 0;
     541        bool isLeadingCharacterInNameComponent = true;
     542        for (unsigned index = 0; index < nameLength; ++index) {
     543            auto character = buffer[index];
     544            if (isLeadingCharacterInNameComponent) {
     545                if (!(isASCIIAlpha(character) || character == '.' || character == '_'))
     546                    return std::nullopt;
     547
     548                currentNameComponentStartIndex = index;
     549                isLeadingCharacterInNameComponent = false;
     550                continue;
     551            }
     552
     553            if (character == '/') {
     554                if (!isValidComponent(currentNameComponentStartIndex, index))
     555                    return std::nullopt;
     556                isLeadingCharacterInNameComponent = true;
     557                continue;
     558            }
     559
     560            if (!(isASCIIAlpha(character) || character == '.' || character == '_' || character == '-'))
     561                return std::nullopt;
     562        }
     563        if (isLeadingCharacterInNameComponent)
     564            return std::nullopt;
     565        if (!isValidComponent(currentNameComponentStartIndex, nameLength))
     566            return std::nullopt;
     567
     568        Vector<LChar> result;
     569        result.reserveInitialCapacity(nameLength);
     570        for (unsigned index = 0; index < nameLength; ++index)
     571            result.uncheckedAppend(buffer[index]);
     572        buffer.advanceBy(nameLength);
     573
     574        if (buffer.atEnd())
     575            return std::nullopt;
     576        if (*buffer != ']')
     577            return std::nullopt;
     578        buffer.advance();
     579        return WTFMove(result);
     580    }
     581    }
     582}
     583
     584template<typename CharacterType>
     585static bool canBeTimeZone(const StringParsingBuffer<CharacterType>& buffer, CharacterType character)
     586{
     587    switch (character) {
     588    // UTCDesignator
     589    // https://tc39.es/proposal-temporal/#prod-UTCDesignator
     590    case 'z':
     591    case 'Z':
     592    // TimeZoneUTCOffsetSign
     593    // https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetSign
     594    case '+':
     595    case '-':
     596    case minusSign:
     597    // TimeZoneBracketedAnnotation
     598    // https://tc39.es/proposal-temporal/#prod-TimeZoneBracketedAnnotation
     599    case '[': {
     600        // We should reject calendar extension case.
     601        // https://tc39.es/proposal-temporal/#prod-Calendar
     602        // Calendar :
     603        //     [u-ca= CalendarName]
     604        if (buffer.lengthRemaining() >= 6 && buffer[1] == 'u' && buffer[2] == '-' && buffer[3] == 'c' && buffer[4] == 'a' && buffer[5] == '=')
     605            return false;
     606        return true;
     607    }
     608    default:
     609        return false;
     610    }
     611}
     612
     613template<typename CharacterType>
     614static std::optional<TimeZoneRecord> parseTimeZone(StringParsingBuffer<CharacterType>& buffer)
     615{
     616    if (buffer.atEnd())
     617        return std::nullopt;
     618    switch (*buffer) {
     619    // UTCDesignator
     620    // https://tc39.es/proposal-temporal/#prod-UTCDesignator
     621    case 'z':
     622    case 'Z': {
     623        buffer.advance();
     624        if (!buffer.atEnd() && *buffer == '[') {
     625            auto timeZone = parseTimeZoneBracketedAnnotation(buffer);
     626            if (!timeZone)
     627                return std::nullopt;
     628            return TimeZoneRecord { true, std::nullopt, WTFMove(timeZone.value()) };
     629        }
     630        return TimeZoneRecord { true, std::nullopt, { } };
     631    }
     632    // TimeZoneUTCOffsetSign
     633    // https://tc39.es/proposal-temporal/#prod-TimeZoneUTCOffsetSign
     634    case '+':
     635    case '-':
     636    case minusSign: {
     637        auto offset = parseTimeZoneNumericUTCOffset(buffer);
     638        if (!offset)
     639            return std::nullopt;
     640        if (!buffer.atEnd() && *buffer == '[') {
     641            auto timeZone = parseTimeZoneBracketedAnnotation(buffer);
     642            if (!timeZone)
     643                return std::nullopt;
     644            return TimeZoneRecord { false, offset.value(), WTFMove(timeZone.value()) };
     645        }
     646        return TimeZoneRecord { false, offset.value(), { } };
     647    }
     648    // TimeZoneBracketedAnnotation
     649    // https://tc39.es/proposal-temporal/#prod-TimeZoneBracketedAnnotation
     650    case '[': {
     651        auto timeZone = parseTimeZoneBracketedAnnotation(buffer);
     652        if (!timeZone)
     653            return std::nullopt;
     654        return TimeZoneRecord { false, std::nullopt, WTFMove(timeZone.value()) };
     655    }
     656    default:
     657        return std::nullopt;
     658    }
     659}
     660
     661template<typename CharacterType>
     662static std::optional<std::tuple<PlainTime, std::optional<TimeZoneRecord>>> parseTime(StringParsingBuffer<CharacterType>& buffer)
     663{
     664    // https://tc39.es/proposal-temporal/#prod-Time
     665    // Time :
     666    //     TimeSpec TimeZone[opt]
     667    auto plainTime = parseTimeSpec(buffer, Second60Mode::Accept);
     668    if (!plainTime)
     669        return std::nullopt;
     670    if (buffer.atEnd())
     671        return std::tuple { plainTime.value(), std::nullopt };
     672    if (canBeTimeZone(buffer, *buffer)) {
     673        auto timeZone = parseTimeZone(buffer);
     674        if (!timeZone)
     675            return std::nullopt;
     676        return std::tuple { plainTime.value(), timeZone };
     677    }
     678    return std::tuple { plainTime.value(), std::nullopt };
     679}
     680
     681// https://tc39.es/proposal-temporal/#sec-temporal-isodaysinmonth
     682static inline unsigned daysInMonth(int32_t year, unsigned month)
     683{
     684    switch (month) {
     685    case 1:
     686    case 3:
     687    case 5:
     688    case 7:
     689    case 8:
     690    case 10:
     691    case 12:
     692        return 31;
     693    case 4:
     694    case 6:
     695    case 9:
     696    case 11:
     697        return 30;
     698    case 2:
     699        if (isLeapYear(year))
     700            return 29;
     701        return 28;
     702    }
     703    return 0;
     704}
     705
     706template<typename CharacterType>
     707static std::optional<PlainDate> parseDate(StringParsingBuffer<CharacterType>& buffer)
     708{
     709    // https://tc39.es/proposal-temporal/#prod-Date
     710    // Date :
     711    //     DateYear - DateMonth - DateDay
     712    //     DateYear DateMonth DateDay
     713    //
     714    // DateYear :
     715    //     DateFourDigitYear
     716    //     DateExtendedYear
     717    //
     718    // DateFourDigitYear :
     719    //     Digit Digit Digit Digit
     720    //
     721    // DateExtendedYear :
     722    //     Sign Digit Digit Digit Digit Digit Digit
     723    //
     724    // DateMonth :
     725    //     0 NonzeroDigit
     726    //     10
     727    //     11
     728    //     12
     729    //
     730    // DateDay :
     731    //     0 NonzeroDigit
     732    //     1 Digit
     733    //     2 Digit
     734    //     30
     735    //     31
     736
     737    if (buffer.atEnd())
     738        return std::nullopt;
     739
     740    bool sixDigitsYear = false;
     741    int yearFactor = 1;
     742    if (*buffer == '+') {
     743        buffer.advance();
     744        sixDigitsYear = true;
     745    } else if (*buffer == '-' || *buffer == minusSign) {
     746        yearFactor = -1;
     747        buffer.advance();
     748        sixDigitsYear = true;
     749    } else if (!isASCIIDigit(*buffer))
     750        return std::nullopt;
     751
     752    int32_t year = 0;
     753    if (sixDigitsYear) {
     754        if (buffer.lengthRemaining() < 6)
     755            return std::nullopt;
     756        for (unsigned index = 0; index < 6; ++index) {
     757            if (!isASCIIDigit(buffer[index]))
     758                return std::nullopt;
     759        }
     760        year = parseDecimalInt32(buffer.position(), 6) * yearFactor;
     761        buffer.advanceBy(6);
     762    } else {
     763        if (buffer.lengthRemaining() < 4)
     764            return std::nullopt;
     765        for (unsigned index = 0; index < 4; ++index) {
     766            if (!isASCIIDigit(buffer[index]))
     767                return std::nullopt;
     768        }
     769        year = parseDecimalInt32(buffer.position(), 4);
     770        buffer.advanceBy(4);
     771    }
     772
     773    if (buffer.atEnd())
     774        return std::nullopt;
     775
     776    bool splitByHyphen = false;
     777    if (*buffer == '-') {
     778        splitByHyphen = true;
     779        buffer.advance();
     780        if (buffer.lengthRemaining() < 5)
     781            return std::nullopt;
     782    } else {
     783        if (buffer.lengthRemaining() < 4)
     784            return std::nullopt;
     785    }
     786    // We ensured that buffer has enough length for month and day. We do not need to check length.
     787
     788    unsigned month = 0;
     789    auto firstMonthCharacter = *buffer;
     790    if (firstMonthCharacter == '0' || firstMonthCharacter == '1') {
     791        buffer.advance();
     792        auto secondMonthCharacter = *buffer;
     793        if (!isASCIIDigit(secondMonthCharacter))
     794            return std::nullopt;
     795        month = (secondMonthCharacter - '0') + 10 * (firstMonthCharacter - '0');
     796        if (!month || month > 12)
    281797            return std::nullopt;
    282798        buffer.advance();
     
    284800        return std::nullopt;
    285801
    286     if (buffer.atEnd())
    287         return (nsPerHour * hour) * factor;
    288 
    289     bool splitByColon = false;
    290     if (*buffer == ':') {
    291         buffer.advance();
    292         splitByColon = true;
    293     }
    294 
    295     int64_t minute = 0;
    296     if (buffer.lengthRemaining() < 2)
    297         return std::nullopt;
    298     auto firstMinuteCharacter = *buffer;
    299     if (firstMinuteCharacter >= '0' && firstMinuteCharacter <= '5') {
    300         buffer.advance();
    301         auto secondMinuteCharacter = *buffer;
    302         if (!isASCIIDigit(secondMinuteCharacter))
    303             return std::nullopt;
    304         minute = (secondMinuteCharacter - '0') + 10 * (firstMinuteCharacter - '0');
    305         ASSERT(minute < 60);
    306         buffer.advance();
    307     } else
    308         return std::nullopt;
    309 
    310     if (buffer.atEnd())
    311         return (nsPerHour * hour + nsPerMinute * minute) * factor;
    312 
    313     if (splitByColon) {
    314         if (*buffer == ':')
     802    if (splitByHyphen) {
     803        if (*buffer == '-')
    315804            buffer.advance();
    316805        else
     
    318807    }
    319808
    320     int64_t second = 0;
    321     if (buffer.lengthRemaining() < 2)
    322         return std::nullopt;
    323     auto firstSecondCharacter = *buffer;
    324     if (firstSecondCharacter >= '0' && firstSecondCharacter <= '5') {
    325         buffer.advance();
    326         auto secondSecondCharacter = *buffer;
    327         if (!isASCIIDigit(secondSecondCharacter))
    328             return std::nullopt;
    329         second = (secondSecondCharacter - '0') + 10 * (firstSecondCharacter - '0');
    330         ASSERT(minute < 60);
     809    unsigned day = 0;
     810    auto firstDayCharacter = *buffer;
     811    if (firstDayCharacter >= '0' && firstDayCharacter <= '3') {
     812        buffer.advance();
     813        auto secondDayCharacter = *buffer;
     814        if (!isASCIIDigit(secondDayCharacter))
     815            return std::nullopt;
     816        day = (secondDayCharacter - '0') + 10 * (firstDayCharacter - '0');
     817        if (!day || day > daysInMonth(year, month))
     818            return std::nullopt;
    331819        buffer.advance();
    332820    } else
    333821        return std::nullopt;
    334822
     823    return PlainDate(year, month, day);
     824}
     825
     826template<typename CharacterType>
     827static std::optional<std::tuple<PlainDate, std::optional<PlainTime>, std::optional<TimeZoneRecord>>> parseDateTime(StringParsingBuffer<CharacterType>& buffer)
     828{
     829    // https://tc39.es/proposal-temporal/#prod-DateTime
     830    // DateTime :
     831    //     Date TimeSpecSeparator[opt] TimeZone[opt]
     832    //
     833    // TimeSpecSeparator :
     834    //     DateTimeSeparator TimeSpec
     835    auto plainDate = parseDate(buffer);
     836    if (!plainDate)
     837        return std::nullopt;
    335838    if (buffer.atEnd())
    336         return (nsPerHour * hour + nsPerMinute * minute + nsPerSecond * second) * factor;
    337 
    338     if (*buffer != '.' && *buffer != ',')
    339         return std::nullopt;
    340     buffer.advance();
    341 
    342     unsigned digits = 0;
    343     while (digits < buffer.lengthRemaining() && isASCIIDigit(buffer[digits]))
    344         digits++;
    345     if (!digits || digits > 9)
    346         return std::nullopt;
    347 
    348     Vector<LChar, 9> padded(9, '0');
    349     for (unsigned i = 0; i < digits; ++i)
    350         padded[i] = buffer[i];
    351     buffer.advanceBy(digits);
    352     if (!buffer.atEnd())
    353         return std::nullopt;
    354 
    355     int64_t millisecond = parseDecimalInt32(padded.data(), 3);
    356     int64_t microsecond = parseDecimalInt32(padded.data() + 3, 3);
    357     int64_t nanosecond = parseDecimalInt32(padded.data() + 6, 3);
    358 
    359     return (nsPerHour * hour + nsPerMinute * minute + nsPerSecond * second + nsPerMillisecond * millisecond + nsPerMicrosecond * microsecond + nanosecond) * factor;
    360 }
    361 
    362 std::optional<int64_t> parseTimeZoneNumericUTCOffset(StringView string)
    363 {
    364     return readCharactersForParsing(string, [](auto buffer) -> std::optional<int64_t> {
    365         return parseTimeZoneNumericUTCOffset(buffer);
     839        return std::tuple { plainDate.value(), std::nullopt, std::nullopt };
     840
     841    if (*buffer == ' ' || *buffer == 'T' || *buffer == 't') {
     842        buffer.advance();
     843        auto plainTimeAndTimeZone = parseTime(buffer);
     844        if (!plainTimeAndTimeZone)
     845            return std::nullopt;
     846        auto [plainTime, timeZone] = plainTimeAndTimeZone.value();
     847        return std::tuple { plainDate.value(), plainTime, timeZone };
     848    }
     849
     850    if (canBeTimeZone(buffer, *buffer)) {
     851        auto timeZone = parseTimeZone(buffer);
     852        if (!timeZone)
     853            return std::nullopt;
     854        return std::tuple { plainDate.value(), std::nullopt, timeZone };
     855    }
     856
     857    return std::tuple { plainDate.value(), std::nullopt, std::nullopt };
     858}
     859
     860std::optional<std::tuple<PlainTime, std::optional<TimeZoneRecord>>> parseTime(StringView string)
     861{
     862    return readCharactersForParsing(string, [](auto buffer) -> std::optional<std::tuple<PlainTime, std::optional<TimeZoneRecord>>> {
     863        auto result = parseTime(buffer);
     864        if (!buffer.atEnd())
     865            return std::nullopt;
     866        return result;
     867    });
     868}
     869
     870std::optional<std::tuple<PlainDate, std::optional<PlainTime>, std::optional<TimeZoneRecord>>> parseDateTime(StringView string)
     871{
     872    return readCharactersForParsing(string, [](auto buffer) -> std::optional<std::tuple<PlainDate, std::optional<PlainTime>, std::optional<TimeZoneRecord>>> {
     873        auto result = parseDateTime(buffer);
     874        if (!buffer.atEnd())
     875            return std::nullopt;
     876        return result;
    366877    });
    367878}
     
    382893    if (nanoseconds) {
    383894        // Since nsPerSecond is 1000000000, stringified nanoseconds takes at most 9 characters (999999999).
    384         auto fraction = WTF::numberToStringUnsigned<Vector<LChar, 9>>(nanoseconds);
    385         unsigned index = 0;
     895        auto fraction = numberToStringUnsigned<Vector<LChar, 9>>(nanoseconds);
    386896        unsigned paddingLength = 9 - fraction.size();
    387         for (; index < fraction.size(); ++index) {
    388             if (fraction[index] == '0')
     897        unsigned index = fraction.size();
     898        std::optional<unsigned> validLength;
     899        while (index--) {
     900            if (fraction[index] != '0') {
     901                validLength = index + 1;
    389902                break;
    390         }
    391         fraction.resize(index);
     903            }
     904        }
     905        if (validLength)
     906            fraction.resize(validLength.value());
     907        else
     908            fraction.clear();
    392909        return makeString(negative ? '-' : '+', pad('0', 2, hours), ':', pad('0', 2, minutes), ':', pad('0', 2, seconds), '.', pad('0', paddingLength, emptyString()), fraction);
    393910    }
     
    397914}
    398915
     916String temporalTimeToString(PlainTime plainTime, std::tuple<Precision, unsigned> precision)
     917{
     918    auto [precisionType, precisionValue] = precision;
     919    ASSERT(precisionType == Precision::Auto || precisionValue < 10);
     920    if (precisionType == Precision::Minute)
     921        return makeString(pad('0', 2, plainTime.hour()), ':', pad('0', 2, plainTime.minute()));
     922
     923    int64_t milliseconds = plainTime.millisecond();
     924    int64_t microseconds = plainTime.microsecond();
     925    int64_t nanoseconds = plainTime.nanosecond();
     926    int64_t fractionNanoseconds = milliseconds * nsPerMillisecond + microseconds * nsPerMicrosecond + nanoseconds;
     927    if (precisionType == Precision::Auto) {
     928        if (!fractionNanoseconds)
     929            return makeString(pad('0', 2, plainTime.hour()), ':', pad('0', 2, plainTime.minute()), ':', pad('0', 2, plainTime.second()));
     930        auto fraction = numberToStringUnsigned<Vector<LChar, 9>>(fractionNanoseconds);
     931        unsigned paddingLength = 9 - fraction.size();
     932        unsigned index = fraction.size();
     933        std::optional<unsigned> validLength;
     934        while (index--) {
     935            if (fraction[index] != '0') {
     936                validLength = index + 1;
     937                break;
     938            }
     939        }
     940        if (validLength)
     941            fraction.resize(validLength.value());
     942        else
     943            fraction.clear();
     944        return makeString(pad('0', 2, plainTime.hour()), ':', pad('0', 2, plainTime.minute()), ':', pad('0', 2, plainTime.second()), '.', pad('0', paddingLength, emptyString()), fraction);
     945    }
     946    if (!precisionValue)
     947        return makeString(pad('0', 2, plainTime.hour()), ':', pad('0', 2, plainTime.minute()), ':', pad('0', 2, plainTime.second()));
     948    auto fraction = numberToStringUnsigned<Vector<LChar, 9>>(fractionNanoseconds);
     949    unsigned paddingLength = 9 - fraction.size();
     950    paddingLength = std::min(paddingLength, precisionValue);
     951    precisionValue -= paddingLength;
     952    fraction.resize(precisionValue);
     953    return makeString(pad('0', 2, plainTime.hour()), ':', pad('0', 2, plainTime.minute()), ':', pad('0', 2, plainTime.second()), '.', pad('0', paddingLength, emptyString()), fraction);
     954}
     955
     956// IsValidDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds )
     957// https://tc39.es/proposal-temporal/#sec-temporal-isvalidduration
     958bool isValidDuration(const Duration& duration)
     959{
     960    int sign = 0;
     961    for (auto value : duration) {
     962        if (!std::isfinite(value) || (value < 0 && sign > 0) || (value > 0 && sign < 0))
     963            return false;
     964
     965        if (!sign && value)
     966            sign = value > 0 ? 1 : -1;
     967    }
     968
     969    return true;
     970}
     971
    399972} // namespace ISO8601
    400973} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/ISO8601.h

    r282018 r282125  
    11/*
    22 * Copyright (C) 2021 Sony Interactive Entertainment Inc.
     3 * Copyright (C) 2021 Apple Inc.
    34 *
    45 * Redistribution and use in source and binary forms, with or without
     
    2627#pragma once
    2728
     29#include "IntlObject.h"
    2830#include "TemporalObject.h"
    2931
     
    3133namespace ISO8601 {
    3234
    33 struct Duration {
     35class Duration {
     36    WTF_MAKE_FAST_ALLOCATED(Duration);
     37public:
    3438    using const_iterator = std::array<double, numberOfTemporalUnits>::const_iterator;
    3539
     40    Duration() = default;
     41    Duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
     42        : m_data {
     43            years,
     44            months,
     45            weeks,
     46            days,
     47            hours,
     48            minutes,
     49            seconds,
     50            milliseconds,
     51            microseconds,
     52            nanoseconds,
     53        }
     54    { }
     55
    3656#define JSC_DEFINE_ISO8601_DURATION_FIELD(name, capitalizedName) \
    37     double name##s() const { return data[static_cast<uint8_t>(TemporalUnit::capitalizedName)]; } \
    38     void set##capitalizedName##s(double value) { data[static_cast<uint8_t>(TemporalUnit::capitalizedName)] = value; }
     57    double name##s() const { return m_data[static_cast<uint8_t>(TemporalUnit::capitalizedName)]; } \
     58    void set##capitalizedName##s(double value) { m_data[static_cast<uint8_t>(TemporalUnit::capitalizedName)] = value; }
    3959    JSC_TEMPORAL_UNITS(JSC_DEFINE_ISO8601_DURATION_FIELD);
    4060#undef JSC_DEFINE_ISO8601_DURATION_FIELD
    4161
    42     double& operator[](size_t i) { return data[i]; }
    43     const double& operator[](size_t i) const { return data[i]; }
    44     const_iterator begin() const { return data.begin(); }
    45     const_iterator end() const { return data.end(); }
    46     void clear() { data.fill(0); }
     62    double& operator[](size_t i) { return m_data[i]; }
     63    const double& operator[](size_t i) const { return m_data[i]; }
     64    const_iterator begin() const { return m_data.begin(); }
     65    const_iterator end() const { return m_data.end(); }
     66    void clear() { m_data.fill(0); }
    4767
    48     std::array<double, numberOfTemporalUnits> data { };
     68    Duration operator-() const
     69    {
     70        Duration result(*this);
     71        for (auto& value : result.m_data)
     72            value = -value;
     73        return result;
     74    }
     75
     76private:
     77    std::array<double, numberOfTemporalUnits> m_data { };
    4978};
    5079
     80class PlainTime {
     81    WTF_MAKE_FAST_ALLOCATED(PlainTime);
     82public:
     83    constexpr PlainTime()
     84        : m_millisecond(0)
     85        , m_microsecond(0)
     86        , m_nanosecond(0)
     87    {
     88    }
     89
     90    constexpr PlainTime(unsigned hour, unsigned minute, unsigned second, unsigned millisecond, unsigned microsecond, unsigned nanosecond)
     91        : m_hour(hour)
     92        , m_minute(minute)
     93        , m_second(second)
     94        , m_millisecond(millisecond)
     95        , m_microsecond(microsecond)
     96        , m_nanosecond(nanosecond)
     97    { }
     98
     99#define JSC_DEFINE_ISO8601_PLAIN_TIME_FIELD(name, capitalizedName) \
     100    unsigned name() const { return m_##name; }
     101    JSC_TEMPORAL_PLAIN_TIME_UNITS(JSC_DEFINE_ISO8601_PLAIN_TIME_FIELD);
     102#undef JSC_DEFINE_ISO8601_DURATION_FIELD
     103
     104    friend bool operator==(PlainTime lhs, PlainTime rhs)
     105    {
     106        return lhs.hour() == rhs.hour()
     107            && lhs.minute() == rhs.minute()
     108            && lhs.second() == rhs.second()
     109            && lhs.millisecond() == rhs.millisecond()
     110            && lhs.microsecond() == rhs.microsecond()
     111            && lhs.nanosecond() == rhs.nanosecond();
     112    }
     113
     114private:
     115    uint8_t m_hour { 0 };
     116    uint8_t m_minute { 0 };
     117    uint8_t m_second { 0 };
     118    uint32_t m_millisecond : 10;
     119    uint32_t m_microsecond : 10;
     120    uint32_t m_nanosecond : 10;
     121};
     122static_assert(sizeof(PlainTime) <= sizeof(uint64_t));
     123
     124// Note that PlainDate does not include week unit.
     125// year can be negative. And month and day starts with 1.
     126class PlainDate {
     127    WTF_MAKE_FAST_ALLOCATED(PlainDate);
     128public:
     129    constexpr PlainDate() = default;
     130    constexpr PlainDate(int32_t year, unsigned month, unsigned day)
     131        : m_year(year)
     132        , m_month(month)
     133        , m_day(day)
     134    {
     135    }
     136
     137    int32_t year() const { return m_year; }
     138    uint8_t month() const { return m_month; }
     139    uint8_t day() const { return m_day; }
     140
     141private:
     142    int32_t m_year { 0 };
     143    uint8_t m_month { 1 };
     144    uint8_t m_day { 1 };
     145};
     146
     147using TimeZone = Variant<TimeZoneID, int64_t>;
     148
     149// https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimezonestring
     150// Record { [[Z]], [[OffsetString]], [[Name]] }
     151struct TimeZoneRecord {
     152    bool m_z { false };
     153    std::optional<int64_t> m_offset;
     154    Variant<Vector<LChar>, int64_t> m_nameOrOffset;
     155};
     156
     157// https://tc39.es/proposal-temporal/#sup-isvalidtimezonename
     158std::optional<TimeZoneID> parseTimeZoneName(StringView);
    51159std::optional<Duration> parseDuration(StringView);
    52160std::optional<int64_t> parseTimeZoneNumericUTCOffset(StringView);
     161enum class ValidateTimeZoneID { Yes, No };
     162std::optional<std::tuple<PlainTime, std::optional<TimeZoneRecord>>> parseTime(StringView);
     163std::optional<std::tuple<PlainDate, std::optional<PlainTime>, std::optional<TimeZoneRecord>>> parseDateTime(StringView);
    53164String formatTimeZoneOffsetString(int64_t);
     165String temporalTimeToString(PlainTime, std::tuple<Precision, unsigned> precision);
     166
     167bool isValidDuration(const Duration&);
    54168
    55169} // namespace ISO8601
  • trunk/Source/JavaScriptCore/runtime/IntlObject.cpp

    r282018 r282125  
    18401840}
    18411841
     1842TimeZoneID utcTimeZoneIDStorage { std::numeric_limits<TimeZoneID>::max() };
     1843TimeZoneID utcTimeZoneIDSlow()
     1844{
     1845    static std::once_flag initializeOnce;
     1846    std::call_once(initializeOnce, [&] {
     1847        auto& timeZones = intlAvailableTimeZones();
     1848        auto index = timeZones.find("UTC"_s);
     1849        RELEASE_ASSERT(index != WTF::notFound);
     1850        utcTimeZoneIDStorage = index;
     1851    });
     1852    return utcTimeZoneIDStorage;
     1853}
     1854
    18421855// https://tc39.es/proposal-intl-enumeration/#sec-availabletimezones
    18431856static JSArray* availableTimeZones(JSGlobalObject* globalObject)
  • trunk/Source/JavaScriptCore/runtime/IntlObject.h

    r282018 r282125  
    118118const Vector<String>& intlAvailableTimeZones();
    119119
     120extern TimeZoneID utcTimeZoneIDStorage;
     121TimeZoneID utcTimeZoneIDSlow();
     122CalendarID utcTimeZoneID();
     123
    120124TriState intlBooleanOption(JSGlobalObject*, JSObject* options, PropertyName);
    121125String intlStringOption(JSGlobalObject*, JSObject* options, PropertyName, std::initializer_list<const char*> values, const char* notFound, const char* fallback);
     
    165169std::optional<String> mapBCP47ToICUCalendarKeyword(const String&);
    166170
     171
     172inline CalendarID utcTimeZoneID()
     173{
     174    unsigned value = utcTimeZoneIDStorage;
     175    if (value == std::numeric_limits<TimeZoneID>::max())
     176        return utcTimeZoneIDSlow();
     177    return value;
     178}
     179
    167180} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp

    r282018 r282125  
    207207#include "TemporalDurationPrototype.h"
    208208#include "TemporalObject.h"
     209#include "TemporalPlainTime.h"
     210#include "TemporalPlainTimePrototype.h"
    209211#include "TemporalTimeZone.h"
    210212#include "TemporalTimeZonePrototype.h"
     
    12311233                TemporalDurationPrototype* durationPrototype = TemporalDurationPrototype::create(init.vm, TemporalDurationPrototype::createStructure(init.vm, globalObject, globalObject->objectPrototype()));
    12321234                init.set(TemporalDuration::createStructure(init.vm, globalObject, durationPrototype));
     1235            });
     1236
     1237        m_plainTimeStructure.initLater(
     1238            [] (const Initializer<Structure>& init) {
     1239                auto* globalObject = jsCast<JSGlobalObject*>(init.owner);
     1240                auto* plainTimePrototype = TemporalPlainTimePrototype::create(init.vm, globalObject, TemporalPlainTimePrototype::createStructure(init.vm, globalObject, globalObject->objectPrototype()));
     1241                init.set(TemporalPlainTime::createStructure(init.vm, globalObject, plainTimePrototype));
    12331242            });
    12341243
     
    21202129    thisObject->m_calendarStructure.visit(visitor);
    21212130    thisObject->m_durationStructure.visit(visitor);
     2131    thisObject->m_plainTimeStructure.visit(visitor);
    21222132    thisObject->m_timeZoneStructure.visit(visitor);
    21232133
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h

    r282018 r282125  
    327327    LazyProperty<JSGlobalObject, Structure> m_calendarStructure;
    328328    LazyProperty<JSGlobalObject, Structure> m_durationStructure;
     329    LazyProperty<JSGlobalObject, Structure> m_plainTimeStructure;
    329330    LazyProperty<JSGlobalObject, Structure> m_timeZoneStructure;
    330331
     
    877878    Structure* calendarStructure() { return m_calendarStructure.get(this); }
    878879    Structure* durationStructure() { return m_durationStructure.get(this); }
     880    Structure* plainTimeStructure() { return m_plainTimeStructure.get(this); }
    879881    Structure* timeZoneStructure() { return m_timeZoneStructure.get(this); }
    880882
  • trunk/Source/JavaScriptCore/runtime/TemporalCalendarConstructor.cpp

    r281788 r282125  
    7474    Base::finishCreation(vm, 0, "Calendar"_s, PropertyAdditionMode::WithoutStructureTransition);
    7575    putDirectWithoutTransition(vm, vm.propertyNames->prototype, temporalCalendarPrototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
     76    temporalCalendarPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, this, static_cast<unsigned>(PropertyAttribute::DontEnum));
    7677}
    7778
  • trunk/Source/JavaScriptCore/runtime/TemporalDuration.cpp

    r281986 r282125  
    7373}
    7474
    75 // IsValidDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds )
    76 // https://tc39.es/proposal-temporal/#sec-temporal-isvalidduration
    77 static bool isValidDuration(const TemporalDuration::Subdurations& subdurations)
    78 {
    79     int sign = 0;
    80     for (auto value : subdurations) {
    81         if (!std::isfinite(value) || (value < 0 && sign > 0) || (value > 0 && sign < 0))
    82             return false;
    83 
    84         if (!sign && value)
    85             sign = value > 0 ? 1 : -1;
    86     }
    87 
    88     return true;
    89 }
    90 
    9175// CreateTemporalDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds [ , newTarget ] )
    9276// https://tc39.es/proposal-temporal/#sec-temporal-createtemporalduration
     
    9680    auto scope = DECLARE_THROW_SCOPE(vm);
    9781
    98     if (!isValidDuration(subdurations)) {
     82    if (!ISO8601::isValidDuration(subdurations)) {
    9983        throwRangeError(globalObject, scope, "Temporal.Duration properties must be finite and of consistent sign"_s);
    10084        return { };
     
    10690// ToTemporalDurationRecord ( temporalDurationLike )
    10791// https://tc39.es/proposal-temporal/#sec-temporal-totemporaldurationrecord
    108 TemporalDuration::Subdurations TemporalDuration::fromObject(JSGlobalObject* globalObject, JSObject* durationLike)
    109 {
    110     VM& vm = globalObject->vm();
    111     auto scope = DECLARE_THROW_SCOPE(vm);
    112 
    113     ASSERT(!durationLike->inherits<TemporalDuration>(vm));
     92TemporalDuration::Subdurations TemporalDuration::fromNonDurationValue(JSGlobalObject* globalObject, JSValue durationLike)
     93{
     94    VM& vm = globalObject->vm();
     95    auto scope = DECLARE_THROW_SCOPE(vm);
     96
     97    ASSERT(!durationLike.inherits<TemporalDuration>(vm));
    11498
    11599    Subdurations result;
    116100    auto hasRelevantProperty = false;
    117101    for (size_t i = 0; i < numberOfTemporalUnits; i++) {
    118         JSValue value = durationLike->get(globalObject, propertyName(vm, i));
     102        JSValue value = durationLike.get(globalObject, propertyName(vm, i));
    119103        RETURN_IF_EXCEPTION(scope, { });
    120104
     
    153137
    154138    if (itemValue.isObject()) {
    155         auto subdurations = fromObject(globalObject, asObject(itemValue));
     139        auto subdurations = fromNonDurationValue(globalObject, itemValue);
    156140        RETURN_IF_EXCEPTION(scope, nullptr);
    157141
     
    169153
    170154    RELEASE_AND_RETURN(scope, TemporalDuration::tryCreateIfValid(globalObject, WTFMove(parsedSubdurations.value())));
     155}
     156
     157TemporalDuration::Subdurations TemporalDuration::toDurationRecord(JSGlobalObject* globalObject, JSValue temporalDurationLike)
     158{
     159    VM& vm = globalObject->vm();
     160
     161    if (temporalDurationLike.inherits<TemporalDuration>(vm))
     162        return jsCast<TemporalDuration*>(temporalDurationLike)->subdurations();
     163
     164    return fromNonDurationValue(globalObject, temporalDurationLike);
    171165}
    172166
     
    531525    PrecisionData data = secondsStringPrecision(globalObject, options);
    532526    RETURN_IF_EXCEPTION(scope, { });
    533     ASSERT(data.unit >= TemporalUnit::Second);
     527    if (data.unit < TemporalUnit::Second) {
     528        throwRangeError(globalObject, scope, "smallestUnit must not be \"minute\""_s);
     529        return { };
     530    }
    534531
    535532    auto roundingMode = intlOption<RoundingMode>(globalObject, options, vm.propertyNames->roundingMode,
     
    539536
    540537    // No need to make a new object if we were given explicit defaults.
    541     if (!data.precision && roundingMode == RoundingMode::Trunc)
     538    if (std::get<0>(data.precision) == Precision::Auto && roundingMode == RoundingMode::Trunc)
    542539        return toString();
    543540
     
    549546// TemporalDurationToString ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, precision )
    550547// https://tc39.es/proposal-temporal/#sec-temporal-temporaldurationtostring
    551 String TemporalDuration::toString(const TemporalDuration::Subdurations& subdurations, std::optional<unsigned> precision)
    552 {
    553     ASSERT(!precision || precision.value() < 10);
     548String TemporalDuration::toString(const TemporalDuration::Subdurations& subdurations, std::tuple<Precision, unsigned> precision)
     549{
     550    auto [precisionType, precisionValue] = precision;
     551    ASSERT(precisionType == Precision::Auto || precisionValue < 10);
    554552
    555553    auto balancedMicroseconds = subdurations.microseconds() + std::trunc(subdurations.nanoseconds() / 1000);
     
    598596
    599597        auto fraction = std::abs(balancedMilliseconds) * 1e6 + std::abs(balancedMicroseconds) * 1e3 + std::abs(balancedNanoseconds);
    600         if ((!precision && fraction) || (precision && precision.value())) {
     598        if ((precisionType == Precision::Auto && fraction) || (precisionType == Precision::Fixed && precisionValue)) {
    601599            auto padded = makeString('.', pad('0', 9, fraction));
    602             if (precision)
    603                 builder.append(StringView(padded).left(padded.length() - (9 - precision.value())));
     600            if (precisionType == Precision::Fixed)
     601                builder.append(StringView(padded).left(padded.length() - (9 - precisionValue)));
    604602            else {
    605603                auto lengthWithoutTrailingZeroes = padded.length();
  • trunk/Source/JavaScriptCore/runtime/TemporalDuration.h

    r281838 r282125  
    6767    double total(JSGlobalObject*, JSValue options) const;
    6868    String toString(JSGlobalObject*, JSValue options) const;
    69     String toString(std::optional<unsigned> precision = std::nullopt) const { return toString(m_subdurations, precision); }
     69    String toString(std::tuple<Precision, unsigned> precision = { Precision::Auto, 0 }) const { return toString(m_subdurations, precision); }
     70
     71    const Subdurations& subdurations() const { return m_subdurations; }
     72
     73    static Subdurations toDurationRecord(JSGlobalObject*, JSValue);
     74
     75    static int sign(const Subdurations&);
     76    static double round(Subdurations&, double increment, TemporalUnit, RoundingMode);
     77    static void balance(Subdurations&, TemporalUnit largestUnit);
    7078
    7179private:
     
    7583    template<typename CharacterType>
    7684    static std::optional<Subdurations> parse(StringParsingBuffer<CharacterType>&);
    77     static Subdurations fromObject(JSGlobalObject*, JSObject*);
     85    static Subdurations fromNonDurationValue(JSGlobalObject*, JSValue);
    7886
    79     static int sign(const Subdurations&);
    80     static void balance(Subdurations&, TemporalUnit largestUnit);
    81     static double round(Subdurations&, double increment, TemporalUnit, RoundingMode);
    82     static String toString(const Subdurations&, std::optional<unsigned> precision);
     87    static String toString(const Subdurations&, std::tuple<Precision, unsigned> precision);
    8388
    8489    TemporalUnit largestSubduration() const;
  • trunk/Source/JavaScriptCore/runtime/TemporalNow.cpp

    r282018 r282125  
    7878
    7979    String timeZoneString = vm.dateCache.defaultTimeZone();
    80     std::optional<TimeZoneID> identifier = TemporalTimeZone::idForTimeZoneName(timeZoneString);
     80    std::optional<TimeZoneID> identifier = ISO8601::parseTimeZoneName(timeZoneString);
    8181    if (!identifier)
    8282        return JSValue::encode(TemporalTimeZone::createFromUTCOffset(vm, globalObject->timeZoneStructure(), 0));
  • trunk/Source/JavaScriptCore/runtime/TemporalObject.cpp

    r282018 r282125  
    3434#include "TemporalDurationPrototype.h"
    3535#include "TemporalNow.h"
     36#include "TemporalPlainTimeConstructor.h"
     37#include "TemporalPlainTimePrototype.h"
    3638#include "TemporalTimeZoneConstructor.h"
    3739#include "TemporalTimeZonePrototype.h"
     
    6062    JSGlobalObject* globalObject = temporalObject->globalObject(vm);
    6163    return TemporalDurationConstructor::create(vm, TemporalDurationConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()), jsCast<TemporalDurationPrototype*>(globalObject->durationStructure()->storedPrototypeObject()));
     64}
     65
     66static JSValue createPlainTimeConstructor(VM& vm, JSObject* object)
     67{
     68    TemporalObject* temporalObject = jsCast<TemporalObject*>(object);
     69    auto* globalObject = temporalObject->globalObject(vm);
     70    return TemporalPlainTimeConstructor::create(vm, TemporalPlainTimeConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()), jsCast<TemporalPlainTimePrototype*>(globalObject->plainTimeStructure()->storedPrototypeObject()));
    6271}
    6372
     
    8089  Duration       createDurationConstructor       DontEnum|PropertyCallback
    8190  Now            createNowObject                 DontEnum|PropertyCallback
     91  PlainTime      createPlainTimeConstructor      DontEnum|PropertyCallback
    8292  TimeZone       createTimeZoneConstructor       DontEnum|PropertyCallback
    8393@end
     
    243253    auto scope = DECLARE_THROW_SCOPE(vm);
    244254
    245     auto smallestUnit = temporalSmallestUnit(globalObject, options, { TemporalUnit::Year, TemporalUnit::Month, TemporalUnit::Week, TemporalUnit::Day, TemporalUnit::Hour, TemporalUnit::Minute });
     255    auto smallestUnit = temporalSmallestUnit(globalObject, options, { TemporalUnit::Year, TemporalUnit::Month, TemporalUnit::Week, TemporalUnit::Day, TemporalUnit::Hour });
    246256    RETURN_IF_EXCEPTION(scope, { });
    247257
    248258    if (smallestUnit) {
    249259        switch (smallestUnit.value()) {
     260        case TemporalUnit::Minute:
     261            return { { Precision::Minute, 0 }, TemporalUnit::Minute, 1 };
    250262        case TemporalUnit::Second:
    251             return { 0, TemporalUnit::Second, 1 };
     263            return { { Precision::Fixed, 0 }, TemporalUnit::Second, 1 };
    252264        case TemporalUnit::Millisecond:
    253             return { 3, TemporalUnit::Millisecond, 1 };
     265            return { { Precision::Fixed, 3 }, TemporalUnit::Millisecond, 1 };
    254266        case TemporalUnit::Microsecond:
    255             return { 6, TemporalUnit::Microsecond, 1 };
     267            return { { Precision::Fixed, 6 }, TemporalUnit::Microsecond, 1 };
    256268        case TemporalUnit::Nanosecond:
    257             return { 9, TemporalUnit::Nanosecond, 1 };
     269            return { { Precision::Fixed, 9 }, TemporalUnit::Nanosecond, 1 };
    258270        default:
    259271            RELEASE_ASSERT_NOT_REACHED();
     
    266278
    267279    if (!precision)
    268         return { std::nullopt, TemporalUnit::Nanosecond, 1 };
     280        return { { Precision::Auto, 0 }, TemporalUnit::Nanosecond, 1 };
     281
     282    auto pow10Unsigned = [](unsigned n) -> unsigned {
     283        unsigned result = 1;
     284        for (unsigned i = 0; i < n; ++i)
     285            result *= 10;
     286        return result;
     287    };
    269288
    270289    unsigned digits = precision.value();
    271290    if (!digits)
    272         return { 0, TemporalUnit::Second, 1 };
     291        return { { Precision::Fixed, 0 }, TemporalUnit::Second, 1 };
    273292
    274293    if (digits <= 3)
    275         return { digits, TemporalUnit::Millisecond, std::pow(10, 3 - digits) };
     294        return { { Precision::Fixed, digits }, TemporalUnit::Millisecond, pow10Unsigned(3 - digits) };
    276295
    277296    if (digits <= 6)
    278         return { digits, TemporalUnit::Microsecond, std::pow(10, 6 - digits) };
     297        return { { Precision::Fixed, digits }, TemporalUnit::Microsecond, pow10Unsigned(6 - digits) };
    279298
    280299    ASSERT(digits <= 9);
    281     return { digits, TemporalUnit::Nanosecond, std::pow(10, 9 - digits) };
     300    return { { Precision::Fixed, digits }, TemporalUnit::Nanosecond, pow10Unsigned(9 - digits) };
    282301}
    283302
     
    343362}
    344363
     364TemporalOverflow toTemporalOverflow(JSGlobalObject* globalObject, JSObject* options)
     365{
     366    return intlOption<TemporalOverflow>(globalObject, options, globalObject->vm().propertyNames->overflow,
     367        { { "constrain"_s, TemporalOverflow::Constrain }, { "reject"_s, TemporalOverflow::Reject } },
     368        "overflow must be either \"constrain\" or \"reject\""_s, TemporalOverflow::Constrain);
     369}
     370
    345371} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/TemporalObject.h

    r281838 r282125  
    2222
    2323#include "JSObject.h"
     24#include <wtf/Variant.h>
    2425
    2526namespace JSC {
     27
     28#define JSC_TEMPORAL_PLAIN_TIME_UNITS(macro) \
     29    macro(hour, Hour) \
     30    macro(minute, Minute) \
     31    macro(second, Second) \
     32    macro(millisecond, Millisecond) \
     33    macro(microsecond, Microsecond) \
     34    macro(nanosecond, Nanosecond) \
     35
    2636
    2737#define JSC_TEMPORAL_UNITS(macro) \
     
    3040    macro(week, Week) \
    3141    macro(day, Day) \
    32     macro(hour, Hour) \
    33     macro(minute, Minute) \
    34     macro(second, Second) \
    35     macro(millisecond, Millisecond) \
    36     macro(microsecond, Microsecond) \
    37     macro(nanosecond, Nanosecond) \
     42    JSC_TEMPORAL_PLAIN_TIME_UNITS(macro) \
     43
    3844
    3945enum class TemporalUnit : uint8_t {
     
    4450#define JSC_COUNT_TEMPORAL_UNITS(name, capitalizedName) + 1
    4551static constexpr unsigned numberOfTemporalUnits = 0 JSC_TEMPORAL_UNITS(JSC_COUNT_TEMPORAL_UNITS);
     52static constexpr unsigned numberOfTemporalPlainTimeUnits = 0 JSC_TEMPORAL_PLAIN_TIME_UNITS(JSC_COUNT_TEMPORAL_UNITS);
    4653#undef JSC_COUNT_TEMPORAL_UNITS
    4754
     
    7582};
    7683
     84enum class Precision : uint8_t {
     85    Minute,
     86    Fixed,
     87    Auto,
     88};
     89
    7790struct PrecisionData {
    78     std::optional<unsigned> precision;
     91    std::tuple<Precision, unsigned> precision;
    7992    TemporalUnit unit;
    80     double increment;
     93    unsigned increment;
    8194};
    8295
     
    90103double roundNumberToIncrement(double, double increment, RoundingMode);
    91104
     105enum class TemporalOverflow : bool {
     106    Constrain,
     107    Reject,
     108};
     109
     110TemporalOverflow toTemporalOverflow(JSGlobalObject*, JSObject*);
     111
    92112} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/TemporalPlainTimeConstructor.h

    r282119 r282125  
    11/*
    2  * Copyright (C) 2021 Apple Inc. All rights reserved.
     2 * Copyright (C) 2021 Apple Inc.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2626#pragma once
    2727
    28 #include "IntlObject.h"
    29 #include "JSObject.h"
    30 #include <wtf/Variant.h>
     28#include "InternalFunction.h"
    3129
    3230namespace JSC {
    3331
    34 class TemporalTimeZone final : public JSNonFinalObject {
     32class TemporalPlainTimePrototype;
     33
     34class TemporalPlainTimeConstructor final : public InternalFunction {
    3535public:
    36     using Base = JSNonFinalObject;
     36    using Base = InternalFunction;
     37    static constexpr unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable;
    3738
    38     template<typename CellType, SubspaceAccess mode>
    39     static IsoSubspace* subspaceFor(VM& vm)
    40     {
    41         return vm.temporalTimeZoneSpace<mode>();
    42     }
    43 
    44     static TemporalTimeZone* createFromID(VM&, Structure*, TimeZoneID);
    45     static TemporalTimeZone* createFromUTCOffset(VM&, Structure*, int64_t);
     39    static TemporalPlainTimeConstructor* create(VM&, Structure*, TemporalPlainTimePrototype*);
    4640    static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
    4741
    4842    DECLARE_INFO;
    4943
    50     using TimeZone = Variant<TimeZoneID, int64_t>;
    51     TimeZone timeZone() const { return m_timeZone; }
    52 
    53     static std::optional<TimeZoneID> idForTimeZoneName(StringView);
    54 
    55     static JSObject* from(JSGlobalObject*, JSValue);
    56 
    5744private:
    58     TemporalTimeZone(VM&, Structure*, TimeZone);
    59 
    60     // TimeZoneID or UTC offset.
    61     TimeZone m_timeZone;
     45    TemporalPlainTimeConstructor(VM&, Structure*);
     46    void finishCreation(VM&, TemporalPlainTimePrototype*);
    6247};
     48STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(TemporalPlainTimeConstructor, InternalFunction);
    6349
    6450} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/TemporalPlainTimePrototype.h

    r282119 r282125  
    11/*
    2  * Copyright (C) 2021 Apple Inc. All rights reserved.
     2 * Copyright (C) 2021 Apple Inc.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2626#pragma once
    2727
    28 #include "IntlObject.h"
    2928#include "JSObject.h"
    30 #include <wtf/Variant.h>
    3129
    3230namespace JSC {
    3331
    34 class TemporalTimeZone final : public JSNonFinalObject {
     32class TemporalPlainTimePrototype final : public JSNonFinalObject {
    3533public:
    3634    using Base = JSNonFinalObject;
     35    static constexpr unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable;
    3736
    38     template<typename CellType, SubspaceAccess mode>
     37    template<typename CellType, SubspaceAccess>
    3938    static IsoSubspace* subspaceFor(VM& vm)
    4039    {
    41         return vm.temporalTimeZoneSpace<mode>();
     40        STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(TemporalPlainTimePrototype, Base);
     41        return &vm.plainObjectSpace;
    4242    }
    4343
    44     static TemporalTimeZone* createFromID(VM&, Structure*, TimeZoneID);
    45     static TemporalTimeZone* createFromUTCOffset(VM&, Structure*, int64_t);
     44    static TemporalPlainTimePrototype* create(VM&, JSGlobalObject*, Structure*);
    4645    static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
    4746
    4847    DECLARE_INFO;
    4948
    50     using TimeZone = Variant<TimeZoneID, int64_t>;
    51     TimeZone timeZone() const { return m_timeZone; }
    52 
    53     static std::optional<TimeZoneID> idForTimeZoneName(StringView);
    54 
    55     static JSObject* from(JSGlobalObject*, JSValue);
    56 
    5749private:
    58     TemporalTimeZone(VM&, Structure*, TimeZone);
    59 
    60     // TimeZoneID or UTC offset.
    61     TimeZone m_timeZone;
     50    TemporalPlainTimePrototype(VM&, Structure*);
     51    void finishCreation(VM&, JSGlobalObject*);
    6252};
    6353
  • trunk/Source/JavaScriptCore/runtime/TemporalTimeZone.cpp

    r282080 r282125  
    5959}
    6060
    61 // https://tc39.es/proposal-temporal/#sup-isvalidtimezonename
    62 std::optional<TimeZoneID> TemporalTimeZone::idForTimeZoneName(StringView string)
    63 {
    64     const auto& timeZones = intlAvailableTimeZones();
    65     for (unsigned index = 0; index < timeZones.size(); ++index) {
    66         if (equalIgnoringASCIICase(timeZones[index], string))
    67             return index;
    68     }
    69     return std::nullopt;
    70 }
    71 
    7261// https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimeZonestring
    7362static std::optional<int64_t> parseTemporalTimeZoneString(StringView)
     
    112101        return TemporalTimeZone::createFromUTCOffset(vm, globalObject->timeZoneStructure(), utcOffset.value());
    113102
    114     std::optional<TimeZoneID> identifier = idForTimeZoneName(timeZoneString);
     103    std::optional<TimeZoneID> identifier = ISO8601::parseTimeZoneName(timeZoneString);
    115104    if (identifier)
    116105        return TemporalTimeZone::createFromID(vm, globalObject->timeZoneStructure(), identifier.value());
  • trunk/Source/JavaScriptCore/runtime/TemporalTimeZone.h

    r282018 r282125  
    2626#pragma once
    2727
     28#include "ISO8601.h"
    2829#include "IntlObject.h"
    2930#include "JSObject.h"
     
    4849    DECLARE_INFO;
    4950
    50     using TimeZone = Variant<TimeZoneID, int64_t>;
     51    using TimeZone = ISO8601::TimeZone;
     52
    5153    TimeZone timeZone() const { return m_timeZone; }
    52 
    53     static std::optional<TimeZoneID> idForTimeZoneName(StringView);
    5454
    5555    static JSObject* from(JSGlobalObject*, JSValue);
  • trunk/Source/JavaScriptCore/runtime/TemporalTimeZoneConstructor.cpp

    r282018 r282125  
    7575    Base::finishCreation(vm, 0, "TimeZone"_s, PropertyAdditionMode::WithoutStructureTransition);
    7676    putDirectWithoutTransition(vm, vm.propertyNames->prototype, temporalTimeZonePrototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
     77    temporalTimeZonePrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, this, static_cast<unsigned>(PropertyAttribute::DontEnum));
    7778}
    7879
     
    9394        return JSValue::encode(TemporalTimeZone::createFromUTCOffset(vm, structure, utcOffset.value()));
    9495
    95     std::optional<TimeZoneID> identifier = TemporalTimeZone::idForTimeZoneName(timeZoneString);
     96    std::optional<TimeZoneID> identifier = ISO8601::parseTimeZoneName(timeZoneString);
    9697    if (!identifier) {
    9798        throwRangeError(globalObject, scope, "argument needs to be UTC offset string or TimeZone identifier"_s);
  • trunk/Source/JavaScriptCore/runtime/VM.cpp

    r282018 r282125  
    165165#include "TemporalCalendar.h"
    166166#include "TemporalDuration.h"
     167#include "TemporalPlainTime.h"
    167168#include "TemporalTimeZone.h"
    168169#include "TestRunnerUtils.h"
     
    15891590DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(templateObjectDescriptorSpace, destructibleCellHeapCellType.get(), JSTemplateObjectDescriptor)
    15901591DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(temporalCalendarSpace, cellHeapCellType.get(), TemporalCalendar)
     1592DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(temporalDurationSpace, cellHeapCellType.get(), TemporalDuration)
     1593DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(temporalPlainTimeSpace, cellHeapCellType.get(), TemporalPlainTime)
    15911594DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(temporalTimeZoneSpace, cellHeapCellType.get(), TemporalTimeZone)
    15921595DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(uint8ArraySpace, cellHeapCellType.get(), JSUint8Array)
     
    16231626DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(intlSegmenterSpace, intlSegmenterHeapCellType.get(), IntlSegmenter)
    16241627DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(intlSegmentsSpace, intlSegmentsHeapCellType.get(), IntlSegments)
    1625 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(temporalDurationSpace, cellHeapCellType.get(), TemporalDuration)
    16261628#if ENABLE(WEBASSEMBLY)
    16271629DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(jsToWasmICCalleeSpace, cellHeapCellType.get(), JSToWasmICCallee)
  • trunk/Source/JavaScriptCore/runtime/VM.h

    r282018 r282125  
    196196class VMEntryScope;
    197197class TemporalCalendar;
     198class TemporalPlainTime;
    198199class TemporalTimeZone;
    199200class TopLevelGlobalObjectScope;
     
    596597    DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(templateObjectDescriptorSpace)
    597598    DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(temporalCalendarSpace)
     599    DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(temporalDurationSpace)
     600    DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(temporalPlainTimeSpace)
    598601    DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(temporalTimeZoneSpace)
    599602    DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(uint8ArraySpace)
     
    621624    DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(intlSegmenterSpace)
    622625    DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(intlSegmentsSpace)
    623     DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(temporalDurationSpace)
    624626#if ENABLE(WEBASSEMBLY)
    625627    DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(jsToWasmICCalleeSpace)
  • trunk/Source/WTF/ChangeLog

    r282115 r282125  
     12021-09-07  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] Implement Temporal.PlainTime
     4        https://bugs.webkit.org/show_bug.cgi?id=229892
     5
     6        Reviewed by Darin Adler.
     7
     8        * wtf/text/IntegerToStringConversion.h:
     9        * wtf/text/StringParsingBuffer.h:
     10
    1112021-09-07  Cameron McCormack  <heycam@apple.com>
    212
  • trunk/Source/WTF/wtf/text/IntegerToStringConversion.h

    r282018 r282125  
    137137} // namespace WTF
    138138
     139using WTF::numberToStringSigned;
     140using WTF::numberToStringUnsigned;
    139141using WTF::lengthOfIntegerAsString;
    140142using WTF::writeIntegerToBuffer;
  • trunk/Source/WTF/wtf/text/StringParsingBuffer.h

    r264630 r282125  
    6262    constexpr unsigned lengthRemaining() const { return m_end - m_position; }
    6363
    64     StringView stringViewOfCharactersRemaining() { return { m_position, lengthRemaining() }; }
     64    StringView stringViewOfCharactersRemaining() const { return { m_position, lengthRemaining() }; }
    6565
    66     CharacterType operator[](unsigned i)
     66    CharacterType operator[](unsigned i) const
    6767    {
    6868        ASSERT(i < lengthRemaining());
Note: See TracChangeset for help on using the changeset viewer.