CalendarFields.h (12045B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef builtin_temporal_CalendarFields_h 8 #define builtin_temporal_CalendarFields_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/Attributes.h" 12 #include "mozilla/EnumSet.h" 13 #include "mozilla/EnumTypeTraits.h" 14 #include "mozilla/FloatingPoint.h" 15 16 #include <cmath> 17 #include <stdint.h> 18 19 #include "jstypes.h" 20 21 #include "builtin/temporal/MonthCode.h" 22 #include "builtin/temporal/TemporalUnit.h" 23 #include "builtin/temporal/TimeZone.h" 24 #include "js/RootingAPI.h" 25 #include "js/TypeDecls.h" 26 27 class JS_PUBLIC_API JSTracer; 28 29 namespace js::temporal { 30 31 // NB: The fields must be sorted alphabetically! 32 enum class CalendarField { 33 Day, 34 Era, 35 EraYear, 36 Hour, 37 Microsecond, 38 Millisecond, 39 Minute, 40 Month, 41 MonthCode, 42 Nanosecond, 43 Offset, 44 Second, 45 TimeZone, 46 Year, 47 }; 48 49 class MonthCodeField final { 50 // Packed representation for ordinal month (31 bits) and leap month (1 bit). 51 uint32_t code_ = 0; 52 53 public: 54 MonthCodeField() = default; 55 56 MonthCodeField(int32_t ordinal, bool isLeapMonth) 57 : code_((ordinal << 1) | isLeapMonth) { 58 MOZ_ASSERT(ordinal >= 0); 59 MOZ_ASSERT_IF(ordinal == 0, isLeapMonth); 60 } 61 62 MOZ_IMPLICIT MonthCodeField(MonthCode monthCode) 63 : MonthCodeField(monthCode.ordinal(), monthCode.isLeapMonth()) {} 64 65 int32_t ordinal() const { return (code_ >> 1); } 66 67 bool isLeapMonth() const { return bool(code_ & 1); } 68 }; 69 70 class OffsetField final { 71 int64_t offset_ = INT64_MIN; 72 73 public: 74 OffsetField() = default; 75 76 explicit OffsetField(int64_t offset) : offset_(offset) { 77 MOZ_ASSERT(std::abs(offset) < ToNanoseconds(TemporalUnit::Day)); 78 } 79 80 explicit operator int64_t() const { 81 MOZ_ASSERT(offset_ != INT64_MIN); 82 return offset_; 83 } 84 }; 85 86 // Default values are specified in [1]. `UNSET` is replaced with an appropriate 87 // value based on the type, for example `double` fields use NaN whereas pointer 88 // fields use nullptr. 89 // 90 // [1] 91 // <https://tc39.es/proposal-temporal/#table-temporal-calendar-fields-record-fields> 92 class MOZ_STACK_CLASS CalendarFields final { 93 mozilla::EnumSet<CalendarField> fields_ = {}; 94 95 JSString* era_ = nullptr; 96 double eraYear_ = mozilla::UnspecifiedNaN<double>(); 97 double year_ = mozilla::UnspecifiedNaN<double>(); 98 double month_ = mozilla::UnspecifiedNaN<double>(); 99 MonthCodeField monthCode_ = {}; 100 double day_ = mozilla::UnspecifiedNaN<double>(); 101 double hour_ = 0; 102 double minute_ = 0; 103 double second_ = 0; 104 double millisecond_ = 0; 105 double microsecond_ = 0; 106 double nanosecond_ = 0; 107 OffsetField offset_ = {}; 108 TimeZoneValue timeZone_ = {}; 109 110 public: 111 CalendarFields() = default; 112 CalendarFields(const CalendarFields&) = default; 113 114 auto* era() const { return era_; } 115 auto eraYear() const { return eraYear_; } 116 auto year() const { return year_; } 117 auto month() const { return month_; } 118 auto monthCode() const { return monthCode_; } 119 auto day() const { return day_; } 120 auto hour() const { return hour_; } 121 auto minute() const { return minute_; } 122 auto second() const { return second_; } 123 auto millisecond() const { return millisecond_; } 124 auto microsecond() const { return microsecond_; } 125 auto nanosecond() const { return nanosecond_; } 126 auto offset() const { return offset_; } 127 auto& timeZone() const { return timeZone_; } 128 129 void setEra(JSString* era) { 130 fields_ += CalendarField::Era; 131 era_ = era; 132 } 133 void setEraYear(double eraYear) { 134 fields_ += CalendarField::EraYear; 135 eraYear_ = eraYear; 136 } 137 void setYear(double year) { 138 fields_ += CalendarField::Year; 139 year_ = year; 140 } 141 void setMonth(double month) { 142 fields_ += CalendarField::Month; 143 month_ = month; 144 } 145 void setMonthCode(MonthCodeField monthCode) { 146 fields_ += CalendarField::MonthCode; 147 monthCode_ = monthCode; 148 } 149 void setDay(double day) { 150 fields_ += CalendarField::Day; 151 day_ = day; 152 } 153 void setHour(double hour) { 154 fields_ += CalendarField::Hour; 155 hour_ = hour; 156 } 157 void setMinute(double minute) { 158 fields_ += CalendarField::Minute; 159 minute_ = minute; 160 } 161 void setSecond(double second) { 162 fields_ += CalendarField::Second; 163 second_ = second; 164 } 165 void setMillisecond(double millisecond) { 166 fields_ += CalendarField::Millisecond; 167 millisecond_ = millisecond; 168 } 169 void setMicrosecond(double microsecond) { 170 fields_ += CalendarField::Microsecond; 171 microsecond_ = microsecond; 172 } 173 void setNanosecond(double nanosecond) { 174 fields_ += CalendarField::Nanosecond; 175 nanosecond_ = nanosecond; 176 } 177 void setOffset(OffsetField offset) { 178 fields_ += CalendarField::Offset; 179 offset_ = offset; 180 } 181 void setTimeZone(const TimeZoneValue& timeZone) { 182 fields_ += CalendarField::TimeZone; 183 timeZone_ = timeZone; 184 } 185 186 /** 187 * Return `true` if the field is present. 188 */ 189 bool has(CalendarField field) const { return fields_.contains(field); } 190 191 /** 192 * Return the set of all present fields. 193 */ 194 mozilla::EnumSet<CalendarField> keys() const { return fields_; } 195 196 /** 197 * Mark that `field` is present, but uses its default value. The field must 198 * not already be present in `this`. 199 */ 200 void setDefault(CalendarField field) { 201 MOZ_ASSERT(!fields_.contains(field)); 202 203 // Field whose default value is not UNSET. 204 static constexpr mozilla::EnumSet<CalendarField> notUnsetDefault = { 205 CalendarField::Hour, CalendarField::Minute, 206 CalendarField::Second, CalendarField::Millisecond, 207 CalendarField::Microsecond, CalendarField::Nanosecond, 208 }; 209 210 // Fields whose default value is UNSET are ignored. 211 if (notUnsetDefault.contains(field)) { 212 fields_ += field; 213 } 214 } 215 216 /** 217 * Set `field` from `source`. The field must be present in `source`. 218 */ 219 void setFrom(CalendarField field, const CalendarFields& source); 220 221 // Helper methods for WrappedPtrOperations. 222 auto eraDoNotUse() const { return &era_; } 223 auto timeZoneDoNotUse() const { return &timeZone_; } 224 225 // Trace implementation. 226 void trace(JSTracer* trc); 227 }; 228 } // namespace js::temporal 229 230 namespace js { 231 232 template <typename Wrapper> 233 class WrappedPtrOperations<temporal::CalendarFields, Wrapper> { 234 const temporal::CalendarFields& container() const { 235 return static_cast<const Wrapper*>(this)->get(); 236 } 237 238 public: 239 JS::Handle<JSString*> era() const { 240 return JS::Handle<JSString*>::fromMarkedLocation(container().eraDoNotUse()); 241 } 242 double eraYear() const { return container().eraYear(); } 243 double year() const { return container().year(); } 244 double month() const { return container().month(); } 245 temporal::MonthCodeField monthCode() const { return container().monthCode(); } 246 double day() const { return container().day(); } 247 double hour() const { return container().hour(); } 248 double minute() const { return container().minute(); } 249 double second() const { return container().second(); } 250 double millisecond() const { return container().millisecond(); } 251 double microsecond() const { return container().microsecond(); } 252 double nanosecond() const { return container().nanosecond(); } 253 temporal::OffsetField offset() const { return container().offset(); } 254 JS::Handle<temporal::TimeZoneValue> timeZone() const { 255 return JS::Handle<temporal::TimeZoneValue>::fromMarkedLocation( 256 container().timeZoneDoNotUse()); 257 } 258 259 bool has(temporal::CalendarField field) const { 260 return container().has(field); 261 } 262 auto keys() const { return container().keys(); } 263 }; 264 265 template <typename Wrapper> 266 class MutableWrappedPtrOperations<temporal::CalendarFields, Wrapper> 267 : public WrappedPtrOperations<temporal::CalendarFields, Wrapper> { 268 temporal::CalendarFields& container() { 269 return static_cast<Wrapper*>(this)->get(); 270 } 271 272 public: 273 void setEra(JSString* era) { container().setEra(era); } 274 void setEraYear(double eraYear) { container().setEraYear(eraYear); } 275 void setYear(double year) { container().setYear(year); } 276 void setMonth(double month) { container().setMonth(month); } 277 void setMonthCode(temporal::MonthCodeField monthCode) { 278 container().setMonthCode(monthCode); 279 } 280 void setDay(double day) { container().setDay(day); } 281 void setHour(double hour) { container().setHour(hour); } 282 void setMinute(double minute) { container().setMinute(minute); } 283 void setSecond(double second) { container().setSecond(second); } 284 void setMillisecond(double millisecond) { 285 container().setMillisecond(millisecond); 286 } 287 void setMicrosecond(double microsecond) { 288 container().setMicrosecond(microsecond); 289 } 290 void setNanosecond(double nanosecond) { 291 container().setNanosecond(nanosecond); 292 } 293 void setOffset(temporal::OffsetField offset) { 294 container().setOffset(offset); 295 } 296 void setTimeZone(const temporal::TimeZoneValue& timeZone) { 297 container().setTimeZone(timeZone); 298 } 299 300 void setDefault(temporal::CalendarField field) { 301 container().setDefault(field); 302 } 303 }; 304 305 } // namespace js 306 307 namespace js::temporal { 308 309 class CalendarValue; 310 class PlainDate; 311 class PlainDateTime; 312 class PlainMonthDay; 313 class PlainYearMonth; 314 315 /** 316 * PrepareCalendarFields ( calendar, fields, calendarFieldNames, 317 * nonCalendarFieldNames, requiredFieldNames ) 318 */ 319 bool PrepareCalendarFields(JSContext* cx, JS::Handle<CalendarValue> calendar, 320 JS::Handle<JSObject*> fields, 321 mozilla::EnumSet<CalendarField> fieldNames, 322 mozilla::EnumSet<CalendarField> requiredFields, 323 JS::MutableHandle<CalendarFields> result); 324 325 /** 326 * PrepareCalendarFields ( calendar, fields, calendarFieldNames, 327 * nonCalendarFieldNames, requiredFieldNames ) 328 */ 329 inline bool PrepareCalendarFields(JSContext* cx, 330 JS::Handle<CalendarValue> calendar, 331 JS::Handle<JSObject*> fields, 332 mozilla::EnumSet<CalendarField> fieldNames, 333 JS::MutableHandle<CalendarFields> result) { 334 return PrepareCalendarFields(cx, calendar, fields, fieldNames, {}, result); 335 } 336 337 /** 338 * PrepareCalendarFields ( calendar, fields, calendarFieldNames, 339 * nonCalendarFieldNames, requiredFieldNames ) 340 */ 341 bool PreparePartialCalendarFields(JSContext* cx, 342 JS::Handle<CalendarValue> calendar, 343 JS::Handle<JSObject*> fields, 344 mozilla::EnumSet<CalendarField> fieldNames, 345 JS::MutableHandle<CalendarFields> result); 346 347 /** 348 * CalendarMergeFields ( calendar, fields, additionalFields ) 349 */ 350 CalendarFields CalendarMergeFields(const CalendarValue& calendar, 351 const CalendarFields& fields, 352 const CalendarFields& additionalFields); 353 354 /** 355 * ISODateToFields ( calendar, isoDate, type ) 356 */ 357 bool ISODateToFields(JSContext* cx, Handle<PlainDate> date, 358 MutableHandle<CalendarFields> result); 359 360 /** 361 * ISODateToFields ( calendar, isoDate, type ) 362 */ 363 bool ISODateToFields(JSContext* cx, Handle<PlainDateTime> dateTime, 364 MutableHandle<CalendarFields> result); 365 366 /** 367 * ISODateToFields ( calendar, isoDate, type ) 368 */ 369 bool ISODateToFields(JSContext* cx, Handle<PlainMonthDay> monthDay, 370 MutableHandle<CalendarFields> result); 371 372 /** 373 * ISODateToFields ( calendar, isoDate, type ) 374 */ 375 bool ISODateToFields(JSContext* cx, Handle<PlainYearMonth> yearMonth, 376 MutableHandle<CalendarFields> result); 377 378 } /* namespace js::temporal */ 379 380 namespace mozilla { 381 template <> 382 struct MaxContiguousEnumValue<js::temporal::CalendarField> { 383 static constexpr auto value = js::temporal::CalendarField::Year; 384 }; 385 } // namespace mozilla 386 387 #endif /* builtin_temporal_CalendarFields_h */