Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/builtin/temporal/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 17 kB image not shown  

Quelle  CalendarFields.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "builtin/temporal/CalendarFields.h"

#include "mozilla/Assertions.h"
#include "mozilla/EnumTypeTraits.h"
#include "mozilla/Maybe.h"
#include "mozilla/Range.h"
#include "mozilla/TextUtils.h"

#include <stdint.h>
#include <string_view>

#include "jspubtd.h"
#include "NamespaceImports.h"

#include "builtin/temporal/Calendar.h"
#include "builtin/temporal/Era.h"
#include "builtin/temporal/Temporal.h"
#include "builtin/temporal/TemporalParser.h"
#include "builtin/temporal/TimeZone.h"
#include "gc/Barrier.h"
#include "gc/Tracer.h"
#include "js/Conversions.h"
#include "js/ErrorReport.h"
#include "js/friend/ErrorMessages.h"
#include "js/GCAPI.h"
#include "js/Printer.h"
#include "js/RootingAPI.h"
#include "js/Value.h"
#include "util/Text.h"
#include "vm/BytecodeUtil.h"
#include "vm/JSAtomState.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/StringType.h"

#include "vm/JSObject-inl.h"
#include "vm/ObjectOperations-inl.h"

using namespace js;
using namespace js::temporal;

void CalendarFields::trace(JSTracer* trc) {
  TraceRoot(trc, &era_, "CalendarFields::era");
  timeZone_.trace(trc);
}

void CalendarFields::setFrom(CalendarField field,
                             const CalendarFields& source) {
  MOZ_ASSERT(source.has(field));

  switch (field) {
    case CalendarField::Era:
      setEra(source.era());
      return;
    case CalendarField::EraYear:
      setEraYear(source.eraYear());
      return;
    case CalendarField::Year:
      setYear(source.year());
      return;
    case CalendarField::Month:
      setMonth(source.month());
      return;
    case CalendarField::MonthCode:
      setMonthCode(source.monthCode());
      return;
    case CalendarField::Day:
      setDay(source.day());
      return;
    case CalendarField::Hour:
      setHour(source.hour());
      return;
    case CalendarField::Minute:
      setMinute(source.minute());
      return;
    case CalendarField::Second:
      setSecond(source.second());
      return;
    case CalendarField::Millisecond:
      setMillisecond(source.millisecond());
      return;
    case CalendarField::Microsecond:
      setMicrosecond(source.microsecond());
      return;
    case CalendarField::Nanosecond:
      setNanosecond(source.nanosecond());
      return;
    case CalendarField::Offset:
      setOffset(source.offset());
      return;
    case CalendarField::TimeZone:
      setTimeZone(source.timeZone());
      return;
  }
  MOZ_CRASH("invalid temporal field");
}

static PropertyName* ToPropertyName(JSContext* cx, CalendarField field) {
  switch (field) {
    case CalendarField::Era:
      return cx->names().era;
    case CalendarField::EraYear:
      return cx->names().eraYear;
    case CalendarField::Year:
      return cx->names().year;
    case CalendarField::Month:
      return cx->names().month;
    case CalendarField::MonthCode:
      return cx->names().monthCode;
    case CalendarField::Day:
      return cx->names().day;
    case CalendarField::Hour:
      return cx->names().hour;
    case CalendarField::Minute:
      return cx->names().minute;
    case CalendarField::Second:
      return cx->names().second;
    case CalendarField::Millisecond:
      return cx->names().millisecond;
    case CalendarField::Microsecond:
      return cx->names().microsecond;
    case CalendarField::Nanosecond:
      return cx->names().nanosecond;
    case CalendarField::Offset:
      return cx->names().offset;
    case CalendarField::TimeZone:
      return cx->names().timeZone;
  }
  MOZ_CRASH("invalid temporal field name");
}

static constexpr const char* ToCString(CalendarField field) {
  switch (field) {
    case CalendarField::Era:
      return "era";
    case CalendarField::EraYear:
      return "eraYear";
    case CalendarField::Year:
      return "year";
    case CalendarField::Month:
      return "month";
    case CalendarField::MonthCode:
      return "monthCode";
    case CalendarField::Day:
      return "day";
    case CalendarField::Hour:
      return "hour";
    case CalendarField::Minute:
      return "minute";
    case CalendarField::Second:
      return "second";
    case CalendarField::Millisecond:
      return "millisecond";
    case CalendarField::Microsecond:
      return "microsecond";
    case CalendarField::Nanosecond:
      return "nanosecond";
    case CalendarField::Offset:
      return "offset";
    case CalendarField::TimeZone:
      return "timeZone";
  }
  MOZ_CRASH("invalid temporal field name");
}

static constexpr bool CalendarFieldsAreSorted() {
  constexpr auto min = mozilla::ContiguousEnumValues<CalendarField>::min;
  constexpr auto max = mozilla::ContiguousEnumValues<CalendarField>::max;

  auto field = min;
  while (field != max) {
    auto next = static_cast<CalendarField>(mozilla::UnderlyingValue(field) + 1);

    auto a = std::string_view{ToCString(field)};
    auto b = std::string_view{ToCString(next)};
    if (a.compare(b) >= 0) {
      return false;
    }
    field = next;
  }
  return true;
}

/**
 * CalendarExtraFields ( calendar, fields )
 */

static mozilla::EnumSet<CalendarField> CalendarExtraFields(
    CalendarId calendar, mozilla::EnumSet<CalendarField> fields) {
  // Step 1.
  if (calendar == CalendarId::ISO8601) {
    return {};
  }

  // Step 2.

  // "era" and "eraYear" are relevant for calendars supporting eras when
  // "year" is present.
  if (fields.contains(CalendarField::Year) && CalendarSupportsEra(calendar)) {
    return {CalendarField::Era, CalendarField::EraYear};
  }
  return {};
}

/**
 * ParseMonthCode ( argument )
 */

template <typename CharT>
static mozilla::Maybe<MonthCodeField> ParseMonthCode(
    mozilla::Range<const CharT> chars) {
  // Steps 1-2. (Not applicable)

  // Steps 3-6.
  if (chars.length() < 3 || chars.length() > 4) {
    return mozilla::Nothing();
  }

  // Starts with capital letter 'M'. Leap months end with capital letter 'L'.
  bool isLeapMonth = chars.length() == 4;
  if (chars[0] != 'M' || (isLeapMonth && chars[3] != 'L')) {
    return mozilla::Nothing();
  }

  // Month numbers are ASCII digits.
  if (!mozilla::IsAsciiDigit(chars[1]) || !mozilla::IsAsciiDigit(chars[2])) {
    return mozilla::Nothing();
  }

  // Steps 6-7.
  int32_t ordinal =
      AsciiDigitToNumber(chars[1]) * 10 + AsciiDigitToNumber(chars[2]);

  // Step 8.
  if (ordinal == 0 && !isLeapMonth) {
    return mozilla::Nothing();
  }

  // Step 9.
  return mozilla::Some(MonthCodeField{ordinal, isLeapMonth});
}

/**
 * ParseMonthCode ( argument )
 */

static auto ParseMonthCode(const JSLinearString* linear) {
  JS::AutoCheckCannotGC nogc;

  if (linear->hasLatin1Chars()) {
    return ParseMonthCode(linear->latin1Range(nogc));
  }
  return ParseMonthCode(linear->twoByteRange(nogc));
}

/**
 * ParseMonthCode ( argument )
 */

static bool ParseMonthCode(JSContext* cx, Handle<Value> value,
                           MonthCodeField* result) {
  auto reportInvalidMonthCode = [&](JSLinearString* monthCode) {
    if (auto code = QuoteString(cx, monthCode)) {
      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                               JSMSG_TEMPORAL_CALENDAR_INVALID_MONTHCODE,
                               code.get());
    }
    return false;
  };

  // Step 1.
  Rooted<Value> monthCode(cx, value);
  if (!ToPrimitive(cx, JSTYPE_STRING, &monthCode)) {
    return false;
  }

  // Step 2.
  if (!monthCode.isString()) {
    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, monthCode,
                     nullptr, "not a string");
    return false;
  }

  JSLinearString* monthCodeStr = monthCode.toString()->ensureLinear(cx);
  if (!monthCodeStr) {
    return false;
  }

  // Steps 3-9.
  auto parsed = ParseMonthCode(monthCodeStr);
  if (!parsed) {
    return reportInvalidMonthCode(monthCodeStr);
  }

  *result = *parsed;
  return true;
}

/**
 * ToOffsetString ( argument )
 */

static bool ToOffsetString(JSContext* cx, Handle<Value> value,
                           int64_t* result) {
  // Step 1.
  Rooted<Value> offset(cx, value);
  if (!ToPrimitive(cx, JSTYPE_STRING, &offset)) {
    return false;
  }

  // Step 2.
  if (!offset.isString()) {
    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, offset,
                     nullptr, "not a string");
    return false;
  }
  Rooted<JSString*> offsetStr(cx, offset.toString());

  // Steps 3-4.
  return ParseDateTimeUTCOffset(cx, offsetStr, result);
}

enum class Partial : bool { No, Yes };

/**
 * PrepareCalendarFields ( calendar, fields, calendarFieldNames,
 * nonCalendarFieldNames, requiredFieldNames )
 */

static bool PrepareCalendarFields(
    JSContext* cx, Handle<CalendarValue> calendar, Handle<JSObject*> fields,
    mozilla::EnumSet<CalendarField> fieldNames,
    mozilla::EnumSet<CalendarField> requiredFields, Partial partial,
    MutableHandle<CalendarFields> result) {
  MOZ_ASSERT_IF(partial == Partial::Yes, requiredFields.isEmpty());

  // Steps 1-2. (Not applicable in our implementation.)

  // Step 3.
  auto extraFieldNames = CalendarExtraFields(calendar.identifier(), fieldNames);

  // Step 4.
  fieldNames += extraFieldNames;

  // Step 5. (Not applicable in our implementation.)

  // Step 6.
  //
  // Default initialize the result.
  result.set(CalendarFields{});

  // Step 7. (Not applicable in our implementation.)

  // Step 8.
  static_assert(CalendarFieldsAreSorted(),
                "EnumSet<CalendarField> iteration is sorted");

  // Step 9.
  Rooted<Value> value(cx);
  for (auto fieldName : fieldNames) {
    auto* propertyName = ToPropertyName(cx, fieldName);
    const auto* cstr = ToCString(fieldName);

    // Step 9.a. (Not applicable in our implementation.)

    // Step 9.b.
    if (!GetProperty(cx, fields, fields, propertyName, &value)) {
      return false;
    }

    // Steps 9.c-d.
    if (!value.isUndefined()) {
      // Steps 9.c.i-ii. (Not applicable in our implementation.)

      // Steps 9.c.iii-ix.
      switch (fieldName) {
        case CalendarField::Era: {
          JSString* era = ToString(cx, value);
          if (!era) {
            return false;
          }
          result.setEra(era);
          break;
        }
        case CalendarField::EraYear: {
          double eraYear;
          if (!ToIntegerWithTruncation(cx, value, cstr, &eraYear)) {
            return false;
          }
          result.setEraYear(eraYear);
          break;
        }
        case CalendarField::Year: {
          double year;
          if (!ToIntegerWithTruncation(cx, value, cstr, &year)) {
            return false;
          }
          result.setYear(year);
          break;
        }
        case CalendarField::Month: {
          double month;
          if (!ToPositiveIntegerWithTruncation(cx, value, cstr, &month)) {
            return false;
          }
          result.setMonth(month);
          break;
        }
        case CalendarField::MonthCode: {
          MonthCodeField monthCode;
          if (!ParseMonthCode(cx, value, &monthCode)) {
            return false;
          }
          result.setMonthCode(monthCode);
          break;
        }
        case CalendarField::Day: {
          double day;
          if (!ToPositiveIntegerWithTruncation(cx, value, cstr, &day)) {
            return false;
          }
          result.setDay(day);
          break;
        }
        case CalendarField::Hour: {
          double hour;
          if (!ToIntegerWithTruncation(cx, value, cstr, &hour)) {
            return false;
          }
          result.setHour(hour);
          break;
        }
        case CalendarField::Minute: {
          double minute;
          if (!ToIntegerWithTruncation(cx, value, cstr, &minute)) {
            return false;
          }
          result.setMinute(minute);
          break;
        }
        case CalendarField::Second: {
          double second;
          if (!ToIntegerWithTruncation(cx, value, cstr, &second)) {
            return false;
          }
          result.setSecond(second);
          break;
        }
        case CalendarField::Millisecond: {
          double millisecond;
          if (!ToIntegerWithTruncation(cx, value, cstr, &millisecond)) {
            return false;
          }
          result.setMillisecond(millisecond);
          break;
        }
        case CalendarField::Microsecond: {
          double microsecond;
          if (!ToIntegerWithTruncation(cx, value, cstr, µsecond)) {
            return false;
          }
          result.setMicrosecond(microsecond);
          break;
        }
        case CalendarField::Nanosecond: {
          double nanosecond;
          if (!ToIntegerWithTruncation(cx, value, cstr, &nanosecond)) {
            return false;
          }
          result.setNanosecond(nanosecond);
          break;
        }
        case CalendarField::Offset: {
          int64_t offset;
          if (!ToOffsetString(cx, value, &offset)) {
            return false;
          }
          result.setOffset(OffsetField{offset});
          break;
        }
        case CalendarField::TimeZone:
          Rooted<TimeZoneValue> timeZone(cx);
          if (!ToTemporalTimeZone(cx, value, &timeZone)) {
            return false;
          }
          result.setTimeZone(timeZone);
          break;
      }
    } else if (partial == Partial::No) {
      // Step 9.d.i.
      if (requiredFields.contains(fieldName)) {
        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                  JSMSG_TEMPORAL_MISSING_PROPERTY, cstr);
        return false;
      }

      // Step 9.d.ii.
      result.setDefault(fieldName);
    }
  }

  // Step 10.
  if (partial == Partial::Yes && result.keys().isEmpty()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_TEMPORAL_MISSING_TEMPORAL_FIELDS);
    return false;
  }

  // Step 11.
  return true;
}

/**
 * PrepareCalendarFields ( calendar, fields, calendarFieldNames,
 * nonCalendarFieldNames, requiredFieldNames )
 */

bool js::temporal::PrepareCalendarFields(
    JSContext* cx, Handle<CalendarValue> calendar, Handle<JSObject*> fields,
    mozilla::EnumSet<CalendarField> fieldNames,
    mozilla::EnumSet<CalendarField> requiredFields,
    MutableHandle<CalendarFields> result) {
  return PrepareCalendarFields(cx, calendar, fields, fieldNames, requiredFields,
                               Partial::No, result);
}

/**
 * PrepareCalendarFields ( calendar, fields, calendarFieldNames,
 * nonCalendarFieldNames, requiredFieldNames )
 */

bool js::temporal::PreparePartialCalendarFields(
    JSContext* cx, Handle<CalendarValue> calendar, Handle<JSObject*> fields,
    mozilla::EnumSet<CalendarField> fieldNames,
    JS::MutableHandle<CalendarFields> result) {
  return PrepareCalendarFields(cx, calendar, fields, fieldNames, {},
                               Partial::Yes, result);
}

/**
 * NonISOFieldKeysToIgnore ( calendar, keys )
 */

static auto NonISOFieldKeysToIgnore(CalendarId calendar,
                                    mozilla::EnumSet<CalendarField> keys) {
  static constexpr auto eraOrEraYear = mozilla::EnumSet{
      CalendarField::Era,
      CalendarField::EraYear,
  };

  static constexpr auto eraOrAnyYear = mozilla::EnumSet{
      CalendarField::Era,
      CalendarField::EraYear,
      CalendarField::Year,
  };

  static constexpr auto monthOrMonthCode = mozilla::EnumSet{
      CalendarField::Month,
      CalendarField::MonthCode,
  };

  static constexpr auto dayOrAnyMonth = mozilla::EnumSet{
      CalendarField::Day,
      CalendarField::Month,
      CalendarField::MonthCode,
  };

  // A field always invalidates at least itself, so start with ignoring all
  // input fields.
  auto result = keys;

  // "month" and "monthCode" are mutually exclusive.
  if (!(keys & monthOrMonthCode).isEmpty()) {
    result += monthOrMonthCode;
  }

  // "era", "eraYear", and "year" are mutually exclusive when the calendar
  // supports eras.
  if (CalendarSupportsEra(calendar) && !(keys & eraOrAnyYear).isEmpty()) {
    result += eraOrAnyYear;
  }

  // If eras can start in the middle of the year, we have to ignore "era" and
  // "eraYear" if any of "day", "month", or "monthCode" is present.
  if (CalendarHasMidYearEras(calendar) && !(keys & dayOrAnyMonth).isEmpty()) {
    result += eraOrEraYear;
  }

  return result;
}

/**
 * CalendarFieldKeysToIgnore ( calendar, keys )
 */

static auto CalendarFieldKeysToIgnore(CalendarId calendar,
                                      mozilla::EnumSet<CalendarField> keys) {
  // Step 1.
  if (calendar == CalendarId::ISO8601) {
    // Steps 1.a and 1.b.i.
    auto ignoredKeys = keys;

    // Step 1.b.ii.
    if (keys.contains(CalendarField::Month)) {
      ignoredKeys += CalendarField::MonthCode;
    }

    // Step 1.b.iii.
    else if (keys.contains(CalendarField::MonthCode)) {
      ignoredKeys += CalendarField::Month;
    }

    // Steps 1.c-d.
    return ignoredKeys;
  }

  // Step 2.
  return NonISOFieldKeysToIgnore(calendar, keys);
}

/**
 * CalendarMergeFields ( calendar, fields, additionalFields )
 */

CalendarFields js::temporal::CalendarMergeFields(
    const CalendarValue& calendar, const CalendarFields& fields,
    const CalendarFields& additionalFields) {
  auto calendarId = calendar.identifier();

  // Steps 1.
  auto additionalKeys = additionalFields.keys();

  // Step 2.
  auto overriddenKeys = CalendarFieldKeysToIgnore(calendarId, additionalKeys);
  MOZ_ASSERT(overriddenKeys.contains(additionalKeys));

  // Step 3.
  auto merged = CalendarFields{};

  // Step 4.
  auto fieldsKeys = fields.keys();

  // Step 5.b.
  for (auto key : (fieldsKeys - overriddenKeys)) {
    merged.setFrom(key, fields);
  }

  // Step 5.c.
  for (auto key : additionalKeys) {
    merged.setFrom(key, additionalFields);
  }

  // Step 6.
  return merged;
}

Messung V0.5 in Prozent
C=90 H=98 G=94

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.