Changeset 282125 in webkit
- Timestamp:
- Sep 7, 2021 7:20:36 PM (11 months ago)
- Location:
- trunk
- Files:
-
- 5 added
- 32 edited
- 2 copied
-
JSTests/ChangeLog (modified) (1 diff)
-
JSTests/stress/temporal-calendar.js (modified) (1 diff)
-
JSTests/stress/temporal-duration.js (modified) (1 diff)
-
JSTests/stress/temporal-plaintime.js (added)
-
JSTests/stress/temporal-timezone.js (modified) (3 diffs)
-
JSTests/test262/config.yaml (modified) (1 diff)
-
Source/JavaScriptCore/CMakeLists.txt (modified) (1 diff)
-
Source/JavaScriptCore/ChangeLog (modified) (1 diff)
-
Source/JavaScriptCore/DerivedSources-input.xcfilelist (modified) (1 diff)
-
Source/JavaScriptCore/DerivedSources-output.xcfilelist (modified) (1 diff)
-
Source/JavaScriptCore/DerivedSources.make (modified) (1 diff)
-
Source/JavaScriptCore/Sources.txt (modified) (1 diff)
-
Source/JavaScriptCore/runtime/CommonIdentifiers.h (modified) (5 diffs)
-
Source/JavaScriptCore/runtime/ISO8601.cpp (modified) (12 diffs)
-
Source/JavaScriptCore/runtime/ISO8601.h (modified) (3 diffs)
-
Source/JavaScriptCore/runtime/IntlObject.cpp (modified) (1 diff)
-
Source/JavaScriptCore/runtime/IntlObject.h (modified) (2 diffs)
-
Source/JavaScriptCore/runtime/JSGlobalObject.cpp (modified) (3 diffs)
-
Source/JavaScriptCore/runtime/JSGlobalObject.h (modified) (2 diffs)
-
Source/JavaScriptCore/runtime/TemporalCalendarConstructor.cpp (modified) (1 diff)
-
Source/JavaScriptCore/runtime/TemporalDuration.cpp (modified) (9 diffs)
-
Source/JavaScriptCore/runtime/TemporalDuration.h (modified) (2 diffs)
-
Source/JavaScriptCore/runtime/TemporalNow.cpp (modified) (1 diff)
-
Source/JavaScriptCore/runtime/TemporalObject.cpp (modified) (6 diffs)
-
Source/JavaScriptCore/runtime/TemporalObject.h (modified) (5 diffs)
-
Source/JavaScriptCore/runtime/TemporalPlainTime.cpp (added)
-
Source/JavaScriptCore/runtime/TemporalPlainTime.h (added)
-
Source/JavaScriptCore/runtime/TemporalPlainTimeConstructor.cpp (added)
-
Source/JavaScriptCore/runtime/TemporalPlainTimeConstructor.h (copied) (copied from trunk/Source/JavaScriptCore/runtime/TemporalTimeZone.h) (2 diffs)
-
Source/JavaScriptCore/runtime/TemporalPlainTimePrototype.cpp (added)
-
Source/JavaScriptCore/runtime/TemporalPlainTimePrototype.h (copied) (copied from trunk/Source/JavaScriptCore/runtime/TemporalTimeZone.h) (2 diffs)
-
Source/JavaScriptCore/runtime/TemporalTimeZone.cpp (modified) (2 diffs)
-
Source/JavaScriptCore/runtime/TemporalTimeZone.h (modified) (2 diffs)
-
Source/JavaScriptCore/runtime/TemporalTimeZoneConstructor.cpp (modified) (2 diffs)
-
Source/JavaScriptCore/runtime/VM.cpp (modified) (3 diffs)
-
Source/JavaScriptCore/runtime/VM.h (modified) (3 diffs)
-
Source/WTF/ChangeLog (modified) (1 diff)
-
Source/WTF/wtf/text/IntegerToStringConversion.h (modified) (1 diff)
-
Source/WTF/wtf/text/StringParsingBuffer.h (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r282081 r282125 1 2021-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 1 24 2021-09-07 Yusuke Suzuki <ysuzuki@apple.com> 2 25 -
trunk/JSTests/stress/temporal-calendar.js
r281788 r282125 18 18 shouldBe(String(error), message); 19 19 } 20 21 shouldBe(Temporal.Calendar instanceof Function, true); 22 shouldBe(Temporal.Calendar.length, 0); 23 shouldBe(Object.getOwnPropertyDescriptor(Temporal.Calendar, 'prototype').writable, false); 24 shouldBe(Object.getOwnPropertyDescriptor(Temporal.Calendar, 'prototype').enumerable, false); 25 shouldBe(Object.getOwnPropertyDescriptor(Temporal.Calendar, 'prototype').configurable, false); 26 shouldBe(Temporal.Calendar.prototype.constructor, Temporal.Calendar); 20 27 21 28 { -
trunk/JSTests/stress/temporal-duration.js
r281838 r282125 28 28 shouldBe(Object.getOwnPropertyDescriptor(Temporal.Duration, 'prototype').configurable, false); 29 29 shouldThrow(() => Temporal.Duration(), TypeError); 30 shouldBe(Temporal.Duration.prototype.constructor, Temporal.Duration); 30 31 31 32 shouldBe(new Temporal.Duration() instanceof Temporal.Duration, true); -
trunk/JSTests/stress/temporal-timezone.js
r282018 r282125 19 19 } 20 20 21 shouldBe(Temporal.TimeZone instanceof Function, true); 22 shouldBe(Temporal.TimeZone.length, 0); 23 shouldBe(Object.getOwnPropertyDescriptor(Temporal.TimeZone, 'prototype').writable, false); 24 shouldBe(Object.getOwnPropertyDescriptor(Temporal.TimeZone, 'prototype').enumerable, false); 25 shouldBe(Object.getOwnPropertyDescriptor(Temporal.TimeZone, 'prototype').configurable, false); 26 shouldBe(Temporal.TimeZone.prototype.constructor, Temporal.TimeZone); 27 21 28 let failures = [ 22 29 "", … … 27 34 "+23:", 28 35 "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", 29 48 "+20:0000", 30 49 "+20:60", 31 50 "+20:59:", 51 "+20:59:60", 32 52 "2059:00", 33 53 "+2059:00", … … 53 73 shouldBe(new Temporal.TimeZone("-01:59:30.000000001").id, `-01:59:30.000000001`); 54 74 shouldBe(new Temporal.TimeZone("-01:59:30.123456789").id, `-01:59:30.123456789`); 75 shouldBe(new Temporal.TimeZone("-01:59:30.001002003").id, `-01:59:30.001002003`); 76 shouldBe(new Temporal.TimeZone("-01:59:30.000000000").id, `-01:59:30`); 77 shouldBe(new Temporal.TimeZone("-01:59:00.000000000").id, `-01:59`); 78 shouldBe(new Temporal.TimeZone("-01:00:00.000000000").id, `-01:00`); 55 79 56 80 let tz = new Temporal.TimeZone("-01:59:30.123456789") -
trunk/JSTests/test262/config.yaml
r281838 r282125 28 28 - test/built-ins/Temporal/PlainDateTime 29 29 - 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 31 32 - test/built-ins/Temporal/PlainYearMonth 32 33 - test/built-ins/Temporal/TimeZone -
trunk/Source/JavaScriptCore/CMakeLists.txt
r282018 r282125 114 114 runtime/TemporalNow.cpp 115 115 runtime/TemporalObject.cpp 116 runtime/TemporalPlainTimeConstructor.cpp 117 runtime/TemporalPlainTimePrototype.cpp 116 118 runtime/TemporalTimeZoneConstructor.cpp 117 119 runtime/TemporalTimeZonePrototype.cpp -
trunk/Source/JavaScriptCore/ChangeLog
r282081 r282125 1 2021-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 1 145 2021-09-07 Yusuke Suzuki <ysuzuki@apple.com> 2 146 -
trunk/Source/JavaScriptCore/DerivedSources-input.xcfilelist
r282018 r282125 176 176 $(PROJECT_DIR)/runtime/TemporalNow.cpp 177 177 $(PROJECT_DIR)/runtime/TemporalObject.cpp 178 $(PROJECT_DIR)/runtime/TemporalPlainTimeConstructor.cpp 179 $(PROJECT_DIR)/runtime/TemporalPlainTimePrototype.cpp 178 180 $(PROJECT_DIR)/runtime/TemporalTimeZoneConstructor.cpp 179 181 $(PROJECT_DIR)/runtime/TemporalTimeZonePrototype.cpp -
trunk/Source/JavaScriptCore/DerivedSources-output.xcfilelist
r282018 r282125 73 73 $(BUILT_PRODUCTS_DIR)/DerivedSources/JavaScriptCore/TemporalNow.lut.h 74 74 $(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 75 77 $(BUILT_PRODUCTS_DIR)/DerivedSources/JavaScriptCore/TemporalTimeZoneConstructor.lut.h 76 78 $(BUILT_PRODUCTS_DIR)/DerivedSources/JavaScriptCore/TemporalTimeZonePrototype.lut.h -
trunk/Source/JavaScriptCore/DerivedSources.make
r282018 r282125 198 198 TemporalNow.lut.h \ 199 199 TemporalObject.lut.h \ 200 TemporalPlainTimeConstructor.lut.h \ 201 TemporalPlainTimePrototype.lut.h \ 200 202 TemporalTimeZoneConstructor.lut.h \ 201 203 TemporalTimeZonePrototype.lut.h \ -
trunk/Source/JavaScriptCore/Sources.txt
r282018 r282125 1020 1020 runtime/TemporalNow.cpp 1021 1021 runtime/TemporalObject.cpp 1022 runtime/TemporalPlainTime.cpp 1023 runtime/TemporalPlainTimeConstructor.cpp 1024 runtime/TemporalPlainTimePrototype.cpp 1022 1025 runtime/TemporalTimeZone.cpp 1023 1026 runtime/TemporalTimeZoneConstructor.cpp -
trunk/Source/JavaScriptCore/runtime/CommonIdentifiers.h
r281838 r282125 150 150 macro(inferredName) \ 151 151 macro(input) \ 152 macro(isoHour) \ 153 macro(isoMicrosecond) \ 154 macro(isoMillisecond) \ 155 macro(isoMinute) \ 156 macro(isoNanosecond) \ 157 macro(isoSecond) \ 152 158 macro(instructionCount) \ 153 159 macro(isArray) \ … … 170 176 macro(maximumSignificantDigits) \ 171 177 macro(message) \ 178 macro(microsecond) \ 172 179 macro(microseconds) \ 180 macro(millisecond) \ 173 181 macro(milliseconds) \ 174 182 macro(minimumFractionDigits) \ … … 182 190 macro(multiline) \ 183 191 macro(name) \ 192 macro(nanosecond) \ 184 193 macro(nanoseconds) \ 185 194 macro(next) \ … … 195 204 macro(osrExitSites) \ 196 205 macro(osrExits) \ 206 macro(overflow) \ 197 207 macro(parse) \ 198 208 macro(parseInt) \ … … 246 256 macro(value) \ 247 257 macro(valueOf) \ 258 macro(week) \ 248 259 macro(weekday) \ 249 260 macro(weeks) \ -
trunk/Source/JavaScriptCore/runtime/ISO8601.cpp
r282018 r282125 1 1 /* 2 2 * Copyright (C) 2021 Sony Interactive Entertainment Inc. 3 * Copyright (C) 2021 Apple Inc. 3 4 * 4 5 * Redistribution and use in source and binary forms, with or without … … 27 28 #include "ISO8601.h" 28 29 30 #include "IntlObject.h" 29 31 #include "ParseInt.h" 32 #include <wtf/DateMath.h> 30 33 #include <wtf/text/StringParsingBuffer.h> 34 #include <wtf/unicode/CharacterNames.h> 31 35 32 36 namespace JSC { … … 39 43 static constexpr int64_t nsPerMicrosecond = 1000LL; 40 44 45 std::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 41 55 template<typename CharType> 42 int32_t parseDecimalInt32(const CharType* characters, unsigned length)56 static int32_t parseDecimalInt32(const CharType* characters, unsigned length) 43 57 { 44 58 int32_t result = 0; … … 120 134 if (*buffer == '+') 121 135 buffer.advance(); 122 else if (*buffer == '-' || *buffer == 0x2212) {136 else if (*buffer == '-' || *buffer == minusSign) { 123 137 factor = -1; 124 138 buffer.advance(); … … 244 258 } 245 259 260 261 enum class Second60Mode { Accept, Reject }; 262 template<typename CharacterType> 263 static 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 246 380 template<typename CharacterType> 247 381 static std::optional<int64_t> parseTimeZoneNumericUTCOffset(StringParsingBuffer<CharacterType>& buffer) … … 252 386 // TimeZoneUTCOffsetSign TimeZoneUTCOffsetHour TimeZoneUTCOffsetMinute 253 387 // 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 255 392 // 256 393 // Maximum and minimum values are ±23:59:59.999999999 = ±86399999999999ns, which can be represented by int64_t / double's integer part. … … 263 400 if (*buffer == '+') 264 401 buffer.advance(); 265 else if (*buffer == '-' || *buffer == 0x2212) {402 else if (*buffer == '-' || *buffer == minusSign) { 266 403 factor = -1; 267 404 buffer.advance(); … … 269 406 return std::nullopt; 270 407 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 422 std::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 432 template<typename CharacterType> 433 static 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 584 template<typename CharacterType> 585 static 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 613 template<typename CharacterType> 614 static 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 661 template<typename CharacterType> 662 static 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 682 static 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 706 template<typename CharacterType> 707 static 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) 281 797 return std::nullopt; 282 798 buffer.advance(); … … 284 800 return std::nullopt; 285 801 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 == '-') 315 804 buffer.advance(); 316 805 else … … 318 807 } 319 808 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; 331 819 buffer.advance(); 332 820 } else 333 821 return std::nullopt; 334 822 823 return PlainDate(year, month, day); 824 } 825 826 template<typename CharacterType> 827 static 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; 335 838 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 860 std::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 870 std::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; 366 877 }); 367 878 } … … 382 893 if (nanoseconds) { 383 894 // 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); 386 896 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; 389 902 break; 390 } 391 fraction.resize(index); 903 } 904 } 905 if (validLength) 906 fraction.resize(validLength.value()); 907 else 908 fraction.clear(); 392 909 return makeString(negative ? '-' : '+', pad('0', 2, hours), ':', pad('0', 2, minutes), ':', pad('0', 2, seconds), '.', pad('0', paddingLength, emptyString()), fraction); 393 910 } … … 397 914 } 398 915 916 String 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 958 bool 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 399 972 } // namespace ISO8601 400 973 } // namespace JSC -
trunk/Source/JavaScriptCore/runtime/ISO8601.h
r282018 r282125 1 1 /* 2 2 * Copyright (C) 2021 Sony Interactive Entertainment Inc. 3 * Copyright (C) 2021 Apple Inc. 3 4 * 4 5 * Redistribution and use in source and binary forms, with or without … … 26 27 #pragma once 27 28 29 #include "IntlObject.h" 28 30 #include "TemporalObject.h" 29 31 … … 31 33 namespace ISO8601 { 32 34 33 struct Duration { 35 class Duration { 36 WTF_MAKE_FAST_ALLOCATED(Duration); 37 public: 34 38 using const_iterator = std::array<double, numberOfTemporalUnits>::const_iterator; 35 39 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 36 56 #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; } 39 59 JSC_TEMPORAL_UNITS(JSC_DEFINE_ISO8601_DURATION_FIELD); 40 60 #undef JSC_DEFINE_ISO8601_DURATION_FIELD 41 61 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); } 47 67 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 76 private: 77 std::array<double, numberOfTemporalUnits> m_data { }; 49 78 }; 50 79 80 class PlainTime { 81 WTF_MAKE_FAST_ALLOCATED(PlainTime); 82 public: 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 114 private: 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 }; 122 static_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. 126 class PlainDate { 127 WTF_MAKE_FAST_ALLOCATED(PlainDate); 128 public: 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 141 private: 142 int32_t m_year { 0 }; 143 uint8_t m_month { 1 }; 144 uint8_t m_day { 1 }; 145 }; 146 147 using TimeZone = Variant<TimeZoneID, int64_t>; 148 149 // https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimezonestring 150 // Record { [[Z]], [[OffsetString]], [[Name]] } 151 struct 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 158 std::optional<TimeZoneID> parseTimeZoneName(StringView); 51 159 std::optional<Duration> parseDuration(StringView); 52 160 std::optional<int64_t> parseTimeZoneNumericUTCOffset(StringView); 161 enum class ValidateTimeZoneID { Yes, No }; 162 std::optional<std::tuple<PlainTime, std::optional<TimeZoneRecord>>> parseTime(StringView); 163 std::optional<std::tuple<PlainDate, std::optional<PlainTime>, std::optional<TimeZoneRecord>>> parseDateTime(StringView); 53 164 String formatTimeZoneOffsetString(int64_t); 165 String temporalTimeToString(PlainTime, std::tuple<Precision, unsigned> precision); 166 167 bool isValidDuration(const Duration&); 54 168 55 169 } // namespace ISO8601 -
trunk/Source/JavaScriptCore/runtime/IntlObject.cpp
r282018 r282125 1840 1840 } 1841 1841 1842 TimeZoneID utcTimeZoneIDStorage { std::numeric_limits<TimeZoneID>::max() }; 1843 TimeZoneID 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 1842 1855 // https://tc39.es/proposal-intl-enumeration/#sec-availabletimezones 1843 1856 static JSArray* availableTimeZones(JSGlobalObject* globalObject) -
trunk/Source/JavaScriptCore/runtime/IntlObject.h
r282018 r282125 118 118 const Vector<String>& intlAvailableTimeZones(); 119 119 120 extern TimeZoneID utcTimeZoneIDStorage; 121 TimeZoneID utcTimeZoneIDSlow(); 122 CalendarID utcTimeZoneID(); 123 120 124 TriState intlBooleanOption(JSGlobalObject*, JSObject* options, PropertyName); 121 125 String intlStringOption(JSGlobalObject*, JSObject* options, PropertyName, std::initializer_list<const char*> values, const char* notFound, const char* fallback); … … 165 169 std::optional<String> mapBCP47ToICUCalendarKeyword(const String&); 166 170 171 172 inline CalendarID utcTimeZoneID() 173 { 174 unsigned value = utcTimeZoneIDStorage; 175 if (value == std::numeric_limits<TimeZoneID>::max()) 176 return utcTimeZoneIDSlow(); 177 return value; 178 } 179 167 180 } // namespace JSC -
trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
r282018 r282125 207 207 #include "TemporalDurationPrototype.h" 208 208 #include "TemporalObject.h" 209 #include "TemporalPlainTime.h" 210 #include "TemporalPlainTimePrototype.h" 209 211 #include "TemporalTimeZone.h" 210 212 #include "TemporalTimeZonePrototype.h" … … 1231 1233 TemporalDurationPrototype* durationPrototype = TemporalDurationPrototype::create(init.vm, TemporalDurationPrototype::createStructure(init.vm, globalObject, globalObject->objectPrototype())); 1232 1234 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)); 1233 1242 }); 1234 1243 … … 2120 2129 thisObject->m_calendarStructure.visit(visitor); 2121 2130 thisObject->m_durationStructure.visit(visitor); 2131 thisObject->m_plainTimeStructure.visit(visitor); 2122 2132 thisObject->m_timeZoneStructure.visit(visitor); 2123 2133 -
trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h
r282018 r282125 327 327 LazyProperty<JSGlobalObject, Structure> m_calendarStructure; 328 328 LazyProperty<JSGlobalObject, Structure> m_durationStructure; 329 LazyProperty<JSGlobalObject, Structure> m_plainTimeStructure; 329 330 LazyProperty<JSGlobalObject, Structure> m_timeZoneStructure; 330 331 … … 877 878 Structure* calendarStructure() { return m_calendarStructure.get(this); } 878 879 Structure* durationStructure() { return m_durationStructure.get(this); } 880 Structure* plainTimeStructure() { return m_plainTimeStructure.get(this); } 879 881 Structure* timeZoneStructure() { return m_timeZoneStructure.get(this); } 880 882 -
trunk/Source/JavaScriptCore/runtime/TemporalCalendarConstructor.cpp
r281788 r282125 74 74 Base::finishCreation(vm, 0, "Calendar"_s, PropertyAdditionMode::WithoutStructureTransition); 75 75 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)); 76 77 } 77 78 -
trunk/Source/JavaScriptCore/runtime/TemporalDuration.cpp
r281986 r282125 73 73 } 74 74 75 // IsValidDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds )76 // https://tc39.es/proposal-temporal/#sec-temporal-isvalidduration77 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 91 75 // CreateTemporalDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds [ , newTarget ] ) 92 76 // https://tc39.es/proposal-temporal/#sec-temporal-createtemporalduration … … 96 80 auto scope = DECLARE_THROW_SCOPE(vm); 97 81 98 if (! isValidDuration(subdurations)) {82 if (!ISO8601::isValidDuration(subdurations)) { 99 83 throwRangeError(globalObject, scope, "Temporal.Duration properties must be finite and of consistent sign"_s); 100 84 return { }; … … 106 90 // ToTemporalDurationRecord ( temporalDurationLike ) 107 91 // https://tc39.es/proposal-temporal/#sec-temporal-totemporaldurationrecord 108 TemporalDuration::Subdurations TemporalDuration::from Object(JSGlobalObject* globalObject, JSObject*durationLike)109 { 110 VM& vm = globalObject->vm(); 111 auto scope = DECLARE_THROW_SCOPE(vm); 112 113 ASSERT(!durationLike ->inherits<TemporalDuration>(vm));92 TemporalDuration::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)); 114 98 115 99 Subdurations result; 116 100 auto hasRelevantProperty = false; 117 101 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)); 119 103 RETURN_IF_EXCEPTION(scope, { }); 120 104 … … 153 137 154 138 if (itemValue.isObject()) { 155 auto subdurations = from Object(globalObject, asObject(itemValue));139 auto subdurations = fromNonDurationValue(globalObject, itemValue); 156 140 RETURN_IF_EXCEPTION(scope, nullptr); 157 141 … … 169 153 170 154 RELEASE_AND_RETURN(scope, TemporalDuration::tryCreateIfValid(globalObject, WTFMove(parsedSubdurations.value()))); 155 } 156 157 TemporalDuration::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); 171 165 } 172 166 … … 531 525 PrecisionData data = secondsStringPrecision(globalObject, options); 532 526 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 } 534 531 535 532 auto roundingMode = intlOption<RoundingMode>(globalObject, options, vm.propertyNames->roundingMode, … … 539 536 540 537 // 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) 542 539 return toString(); 543 540 … … 549 546 // TemporalDurationToString ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, precision ) 550 547 // 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); 548 String TemporalDuration::toString(const TemporalDuration::Subdurations& subdurations, std::tuple<Precision, unsigned> precision) 549 { 550 auto [precisionType, precisionValue] = precision; 551 ASSERT(precisionType == Precision::Auto || precisionValue < 10); 554 552 555 553 auto balancedMicroseconds = subdurations.microseconds() + std::trunc(subdurations.nanoseconds() / 1000); … … 598 596 599 597 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)) { 601 599 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))); 604 602 else { 605 603 auto lengthWithoutTrailingZeroes = padded.length(); -
trunk/Source/JavaScriptCore/runtime/TemporalDuration.h
r281838 r282125 67 67 double total(JSGlobalObject*, JSValue options) const; 68 68 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); 70 78 71 79 private: … … 75 83 template<typename CharacterType> 76 84 static std::optional<Subdurations> parse(StringParsingBuffer<CharacterType>&); 77 static Subdurations from Object(JSGlobalObject*, JSObject*);85 static Subdurations fromNonDurationValue(JSGlobalObject*, JSValue); 78 86 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); 83 88 84 89 TemporalUnit largestSubduration() const; -
trunk/Source/JavaScriptCore/runtime/TemporalNow.cpp
r282018 r282125 78 78 79 79 String timeZoneString = vm.dateCache.defaultTimeZone(); 80 std::optional<TimeZoneID> identifier = TemporalTimeZone::idForTimeZoneName(timeZoneString);80 std::optional<TimeZoneID> identifier = ISO8601::parseTimeZoneName(timeZoneString); 81 81 if (!identifier) 82 82 return JSValue::encode(TemporalTimeZone::createFromUTCOffset(vm, globalObject->timeZoneStructure(), 0)); -
trunk/Source/JavaScriptCore/runtime/TemporalObject.cpp
r282018 r282125 34 34 #include "TemporalDurationPrototype.h" 35 35 #include "TemporalNow.h" 36 #include "TemporalPlainTimeConstructor.h" 37 #include "TemporalPlainTimePrototype.h" 36 38 #include "TemporalTimeZoneConstructor.h" 37 39 #include "TemporalTimeZonePrototype.h" … … 60 62 JSGlobalObject* globalObject = temporalObject->globalObject(vm); 61 63 return TemporalDurationConstructor::create(vm, TemporalDurationConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()), jsCast<TemporalDurationPrototype*>(globalObject->durationStructure()->storedPrototypeObject())); 64 } 65 66 static 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())); 62 71 } 63 72 … … 80 89 Duration createDurationConstructor DontEnum|PropertyCallback 81 90 Now createNowObject DontEnum|PropertyCallback 91 PlainTime createPlainTimeConstructor DontEnum|PropertyCallback 82 92 TimeZone createTimeZoneConstructor DontEnum|PropertyCallback 83 93 @end … … 243 253 auto scope = DECLARE_THROW_SCOPE(vm); 244 254 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 }); 246 256 RETURN_IF_EXCEPTION(scope, { }); 247 257 248 258 if (smallestUnit) { 249 259 switch (smallestUnit.value()) { 260 case TemporalUnit::Minute: 261 return { { Precision::Minute, 0 }, TemporalUnit::Minute, 1 }; 250 262 case TemporalUnit::Second: 251 return { 0, TemporalUnit::Second, 1 };263 return { { Precision::Fixed, 0 }, TemporalUnit::Second, 1 }; 252 264 case TemporalUnit::Millisecond: 253 return { 3, TemporalUnit::Millisecond, 1 };265 return { { Precision::Fixed, 3 }, TemporalUnit::Millisecond, 1 }; 254 266 case TemporalUnit::Microsecond: 255 return { 6, TemporalUnit::Microsecond, 1 };267 return { { Precision::Fixed, 6 }, TemporalUnit::Microsecond, 1 }; 256 268 case TemporalUnit::Nanosecond: 257 return { 9, TemporalUnit::Nanosecond, 1 };269 return { { Precision::Fixed, 9 }, TemporalUnit::Nanosecond, 1 }; 258 270 default: 259 271 RELEASE_ASSERT_NOT_REACHED(); … … 266 278 267 279 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 }; 269 288 270 289 unsigned digits = precision.value(); 271 290 if (!digits) 272 return { 0, TemporalUnit::Second, 1 };291 return { { Precision::Fixed, 0 }, TemporalUnit::Second, 1 }; 273 292 274 293 if (digits <= 3) 275 return { digits, TemporalUnit::Millisecond, std::pow(10,3 - digits) };294 return { { Precision::Fixed, digits }, TemporalUnit::Millisecond, pow10Unsigned(3 - digits) }; 276 295 277 296 if (digits <= 6) 278 return { digits, TemporalUnit::Microsecond, std::pow(10,6 - digits) };297 return { { Precision::Fixed, digits }, TemporalUnit::Microsecond, pow10Unsigned(6 - digits) }; 279 298 280 299 ASSERT(digits <= 9); 281 return { digits, TemporalUnit::Nanosecond, std::pow(10,9 - digits) };300 return { { Precision::Fixed, digits }, TemporalUnit::Nanosecond, pow10Unsigned(9 - digits) }; 282 301 } 283 302 … … 343 362 } 344 363 364 TemporalOverflow 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 345 371 } // namespace JSC -
trunk/Source/JavaScriptCore/runtime/TemporalObject.h
r281838 r282125 22 22 23 23 #include "JSObject.h" 24 #include <wtf/Variant.h> 24 25 25 26 namespace 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 26 36 27 37 #define JSC_TEMPORAL_UNITS(macro) \ … … 30 40 macro(week, Week) \ 31 41 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 38 44 39 45 enum class TemporalUnit : uint8_t { … … 44 50 #define JSC_COUNT_TEMPORAL_UNITS(name, capitalizedName) + 1 45 51 static constexpr unsigned numberOfTemporalUnits = 0 JSC_TEMPORAL_UNITS(JSC_COUNT_TEMPORAL_UNITS); 52 static constexpr unsigned numberOfTemporalPlainTimeUnits = 0 JSC_TEMPORAL_PLAIN_TIME_UNITS(JSC_COUNT_TEMPORAL_UNITS); 46 53 #undef JSC_COUNT_TEMPORAL_UNITS 47 54 … … 75 82 }; 76 83 84 enum class Precision : uint8_t { 85 Minute, 86 Fixed, 87 Auto, 88 }; 89 77 90 struct PrecisionData { 78 std:: optional<unsigned> precision;91 std::tuple<Precision, unsigned> precision; 79 92 TemporalUnit unit; 80 doubleincrement;93 unsigned increment; 81 94 }; 82 95 … … 90 103 double roundNumberToIncrement(double, double increment, RoundingMode); 91 104 105 enum class TemporalOverflow : bool { 106 Constrain, 107 Reject, 108 }; 109 110 TemporalOverflow toTemporalOverflow(JSGlobalObject*, JSObject*); 111 92 112 } // namespace JSC -
trunk/Source/JavaScriptCore/runtime/TemporalPlainTimeConstructor.h
r282119 r282125 1 1 /* 2 * Copyright (C) 2021 Apple Inc. All rights reserved.2 * Copyright (C) 2021 Apple Inc. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 26 26 #pragma once 27 27 28 #include "IntlObject.h" 29 #include "JSObject.h" 30 #include <wtf/Variant.h> 28 #include "InternalFunction.h" 31 29 32 30 namespace JSC { 33 31 34 class TemporalTimeZone final : public JSNonFinalObject { 32 class TemporalPlainTimePrototype; 33 34 class TemporalPlainTimeConstructor final : public InternalFunction { 35 35 public: 36 using Base = JSNonFinalObject; 36 using Base = InternalFunction; 37 static constexpr unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; 37 38 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*); 46 40 static Structure* createStructure(VM&, JSGlobalObject*, JSValue); 47 41 48 42 DECLARE_INFO; 49 43 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 57 44 private: 58 TemporalTimeZone(VM&, Structure*, TimeZone); 59 60 // TimeZoneID or UTC offset. 61 TimeZone m_timeZone; 45 TemporalPlainTimeConstructor(VM&, Structure*); 46 void finishCreation(VM&, TemporalPlainTimePrototype*); 62 47 }; 48 STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(TemporalPlainTimeConstructor, InternalFunction); 63 49 64 50 } // namespace JSC -
trunk/Source/JavaScriptCore/runtime/TemporalPlainTimePrototype.h
r282119 r282125 1 1 /* 2 * Copyright (C) 2021 Apple Inc. All rights reserved.2 * Copyright (C) 2021 Apple Inc. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 26 26 #pragma once 27 27 28 #include "IntlObject.h"29 28 #include "JSObject.h" 30 #include <wtf/Variant.h>31 29 32 30 namespace JSC { 33 31 34 class Temporal TimeZone final : public JSNonFinalObject {32 class TemporalPlainTimePrototype final : public JSNonFinalObject { 35 33 public: 36 34 using Base = JSNonFinalObject; 35 static constexpr unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; 37 36 38 template<typename CellType, SubspaceAccess mode>37 template<typename CellType, SubspaceAccess> 39 38 static IsoSubspace* subspaceFor(VM& vm) 40 39 { 41 return vm.temporalTimeZoneSpace<mode>(); 40 STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(TemporalPlainTimePrototype, Base); 41 return &vm.plainObjectSpace; 42 42 } 43 43 44 static TemporalTimeZone* createFromID(VM&, Structure*, TimeZoneID); 45 static TemporalTimeZone* createFromUTCOffset(VM&, Structure*, int64_t); 44 static TemporalPlainTimePrototype* create(VM&, JSGlobalObject*, Structure*); 46 45 static Structure* createStructure(VM&, JSGlobalObject*, JSValue); 47 46 48 47 DECLARE_INFO; 49 48 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 57 49 private: 58 TemporalTimeZone(VM&, Structure*, TimeZone); 59 60 // TimeZoneID or UTC offset. 61 TimeZone m_timeZone; 50 TemporalPlainTimePrototype(VM&, Structure*); 51 void finishCreation(VM&, JSGlobalObject*); 62 52 }; 63 53 -
trunk/Source/JavaScriptCore/runtime/TemporalTimeZone.cpp
r282080 r282125 59 59 } 60 60 61 // https://tc39.es/proposal-temporal/#sup-isvalidtimezonename62 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 72 61 // https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimeZonestring 73 62 static std::optional<int64_t> parseTemporalTimeZoneString(StringView) … … 112 101 return TemporalTimeZone::createFromUTCOffset(vm, globalObject->timeZoneStructure(), utcOffset.value()); 113 102 114 std::optional<TimeZoneID> identifier = idForTimeZoneName(timeZoneString);103 std::optional<TimeZoneID> identifier = ISO8601::parseTimeZoneName(timeZoneString); 115 104 if (identifier) 116 105 return TemporalTimeZone::createFromID(vm, globalObject->timeZoneStructure(), identifier.value()); -
trunk/Source/JavaScriptCore/runtime/TemporalTimeZone.h
r282018 r282125 26 26 #pragma once 27 27 28 #include "ISO8601.h" 28 29 #include "IntlObject.h" 29 30 #include "JSObject.h" … … 48 49 DECLARE_INFO; 49 50 50 using TimeZone = Variant<TimeZoneID, int64_t>; 51 using TimeZone = ISO8601::TimeZone; 52 51 53 TimeZone timeZone() const { return m_timeZone; } 52 53 static std::optional<TimeZoneID> idForTimeZoneName(StringView);54 54 55 55 static JSObject* from(JSGlobalObject*, JSValue); -
trunk/Source/JavaScriptCore/runtime/TemporalTimeZoneConstructor.cpp
r282018 r282125 75 75 Base::finishCreation(vm, 0, "TimeZone"_s, PropertyAdditionMode::WithoutStructureTransition); 76 76 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)); 77 78 } 78 79 … … 93 94 return JSValue::encode(TemporalTimeZone::createFromUTCOffset(vm, structure, utcOffset.value())); 94 95 95 std::optional<TimeZoneID> identifier = TemporalTimeZone::idForTimeZoneName(timeZoneString);96 std::optional<TimeZoneID> identifier = ISO8601::parseTimeZoneName(timeZoneString); 96 97 if (!identifier) { 97 98 throwRangeError(globalObject, scope, "argument needs to be UTC offset string or TimeZone identifier"_s); -
trunk/Source/JavaScriptCore/runtime/VM.cpp
r282018 r282125 165 165 #include "TemporalCalendar.h" 166 166 #include "TemporalDuration.h" 167 #include "TemporalPlainTime.h" 167 168 #include "TemporalTimeZone.h" 168 169 #include "TestRunnerUtils.h" … … 1589 1590 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(templateObjectDescriptorSpace, destructibleCellHeapCellType.get(), JSTemplateObjectDescriptor) 1590 1591 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(temporalCalendarSpace, cellHeapCellType.get(), TemporalCalendar) 1592 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(temporalDurationSpace, cellHeapCellType.get(), TemporalDuration) 1593 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(temporalPlainTimeSpace, cellHeapCellType.get(), TemporalPlainTime) 1591 1594 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(temporalTimeZoneSpace, cellHeapCellType.get(), TemporalTimeZone) 1592 1595 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(uint8ArraySpace, cellHeapCellType.get(), JSUint8Array) … … 1623 1626 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(intlSegmenterSpace, intlSegmenterHeapCellType.get(), IntlSegmenter) 1624 1627 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(intlSegmentsSpace, intlSegmentsHeapCellType.get(), IntlSegments) 1625 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(temporalDurationSpace, cellHeapCellType.get(), TemporalDuration)1626 1628 #if ENABLE(WEBASSEMBLY) 1627 1629 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER_SLOW(jsToWasmICCalleeSpace, cellHeapCellType.get(), JSToWasmICCallee) -
trunk/Source/JavaScriptCore/runtime/VM.h
r282018 r282125 196 196 class VMEntryScope; 197 197 class TemporalCalendar; 198 class TemporalPlainTime; 198 199 class TemporalTimeZone; 199 200 class TopLevelGlobalObjectScope; … … 596 597 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(templateObjectDescriptorSpace) 597 598 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(temporalCalendarSpace) 599 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(temporalDurationSpace) 600 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(temporalPlainTimeSpace) 598 601 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(temporalTimeZoneSpace) 599 602 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(uint8ArraySpace) … … 621 624 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(intlSegmenterSpace) 622 625 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(intlSegmentsSpace) 623 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(temporalDurationSpace)624 626 #if ENABLE(WEBASSEMBLY) 625 627 DYNAMIC_ISO_SUBSPACE_DEFINE_MEMBER(jsToWasmICCalleeSpace) -
trunk/Source/WTF/ChangeLog
r282115 r282125 1 2021-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 1 11 2021-09-07 Cameron McCormack <heycam@apple.com> 2 12 -
trunk/Source/WTF/wtf/text/IntegerToStringConversion.h
r282018 r282125 137 137 } // namespace WTF 138 138 139 using WTF::numberToStringSigned; 140 using WTF::numberToStringUnsigned; 139 141 using WTF::lengthOfIntegerAsString; 140 142 using WTF::writeIntegerToBuffer; -
trunk/Source/WTF/wtf/text/StringParsingBuffer.h
r264630 r282125 62 62 constexpr unsigned lengthRemaining() const { return m_end - m_position; } 63 63 64 StringView stringViewOfCharactersRemaining() { return { m_position, lengthRemaining() }; }64 StringView stringViewOfCharactersRemaining() const { return { m_position, lengthRemaining() }; } 65 65 66 CharacterType operator[](unsigned i) 66 CharacterType operator[](unsigned i) const 67 67 { 68 68 ASSERT(i < lengthRemaining());
Note: See TracChangeset
for help on using the changeset viewer.