Bug 1874684 - Part 10: Replace BigInt with Int128 in RoundNumberToIncrement. r=mgaudet
[gecko.git] / js / src / builtin / temporal / TimeZone.h
blobf1d0bf3f1fabcb5dd7ba5ff137c47630ea576d1c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef builtin_temporal_TimeZone_h
8 #define builtin_temporal_TimeZone_h
10 #include "mozilla/Assertions.h"
11 #include "mozilla/EnumSet.h"
13 #include <stddef.h>
14 #include <stdint.h>
16 #include "builtin/temporal/Wrapped.h"
17 #include "js/GCVector.h"
18 #include "js/RootingAPI.h"
19 #include "js/TypeDecls.h"
20 #include "js/Value.h"
21 #include "vm/JSObject.h"
22 #include "vm/NativeObject.h"
24 class JSLinearString;
25 class JS_PUBLIC_API JSTracer;
26 struct JSClassOps;
28 namespace js {
29 struct ClassSpec;
32 namespace mozilla::intl {
33 class TimeZone;
36 namespace js::temporal {
38 class TimeZoneObjectMaybeBuiltin : public NativeObject {
39 public:
40 static constexpr uint32_t IDENTIFIER_SLOT = 0;
41 static constexpr uint32_t OFFSET_MINUTES_SLOT = 1;
42 static constexpr uint32_t INTL_TIMEZONE_SLOT = 2;
43 static constexpr uint32_t SLOT_COUNT = 3;
45 // Estimated memory use for intl::TimeZone (see IcuMemoryUsage).
46 static constexpr size_t EstimatedMemoryUse = 6840;
48 JSString* identifier() const {
49 return getFixedSlot(IDENTIFIER_SLOT).toString();
52 const auto& offsetMinutes() const {
53 return getFixedSlot(OFFSET_MINUTES_SLOT);
56 mozilla::intl::TimeZone* getTimeZone() const {
57 const auto& slot = getFixedSlot(INTL_TIMEZONE_SLOT);
58 if (slot.isUndefined()) {
59 return nullptr;
61 return static_cast<mozilla::intl::TimeZone*>(slot.toPrivate());
64 void setTimeZone(mozilla::intl::TimeZone* timeZone) {
65 setFixedSlot(INTL_TIMEZONE_SLOT, JS::PrivateValue(timeZone));
68 protected:
69 static void finalize(JS::GCContext* gcx, JSObject* obj);
72 class TimeZoneObject : public TimeZoneObjectMaybeBuiltin {
73 public:
74 static const JSClass class_;
75 static const JSClass& protoClass_;
77 private:
78 static const JSClassOps classOps_;
79 static const ClassSpec classSpec_;
82 class BuiltinTimeZoneObject : public TimeZoneObjectMaybeBuiltin {
83 public:
84 static const JSClass class_;
86 private:
87 static const JSClassOps classOps_;
90 } /* namespace js::temporal */
92 template <>
93 inline bool JSObject::is<js::temporal::TimeZoneObjectMaybeBuiltin>() const {
94 return is<js::temporal::TimeZoneObject>() ||
95 is<js::temporal::BuiltinTimeZoneObject>();
98 namespace js::temporal {
101 * Temporal time zones can be either objects or strings. Objects are either
102 * instances of `Temporal.TimeZone` or user-defined time zones. Strings are
103 * either canonical time zone identifiers or time zone offset strings.
105 * Examples of valid Temporal time zones:
106 * - Any object
107 * - "UTC"
108 * - "America/New_York"
109 * - "+00:00"
111 * Examples of invalid Temporal time zones:
112 * - Number values
113 * - "utc" (wrong case)
114 * - "Etc/UTC" (canonical name is "UTC")
115 * - "+00" (missing minutes part)
116 * - "+00:00:00" (sub-minute precision)
117 * - "+00:00:01" (sub-minute precision)
118 * - "-00:00" (wrong sign for zero offset)
120 * String-valued Temporal time zones are an optimization to avoid allocating
121 * `Temporal.TimeZone` objects when creating `Temporal.ZonedDateTime` objects.
122 * For example `Temporal.ZonedDateTime.from("1970-01-01[UTC]")` doesn't require
123 * to allocate a fresh `Temporal.TimeZone` object for the "UTC" time zone.
125 * The specification creates new `Temporal.TimeZone` objects whenever any
126 * operation is performed on a string-valued Temporal time zone. This newly
127 * created object can't be accessed by the user and implementations are expected
128 * to optimize away the allocation.
130 * The following two implementation approaches are possible:
132 * 1. Represent string-valued time zones as JSStrings. Additionally keep a
133 * mapping from JSString to `mozilla::intl::TimeZone` to avoid repeatedly
134 * creating new `mozilla::intl::TimeZone` for time zone operations. Offset
135 * string time zones have to be special cased, because they don't use
136 * `mozilla::intl::TimeZone`. Either detect offset strings by checking the
137 * time zone identifier or store offset strings as the offset in minutes
138 * value to avoid reparsing the offset string again and again.
139 * 2. Represent string-valued time zones as `Temporal.TimeZone`-like objects.
140 * These internal `Temporal.TimeZone`-like objects must not be exposed to
141 * user-code.
143 * Option 2 is a bit easier to implement, so we use this approach for now.
145 class TimeZoneValue final {
146 JSObject* object_ = nullptr;
148 public:
150 * Default initialize this TimeZoneValue.
152 TimeZoneValue() = default;
155 * Initialize this TimeZoneValue with a "string" time zone object.
157 explicit TimeZoneValue(BuiltinTimeZoneObject* timeZone) : object_(timeZone) {
158 MOZ_ASSERT(isString());
162 * Initialize this TimeZoneValue with an "object" time zone object.
164 explicit TimeZoneValue(JSObject* timeZone) : object_(timeZone) {
165 MOZ_ASSERT(isObject());
169 * Initialize this TimeZoneValue from a slot Value, which must be either a
170 * "string" or "object" time zone object.
172 explicit TimeZoneValue(const JS::Value& value) : object_(&value.toObject()) {}
175 * Return true if this TimeZoneValue is not null.
177 explicit operator bool() const { return !!object_; }
180 * Return true if this TimeZoneValue is a "string" time zone.
182 bool isString() const {
183 return object_ && object_->is<BuiltinTimeZoneObject>();
187 * Return true if this TimeZoneValue is an "object" time zone.
189 bool isObject() const { return object_ && !isString(); }
192 * Return true if this TimeZoneValue holds a TimeZoneObjectMaybeBuiltin.
194 bool isTimeZoneObjectMaybeBuiltin() const {
195 return object_ && object_->is<TimeZoneObjectMaybeBuiltin>();
199 * Return this "string" time zone.
201 auto* toString() const {
202 MOZ_ASSERT(isString());
203 return &object_->as<BuiltinTimeZoneObject>();
207 * Return this "object" time zone.
209 JSObject* toObject() const {
210 MOZ_ASSERT(isObject());
211 return object_;
215 * Return the underlying object as a TimeZoneObjectMaybeBuiltin.
217 auto* toTimeZoneObjectMaybeBuiltin() const {
218 MOZ_ASSERT(isTimeZoneObjectMaybeBuiltin());
219 return &object_->as<TimeZoneObjectMaybeBuiltin>();
223 * Return the Value representation of this TimeZoneValue.
225 JS::Value toValue() const {
226 if (isString()) {
227 return JS::StringValue(toString()->identifier());
230 MOZ_ASSERT(object_);
231 return JS::ObjectValue(*object_);
235 * Return the slot Value representation of this TimeZoneValue.
237 JS::Value toSlotValue() const {
238 MOZ_ASSERT(object_);
239 return JS::ObjectValue(*object_);
242 // Helper methods for (Mutable)WrappedPtrOperations.
243 auto address() { return &object_; }
244 auto address() const { return &object_; }
246 // Trace implementation.
247 void trace(JSTracer* trc);
250 enum class TimeZoneMethod {
251 GetOffsetNanosecondsFor,
252 GetPossibleInstantsFor,
255 class TimeZoneRecord {
256 TimeZoneValue receiver_;
258 // Null unless non-builtin time zone methods are used.
259 JSObject* getOffsetNanosecondsFor_ = nullptr;
260 JSObject* getPossibleInstantsFor_ = nullptr;
262 #ifdef DEBUG
263 mozilla::EnumSet<TimeZoneMethod> lookedUp_{};
264 #endif
266 public:
268 * Default initialize this TimeZoneRecord.
270 TimeZoneRecord() = default;
272 explicit TimeZoneRecord(const TimeZoneValue& receiver)
273 : receiver_(receiver) {}
275 const auto& receiver() const { return receiver_; }
276 auto* getOffsetNanosecondsFor() const { return getOffsetNanosecondsFor_; }
277 auto* getPossibleInstantsFor() const { return getPossibleInstantsFor_; }
279 #ifdef DEBUG
280 auto& lookedUp() const { return lookedUp_; }
281 auto& lookedUp() { return lookedUp_; }
282 #endif
284 // Helper methods for (Mutable)WrappedPtrOperations.
285 auto* receiverDoNotUse() const { return &receiver_; }
286 auto* getOffsetNanosecondsForDoNotUse() const {
287 return &getOffsetNanosecondsFor_;
289 auto* getOffsetNanosecondsForDoNotUse() { return &getOffsetNanosecondsFor_; }
290 auto* getPossibleInstantsForDoNotUse() const {
291 return &getPossibleInstantsFor_;
293 auto* getPossibleInstantsForDoNotUse() { return &getPossibleInstantsFor_; }
295 // Trace implementation.
296 void trace(JSTracer* trc);
299 struct Instant;
300 struct ParsedTimeZone;
301 struct PlainDateTime;
302 class CalendarValue;
303 class InstantObject;
304 class PlainDateTimeObject;
305 class PlainDateTimeWithCalendar;
306 enum class TemporalDisambiguation;
309 * IsValidTimeZoneName ( timeZone )
310 * IsAvailableTimeZoneName ( timeZone )
312 bool IsValidTimeZoneName(JSContext* cx, JS::Handle<JSString*> timeZone,
313 JS::MutableHandle<JSAtom*> validatedTimeZone);
316 * CanonicalizeTimeZoneName ( timeZone )
318 JSString* CanonicalizeTimeZoneName(JSContext* cx,
319 JS::Handle<JSLinearString*> timeZone);
322 * IsValidTimeZoneName ( timeZone )
323 * IsAvailableTimeZoneName ( timeZone )
324 * CanonicalizeTimeZoneName ( timeZone )
326 JSString* ValidateAndCanonicalizeTimeZoneName(JSContext* cx,
327 JS::Handle<JSString*> timeZone);
330 * CreateTemporalTimeZone ( identifier [ , newTarget ] )
332 BuiltinTimeZoneObject* CreateTemporalTimeZone(JSContext* cx,
333 JS::Handle<JSString*> identifier);
336 * ToTemporalTimeZoneSlotValue ( temporalTimeZoneLike )
338 bool ToTemporalTimeZone(JSContext* cx,
339 JS::Handle<JS::Value> temporalTimeZoneLike,
340 JS::MutableHandle<TimeZoneValue> result);
343 * ToTemporalTimeZoneSlotValue ( temporalTimeZoneLike )
345 bool ToTemporalTimeZone(JSContext* cx, JS::Handle<ParsedTimeZone> string,
346 JS::MutableHandle<TimeZoneValue> result);
349 * ToTemporalTimeZoneObject ( timeZoneSlotValue )
351 JSObject* ToTemporalTimeZoneObject(JSContext* cx,
352 JS::Handle<TimeZoneValue> timeZone);
355 * ToTemporalTimeZoneIdentifier ( timeZoneSlotValue )
357 JSString* ToTemporalTimeZoneIdentifier(JSContext* cx,
358 JS::Handle<TimeZoneValue> timeZone);
361 * TimeZoneEquals ( one, two )
363 bool TimeZoneEquals(JSContext* cx, JS::Handle<JSString*> one,
364 JS::Handle<JSString*> two, bool* equals);
367 * TimeZoneEquals ( one, two )
369 bool TimeZoneEquals(JSContext* cx, JS::Handle<TimeZoneValue> one,
370 JS::Handle<TimeZoneValue> two, bool* equals);
373 * GetPlainDateTimeFor ( timeZoneRec, instant, calendar [ ,
374 * precalculatedOffsetNanoseconds ] )
376 PlainDateTimeObject* GetPlainDateTimeFor(JSContext* cx,
377 JS::Handle<TimeZoneValue> timeZone,
378 const Instant& instant,
379 JS::Handle<CalendarValue> calendar);
382 * GetPlainDateTimeFor ( timeZoneRec, instant, calendar [ ,
383 * precalculatedOffsetNanoseconds ] )
385 PlainDateTimeObject* GetPlainDateTimeFor(JSContext* cx, const Instant& instant,
386 JS::Handle<CalendarValue> calendar,
387 int64_t offsetNanoseconds);
390 * GetPlainDateTimeFor ( timeZoneRec, instant, calendar [ ,
391 * precalculatedOffsetNanoseconds ] )
393 PlainDateTime GetPlainDateTimeFor(const Instant& instant,
394 int64_t offsetNanoseconds);
397 * GetPlainDateTimeFor ( timeZoneRec, instant, calendar [ ,
398 * precalculatedOffsetNanoseconds ] )
400 bool GetPlainDateTimeFor(JSContext* cx, JS::Handle<TimeZoneRecord> timeZone,
401 const Instant& instant, PlainDateTime* result);
404 * GetPlainDateTimeFor ( timeZoneRec, instant, calendar [ ,
405 * precalculatedOffsetNanoseconds ] )
407 bool GetPlainDateTimeFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
408 const Instant& instant, PlainDateTime* result);
411 * GetInstantFor ( timeZoneRec, dateTime, disambiguation )
413 bool GetInstantFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
414 JS::Handle<PlainDateTimeObject*> dateTime,
415 TemporalDisambiguation disambiguation, Instant* result);
418 * GetInstantFor ( timeZoneRec, dateTime, disambiguation )
420 bool GetInstantFor(JSContext* cx, JS::Handle<TimeZoneRecord> timeZone,
421 JS::Handle<PlainDateTimeWithCalendar> dateTime,
422 TemporalDisambiguation disambiguation, Instant* result);
425 * GetInstantFor ( timeZoneRec, dateTime, disambiguation )
427 bool GetInstantFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
428 JS::Handle<PlainDateTimeWithCalendar> dateTime,
429 TemporalDisambiguation disambiguation, Instant* result);
432 * FormatUTCOffsetNanoseconds ( offsetNanoseconds )
434 JSString* FormatUTCOffsetNanoseconds(JSContext* cx, int64_t offsetNanoseconds);
437 * GetOffsetStringFor ( timeZoneRec, instant )
439 JSString* GetOffsetStringFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
440 const Instant& instant);
443 * GetOffsetStringFor ( timeZoneRec, instant )
445 JSString* GetOffsetStringFor(JSContext* cx, JS::Handle<TimeZoneRecord> timeZone,
446 JS::Handle<Wrapped<InstantObject*>> instant);
449 * GetOffsetNanosecondsFor ( timeZoneRec, instant )
451 bool GetOffsetNanosecondsFor(JSContext* cx, JS::Handle<TimeZoneRecord> timeZone,
452 JS::Handle<Wrapped<InstantObject*>> instant,
453 int64_t* offsetNanoseconds);
456 * GetOffsetNanosecondsFor ( timeZoneRec, instant )
458 bool GetOffsetNanosecondsFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
459 JS::Handle<Wrapped<InstantObject*>> instant,
460 int64_t* offsetNanoseconds);
463 * GetOffsetNanosecondsFor ( timeZoneRec, instant )
465 bool GetOffsetNanosecondsFor(JSContext* cx, JS::Handle<TimeZoneRecord> timeZone,
466 const Instant& instant,
467 int64_t* offsetNanoseconds);
470 * GetOffsetNanosecondsFor ( timeZoneRec, instant )
472 bool GetOffsetNanosecondsFor(JSContext* cx, JS::Handle<TimeZoneValue> timeZone,
473 const Instant& instant,
474 int64_t* offsetNanoseconds);
476 using InstantVector = JS::StackGCVector<Wrapped<InstantObject*>>;
479 * GetPossibleInstantsFor ( timeZoneRec, dateTime )
481 bool GetPossibleInstantsFor(JSContext* cx, JS::Handle<TimeZoneRecord> timeZone,
482 JS::Handle<PlainDateTimeWithCalendar> dateTime,
483 JS::MutableHandle<InstantVector> list);
486 * DisambiguatePossibleInstants ( possibleInstants, timeZoneRec, dateTime,
487 * disambiguation )
489 bool DisambiguatePossibleInstants(
490 JSContext* cx, JS::Handle<InstantVector> possibleInstants,
491 JS::Handle<TimeZoneRecord> timeZone, const PlainDateTime& dateTime,
492 TemporalDisambiguation disambiguation,
493 JS::MutableHandle<Wrapped<InstantObject*>> result);
496 * CreateTimeZoneMethodsRecord ( timeZone, methods )
498 bool CreateTimeZoneMethodsRecord(JSContext* cx,
499 JS::Handle<TimeZoneValue> timeZone,
500 mozilla::EnumSet<TimeZoneMethod> methods,
501 JS::MutableHandle<TimeZoneRecord> result);
503 #ifdef DEBUG
505 * TimeZoneMethodsRecordHasLookedUp ( timeZoneRec, methodName )
507 inline bool TimeZoneMethodsRecordHasLookedUp(const TimeZoneRecord& timeZone,
508 TimeZoneMethod methodName) {
509 // Steps 1-4.
510 return timeZone.lookedUp().contains(methodName);
512 #endif
515 * TimeZoneMethodsRecordIsBuiltin ( timeZoneRec )
517 inline bool TimeZoneMethodsRecordIsBuiltin(const TimeZoneRecord& timeZone) {
518 // Steps 1-2.
519 return timeZone.receiver().isString();
522 // Helper for MutableWrappedPtrOperations.
523 bool WrapTimeZoneValueObject(JSContext* cx,
524 JS::MutableHandle<JSObject*> timeZone);
526 } /* namespace js::temporal */
528 namespace js {
530 template <typename Wrapper>
531 class WrappedPtrOperations<temporal::TimeZoneValue, Wrapper> {
532 const auto& container() const {
533 return static_cast<const Wrapper*>(this)->get();
536 public:
537 explicit operator bool() const { return !!container(); }
539 bool isString() const { return container().isString(); }
541 bool isObject() const { return container().isObject(); }
543 JS::Handle<temporal::BuiltinTimeZoneObject*> toString() const {
544 MOZ_ASSERT(container().isString());
545 auto h = JS::Handle<JSObject*>::fromMarkedLocation(container().address());
546 return h.template as<temporal::BuiltinTimeZoneObject>();
549 JS::Handle<JSObject*> toObject() const {
550 MOZ_ASSERT(container().isObject());
551 return JS::Handle<JSObject*>::fromMarkedLocation(container().address());
554 JS::Handle<temporal::TimeZoneObjectMaybeBuiltin*>
555 toTimeZoneObjectMaybeBuiltin() const {
556 MOZ_ASSERT(container().isTimeZoneObjectMaybeBuiltin());
557 auto h = JS::Handle<JSObject*>::fromMarkedLocation(container().address());
558 return h.template as<temporal::TimeZoneObjectMaybeBuiltin>();
561 JS::Value toValue() const { return container().toValue(); }
563 JS::Value toSlotValue() const { return container().toSlotValue(); }
566 template <typename Wrapper>
567 class MutableWrappedPtrOperations<temporal::TimeZoneValue, Wrapper>
568 : public WrappedPtrOperations<temporal::TimeZoneValue, Wrapper> {
569 auto& container() { return static_cast<Wrapper*>(this)->get(); }
571 public:
573 * Wrap the time zone value into the current compartment.
575 bool wrap(JSContext* cx) {
576 MOZ_ASSERT(container().isString() || container().isObject());
577 auto mh =
578 JS::MutableHandle<JSObject*>::fromMarkedLocation(container().address());
579 return temporal::WrapTimeZoneValueObject(cx, mh);
583 template <typename Wrapper>
584 class WrappedPtrOperations<temporal::TimeZoneRecord, Wrapper> {
585 const auto& container() const {
586 return static_cast<const Wrapper*>(this)->get();
589 public:
590 JS::Handle<temporal::TimeZoneValue> receiver() const {
591 return JS::Handle<temporal::TimeZoneValue>::fromMarkedLocation(
592 container().receiverDoNotUse());
595 JS::Handle<JSObject*> getOffsetNanosecondsFor() const {
596 return JS::Handle<JSObject*>::fromMarkedLocation(
597 container().getOffsetNanosecondsForDoNotUse());
600 JS::Handle<JSObject*> getPossibleInstantsFor() const {
601 return JS::Handle<JSObject*>::fromMarkedLocation(
602 container().getPossibleInstantsForDoNotUse());
606 template <typename Wrapper>
607 class MutableWrappedPtrOperations<temporal::TimeZoneRecord, Wrapper>
608 : public WrappedPtrOperations<temporal::TimeZoneRecord, Wrapper> {
609 auto& container() { return static_cast<Wrapper*>(this)->get(); }
611 public:
612 JS::MutableHandle<JSObject*> getOffsetNanosecondsFor() {
613 return JS::MutableHandle<JSObject*>::fromMarkedLocation(
614 container().getOffsetNanosecondsForDoNotUse());
617 JS::MutableHandle<JSObject*> getPossibleInstantsFor() {
618 return JS::MutableHandle<JSObject*>::fromMarkedLocation(
619 container().getPossibleInstantsForDoNotUse());
623 } /* namespace js */
625 #endif /* builtin_temporal_TimeZone_h */