Skip to content

Commit

Permalink
Bug 1856338 - Part 1: Implement Temporal.TimeZone.prototype.equals. r…
Browse files Browse the repository at this point in the history
…=mgaudet

Implement the changes from <tc39/proposal-temporal#2633>.

Differential Revision: https://phabricator.services.mozilla.com/D189772

UltraBlame original commit: 71b919fe3767226aba654ed1bf4df099c27c4eed
  • Loading branch information
marco-c committed Nov 8, 2023
1 parent 2001ab0 commit 2ad97bd
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 40 deletions.
94 changes: 83 additions & 11 deletions js/src/builtin/temporal/TemporalParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,8 @@ class TemporalParser final {

mozilla::Result<TimeZoneUTCOffset, ParserError> timeZoneUTCOffsetName();

mozilla::Result<TimeZoneAnnotation, ParserError> timeZoneIdentifier();

mozilla::Result<TimeZoneAnnotation, ParserError> timeZoneAnnotation();

bool timeZoneIANANameComponent();
Expand Down Expand Up @@ -914,6 +916,8 @@ class TemporalParser final {
mozilla::Result<ZonedDateTimeString, ParserError>
parseTemporalTimeZoneString();

mozilla::Result<TimeZoneAnnotation, ParserError> parseTimeZoneIdentifier();

mozilla::Result<TimeZoneUTCOffset, ParserError> parseTimeZoneOffsetString();

mozilla::Result<DateTimeUTCOffset, ParserError> parseDateTimeUTCOffset();
Expand Down Expand Up @@ -1297,20 +1301,10 @@ TemporalParser<CharT>::utcOffsetSubMinutePrecision() {

template <typename CharT>
mozilla::Result<TimeZoneAnnotation, ParserError>
TemporalParser<CharT>::timeZoneAnnotation() {


TemporalParser<CharT>::timeZoneIdentifier() {





if (!character('[')) {
return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_BEFORE_TIMEZONE);
}


annotationCriticalFlag();

TimeZoneAnnotation result = {};
if (hasSign()) {
Expand All @@ -1327,6 +1321,27 @@ TemporalParser<CharT>::timeZoneAnnotation() {
result.name = name.unwrap();
}

return result;
}

template <typename CharT>
mozilla::Result<TimeZoneAnnotation, ParserError>
TemporalParser<CharT>::timeZoneAnnotation() {



if (!character('[')) {
return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_BEFORE_TIMEZONE);
}


annotationCriticalFlag();

auto result = timeZoneIdentifier();
if (result.isErr()) {
return result.propagateErr();
}

if (!character(']')) {
return mozilla::Err(JSMSG_TEMPORAL_PARSER_BRACKET_AFTER_TIMEZONE);
}
Expand Down Expand Up @@ -1681,6 +1696,63 @@ bool js::temporal::ParseTemporalTimeZoneString(
return true;
}

template <typename CharT>
mozilla::Result<TimeZoneAnnotation, ParserError>
TemporalParser<CharT>::parseTimeZoneIdentifier() {
auto result = timeZoneIdentifier();
if (result.isErr()) {
return result.propagateErr();
}
if (!reader_.atEnd()) {
return mozilla::Err(JSMSG_TEMPORAL_PARSER_GARBAGE_AFTER_INPUT);
}
return result;
}




template <typename CharT>
static auto ParseTimeZoneIdentifier(mozilla::Span<const CharT> str) {
TemporalParser<CharT> parser(str);
return parser.parseTimeZoneIdentifier();
}




static auto ParseTimeZoneIdentifier(Handle<JSLinearString*> str) {
JS::AutoCheckCannotGC nogc;
if (str->hasLatin1Chars()) {
return ParseTimeZoneIdentifier<Latin1Char>(str->latin1Range(nogc));
}
return ParseTimeZoneIdentifier<char16_t>(str->twoByteRange(nogc));
}




bool js::temporal::ParseTimeZoneIdentifier(
JSContext* cx, Handle<JSString*> str,
MutableHandle<ParsedTimeZone> result) {
Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
if (!linear) {
return false;
}


auto parseResult = ::ParseTimeZoneIdentifier(linear);
if (parseResult.isErr()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
parseResult.unwrapErr());
return false;
}
auto timeZone = parseResult.unwrap();


return ParseTimeZoneAnnotation(cx, timeZone, linear, result);
}

template <typename CharT>
mozilla::Result<TimeZoneUTCOffset, ParserError>
TemporalParser<CharT>::parseTimeZoneOffsetString() {
Expand Down
6 changes: 6 additions & 0 deletions js/src/builtin/temporal/TemporalParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ bool ParseTemporalTimeZoneString(JSContext* cx, JS::Handle<JSString*> str,



bool ParseTimeZoneIdentifier(JSContext* cx, JS::Handle<JSString*> str,
JS::MutableHandle<ParsedTimeZone> result);




bool ParseTimeZoneOffsetString(JSContext* cx, JS::Handle<JSString*> str,
int32_t* result);

Expand Down
134 changes: 134 additions & 0 deletions js/src/builtin/temporal/TimeZone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,108 @@ JSString* js::temporal::GetOffsetStringFor(




bool js::temporal::TimeZoneEquals(JSContext* cx, Handle<JSString*> one,
Handle<JSString*> two, bool* equals) {



if (!EqualStrings(cx, one, two, equals)) {
return false;
}
if (*equals) {
return true;
}


Rooted<ParsedTimeZone> timeZoneOne(cx);
if (!ParseTimeZoneIdentifier(cx, one, &timeZoneOne)) {
return false;
}


Rooted<ParsedTimeZone> timeZoneTwo(cx);
if (!ParseTimeZoneIdentifier(cx, two, &timeZoneTwo)) {
return false;
}


if (timeZoneOne.name() && timeZoneTwo.name()) {

Rooted<JSAtom*> validTimeZoneOne(cx);
if (!IsValidTimeZoneName(cx, timeZoneOne.name(), &validTimeZoneOne)) {
return false;
}
if (!validTimeZoneOne) {
*equals = false;
return true;
}


Rooted<JSAtom*> validTimeZoneTwo(cx);
if (!IsValidTimeZoneName(cx, timeZoneTwo.name(), &validTimeZoneTwo)) {
return false;
}
if (!validTimeZoneTwo) {
*equals = false;
return true;
}


Rooted<JSString*> canonicalOne(
cx, CanonicalizeTimeZoneName(cx, validTimeZoneOne));
if (!canonicalOne) {
return false;
}

JSString* canonicalTwo = CanonicalizeTimeZoneName(cx, validTimeZoneTwo);
if (!canonicalTwo) {
return false;
}

return EqualStrings(cx, canonicalOne, canonicalTwo, equals);
}


if (!timeZoneOne.name() && !timeZoneTwo.name()) {
*equals = (timeZoneOne.offset() == timeZoneTwo.offset());
return true;
}


*equals = false;
return true;
}




bool js::temporal::TimeZoneEquals(JSContext* cx, Handle<TimeZoneValue> one,
Handle<TimeZoneValue> two, bool* equals) {

if (one.isObject() && two.isObject() && one.toObject() == two.toObject()) {
*equals = true;
return true;
}


Rooted<JSString*> timeZoneOne(cx, ToTemporalTimeZoneIdentifier(cx, one));
if (!timeZoneOne) {
return false;
}


Rooted<JSString*> timeZoneTwo(cx, ToTemporalTimeZoneIdentifier(cx, two));
if (!timeZoneTwo) {
return false;
}


return TimeZoneEquals(cx, timeZoneOne, timeZoneTwo, equals);
}



static inline double PositiveModulo(double dividend, double divisor) {
MOZ_ASSERT(divisor > 0);
MOZ_ASSERT(std::isfinite(divisor));
Expand Down Expand Up @@ -1806,6 +1908,37 @@ static bool TimeZone_from(JSContext* cx, unsigned argc, Value* vp) {



static bool TimeZone_equals(JSContext* cx, const CallArgs& args) {
Rooted<TimeZoneValue> timeZone(cx, &args.thisv().toObject());


Rooted<TimeZoneValue> other(cx);
if (!ToTemporalTimeZone(cx, args.get(0), &other)) {
return false;
}


bool equals;
if (!TimeZoneEquals(cx, timeZone, other, &equals)) {
return false;
}

args.rval().setBoolean(equals);
return true;
}




static bool TimeZone_equals(JSContext* cx, unsigned argc, Value* vp) {

CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsTimeZone, TimeZone_equals>(cx, args);
}




static bool TimeZone_getOffsetNanosecondsFor(JSContext* cx,
const CallArgs& args) {
Rooted<TimeZoneObject*> timeZone(
Expand Down Expand Up @@ -2224,6 +2357,7 @@ static const JSFunctionSpec TimeZone_methods[] = {
};

static const JSFunctionSpec TimeZone_prototype_methods[] = {
JS_FN("equals", TimeZone_equals, 1, 0),
JS_FN("getOffsetNanosecondsFor", TimeZone_getOffsetNanosecondsFor, 1, 0),
JS_FN("getOffsetStringFor", TimeZone_getOffsetStringFor, 1, 0),
JS_FN("getPlainDateTimeFor", TimeZone_getPlainDateTimeFor, 1, 0),
Expand Down
12 changes: 12 additions & 0 deletions js/src/builtin/temporal/TimeZone.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,18 @@ JSString* ToTemporalTimeZoneIdentifier(JSContext* cx,



bool TimeZoneEquals(JSContext* cx, JS::Handle<JSString*> one,
JS::Handle<JSString*> two, bool* equals);




bool TimeZoneEquals(JSContext* cx, JS::Handle<TimeZoneValue> one,
JS::Handle<TimeZoneValue> two, bool* equals);




PlainDateTimeObject* GetPlainDateTimeFor(JSContext* cx,
JS::Handle<TimeZoneValue> timeZone,
const Instant& instant,
Expand Down
31 changes: 2 additions & 29 deletions js/src/builtin/temporal/ZonedDateTime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1053,33 +1053,6 @@ bool js::temporal::DifferenceZonedDateTime(JSContext* cx, const Instant& ns1,



static bool TimeZoneEquals(JSContext* cx, Handle<TimeZoneValue> one,
Handle<TimeZoneValue> two, bool* equals) {

if (one.isObject() && two.isObject() && one.toObject() == two.toObject()) {
*equals = true;
return true;
}


Rooted<JSString*> timeZoneOne(cx, ToTemporalTimeZoneIdentifier(cx, one));
if (!timeZoneOne) {
return false;
}


JSString* timeZoneTwo = ToTemporalTimeZoneIdentifier(cx, two);
if (!timeZoneTwo) {
return false;
}


return EqualStrings(cx, timeZoneOne, timeZoneTwo, equals);
}




static bool TimeZoneEqualsOrThrow(JSContext* cx, Handle<TimeZoneValue> one,
Handle<TimeZoneValue> two) {

Expand All @@ -1094,14 +1067,14 @@ static bool TimeZoneEqualsOrThrow(JSContext* cx, Handle<TimeZoneValue> one,
}


JSString* timeZoneTwo = ToTemporalTimeZoneIdentifier(cx, two);
Rooted<JSString*> timeZoneTwo(cx, ToTemporalTimeZoneIdentifier(cx, two));
if (!timeZoneTwo) {
return false;
}


bool equals;
if (!EqualStrings(cx, timeZoneOne, timeZoneTwo, &equals)) {
if (!TimeZoneEquals(cx, timeZoneOne, timeZoneTwo, &equals)) {
return false;
}
if (equals) {
Expand Down

0 comments on commit 2ad97bd

Please sign in to comment.