1872 lines
		
	
	
		
			81 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			1872 lines
		
	
	
		
			81 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From be79a15040dd992cdac91c59d481fea5d7209df3 Mon Sep 17 00:00:00 2001
 | |
| From: Michael Catanzaro <mcatanzaro@redhat.com>
 | |
| Date: Thu, 27 Mar 2025 15:56:41 -0500
 | |
| Subject: [PATCH 1/5] Revert "[CMake] Update minimum ICU version to 70.1"
 | |
| 
 | |
| This reverts commit 95d71be25d5b838b1171e6b9b2cd526190118fba.
 | |
| ---
 | |
|  Source/cmake/OptionsGTK.cmake | 6 +++---
 | |
|  1 file changed, 3 insertions(+), 3 deletions(-)
 | |
| 
 | |
| diff --git a/Source/cmake/OptionsGTK.cmake b/Source/cmake/OptionsGTK.cmake
 | |
| index 285ab3737bfa..83377867f3b1 100644
 | |
| --- a/Source/cmake/OptionsGTK.cmake
 | |
| +++ b/Source/cmake/OptionsGTK.cmake
 | |
| @@ -10,11 +10,11 @@ set(USER_AGENT_BRANDING "" CACHE STRING "Branding to add to user agent string")
 | |
|  find_package(Cairo 1.14.0 REQUIRED)
 | |
|  find_package(LibGcrypt 1.7.0 REQUIRED)
 | |
|  find_package(Libtasn1 REQUIRED)
 | |
| -find_package(HarfBuzz 2.7.4 REQUIRED COMPONENTS ICU)
 | |
| -find_package(ICU 70.1 REQUIRED COMPONENTS data i18n uc)
 | |
| +find_package(HarfBuzz 1.4.2 REQUIRED COMPONENTS ICU)
 | |
| +find_package(ICU 61.2 REQUIRED COMPONENTS data i18n uc)
 | |
|  find_package(JPEG REQUIRED)
 | |
|  find_package(Epoxy 1.5.4 REQUIRED)
 | |
| -find_package(LibXml2 2.9.13 REQUIRED)
 | |
| +find_package(LibXml2 2.8.0 REQUIRED)
 | |
|  find_package(PNG REQUIRED)
 | |
|  find_package(SQLite3 REQUIRED)
 | |
|  find_package(Threads REQUIRED)
 | |
| -- 
 | |
| 2.51.0
 | |
| 
 | |
| 
 | |
| From 4c60130350756d48e7f8ed85b4d86d2b049da332 Mon Sep 17 00:00:00 2001
 | |
| From: Michael Catanzaro <mcatanzaro@redhat.com>
 | |
| Date: Fri, 28 Mar 2025 14:45:03 -0500
 | |
| Subject: [PATCH 2/5] Revert "[JSC] Remove unnecessary ICU version checks"
 | |
| 
 | |
| This reverts commit af62f09a1fad0b72293a7f0d082704d92116cb9a.
 | |
| ---
 | |
|  .../runtime/IntlDurationFormat.cpp            |  2 +-
 | |
|  .../runtime/IntlNumberFormat.cpp              | 12 ++--
 | |
|  .../runtime/IntlNumberFormatInlines.h         | 57 ++++++++++++-------
 | |
|  3 files changed, 47 insertions(+), 24 deletions(-)
 | |
| 
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp b/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp
 | |
| index 9bf431425007..17c5b1b848d7 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp
 | |
| @@ -457,7 +457,7 @@ static Vector<Element> collectElements(JSGlobalObject* globalObject, const IntlD
 | |
|  
 | |
|          // 3.k. If style is "2-digit", then
 | |
|          //     i. Perform ! CreateDataPropertyOrThrow(nfOpts, "minimumIntegerDigits", 2F).
 | |
| -        skeletonBuilder.append(" integer-width/*"_s);
 | |
| +        skeletonBuilder.append(" integer-width/"_s, WTF::ICU::majorVersion() >= 67 ? '*' : '+'); // Prior to ICU 67, use the symbol + instead of *.
 | |
|          if (style == IntlDurationFormat::UnitStyle::TwoDigit)
 | |
|              skeletonBuilder.append("00"_s);
 | |
|          else
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp b/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp
 | |
| index b05c7813294c..c16e6bcee859 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp
 | |
| @@ -496,10 +496,14 @@ void IntlNumberFormat::initializeNumberFormat(JSGlobalObject* globalObject, JSVa
 | |
|              skeletonBuilder.append(" sign-except-zero"_s);
 | |
|          break;
 | |
|      case SignDisplay::Negative:
 | |
| -        if (useAccounting)
 | |
| -            skeletonBuilder.append(" sign-accounting-negative"_s);
 | |
| -        else
 | |
| -            skeletonBuilder.append(" sign-negative"_s);
 | |
| +        // Only ICU69~ supports negative sign display. Ignore this option if linked ICU does not support it.
 | |
| +        // https://github.com/unicode-org/icu/commit/1aa0dad8e06ecc99bff442dd37f6daa2d39d9a5a
 | |
| +        if (WTF::ICU::majorVersion() >= 69) {
 | |
| +            if (useAccounting)
 | |
| +                skeletonBuilder.append(" sign-accounting-negative"_s);
 | |
| +            else
 | |
| +                skeletonBuilder.append(" sign-negative"_s);
 | |
| +        }
 | |
|          break;
 | |
|      }
 | |
|  
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormatInlines.h b/Source/JavaScriptCore/runtime/IntlNumberFormatInlines.h
 | |
| index c640b73cc891..e678978e453d 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlNumberFormatInlines.h
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlNumberFormatInlines.h
 | |
| @@ -200,12 +200,24 @@ void appendNumberFormatDigitOptionsToSkeleton(IntlType* intlInstance, StringBuil
 | |
|      case RoundingMode::Trunc:
 | |
|          skeletonBuilder.append(" rounding-mode-down"_s);
 | |
|          break;
 | |
| -    case RoundingMode::HalfCeil:
 | |
| -        skeletonBuilder.append(" rounding-mode-half-ceiling"_s);
 | |
| +    case RoundingMode::HalfCeil: {
 | |
| +        // Only ICU69~ supports half-ceiling. Ignore this option if linked ICU does not support it.
 | |
| +        // https://github.com/unicode-org/icu/commit/e8dfea9bb6bb27596731173b352759e44ad06b21
 | |
| +        if (WTF::ICU::majorVersion() >= 69)
 | |
| +            skeletonBuilder.append(" rounding-mode-half-ceiling"_s);
 | |
| +        else
 | |
| +            skeletonBuilder.append(" rounding-mode-half-up"_s); // Default option.
 | |
|          break;
 | |
| -    case RoundingMode::HalfFloor:
 | |
| -        skeletonBuilder.append(" rounding-mode-half-floor"_s);
 | |
| +    }
 | |
| +    case RoundingMode::HalfFloor: {
 | |
| +        // Only ICU69~ supports half-flooring. Ignore this option if linked ICU does not support it.
 | |
| +        // https://github.com/unicode-org/icu/commit/e8dfea9bb6bb27596731173b352759e44ad06b21
 | |
| +        if (WTF::ICU::majorVersion() >= 69)
 | |
| +            skeletonBuilder.append(" rounding-mode-half-floor"_s);
 | |
| +        else
 | |
| +            skeletonBuilder.append(" rounding-mode-half-up"_s); // Default option.
 | |
|          break;
 | |
| +    }
 | |
|      case RoundingMode::HalfExpand:
 | |
|          skeletonBuilder.append(" rounding-mode-half-up"_s);
 | |
|          break;
 | |
| @@ -218,7 +230,7 @@ void appendNumberFormatDigitOptionsToSkeleton(IntlType* intlInstance, StringBuil
 | |
|      }
 | |
|  
 | |
|      // https://github.com/unicode-org/icu/blob/master/docs/userguide/format_parse/numbers/skeletons.md#integer-width
 | |
| -    skeletonBuilder.append(" integer-width/*"_s);
 | |
| +    skeletonBuilder.append(" integer-width/"_s, WTF::ICU::majorVersion() >= 67 ? '*' : '+'); // Prior to ICU 67, use the symbol + instead of *.
 | |
|      for (unsigned i = 0; i < intlInstance->m_minimumIntegerDigits; ++i)
 | |
|          skeletonBuilder.append('0');
 | |
|  
 | |
| @@ -256,19 +268,23 @@ void appendNumberFormatDigitOptionsToSkeleton(IntlType* intlInstance, StringBuil
 | |
|          }
 | |
|          case IntlRoundingType::MorePrecision:
 | |
|          case IntlRoundingType::LessPrecision:
 | |
| -            // https://github.com/unicode-org/icu/commit/d7db6c1f8655bb53153695b09a50029fd04a8364
 | |
| -            // https://github.com/unicode-org/icu/blob/main/docs/userguide/format_parse/numbers/skeletons.md#precision
 | |
| -            skeletonBuilder.append(" ."_s);
 | |
| -            for (unsigned i = 0; i < intlInstance->m_minimumFractionDigits; ++i)
 | |
| -                skeletonBuilder.append('0');
 | |
| -            for (unsigned i = 0; i < intlInstance->m_maximumFractionDigits - intlInstance->m_minimumFractionDigits; ++i)
 | |
| -                skeletonBuilder.append('#');
 | |
| -            skeletonBuilder.append('/');
 | |
| -            for (unsigned i = 0; i < intlInstance->m_minimumSignificantDigits; ++i)
 | |
| -                skeletonBuilder.append('@');
 | |
| -            for (unsigned i = 0; i < intlInstance->m_maximumSignificantDigits - intlInstance->m_minimumSignificantDigits; ++i)
 | |
| -                skeletonBuilder.append('#');
 | |
| -            skeletonBuilder.append(intlInstance->m_roundingType == IntlRoundingType::MorePrecision ? 'r' : 's');
 | |
| +            // Before Intl.NumberFormat v3, it was CompactRounding mode, where we do not configure anything.
 | |
| +            // So, if linked ICU is ~68, we do nothing.
 | |
| +            if (WTF::ICU::majorVersion() >= 69) {
 | |
| +                // https://github.com/unicode-org/icu/commit/d7db6c1f8655bb53153695b09a50029fd04a8364
 | |
| +                // https://github.com/unicode-org/icu/blob/main/docs/userguide/format_parse/numbers/skeletons.md#precision
 | |
| +                skeletonBuilder.append(" ."_s);
 | |
| +                for (unsigned i = 0; i < intlInstance->m_minimumFractionDigits; ++i)
 | |
| +                    skeletonBuilder.append('0');
 | |
| +                for (unsigned i = 0; i < intlInstance->m_maximumFractionDigits - intlInstance->m_minimumFractionDigits; ++i)
 | |
| +                    skeletonBuilder.append('#');
 | |
| +                skeletonBuilder.append('/');
 | |
| +                for (unsigned i = 0; i < intlInstance->m_minimumSignificantDigits; ++i)
 | |
| +                    skeletonBuilder.append('@');
 | |
| +                for (unsigned i = 0; i < intlInstance->m_maximumSignificantDigits - intlInstance->m_minimumSignificantDigits; ++i)
 | |
| +                    skeletonBuilder.append('#');
 | |
| +                skeletonBuilder.append(intlInstance->m_roundingType == IntlRoundingType::MorePrecision ? 'r' : 's');
 | |
| +            }
 | |
|              break;
 | |
|          }
 | |
|      }
 | |
| @@ -279,7 +295,10 @@ void appendNumberFormatDigitOptionsToSkeleton(IntlType* intlInstance, StringBuil
 | |
|      case IntlTrailingZeroDisplay::Auto:
 | |
|          break;
 | |
|      case IntlTrailingZeroDisplay::StripIfInteger:
 | |
| -        skeletonBuilder.append("/w"_s);
 | |
| +        // Only ICU69~ supports trailing zero display. Ignore this option if linked ICU does not support it.
 | |
| +        // https://github.com/unicode-org/icu/commit/b79c299f90d4023ac237db3d0335d568bf21cd36
 | |
| +        if (WTF::ICU::majorVersion() >= 69)
 | |
| +            skeletonBuilder.append("/w"_s);
 | |
|          break;
 | |
|      }
 | |
|  }
 | |
| -- 
 | |
| 2.51.0
 | |
| 
 | |
| 
 | |
| From 6f52a2ff3327c5ef714fb385f647daceeab1680f Mon Sep 17 00:00:00 2001
 | |
| From: Michael Catanzaro <mcatanzaro@redhat.com>
 | |
| Date: Fri, 28 Mar 2025 15:01:00 -0500
 | |
| Subject: [PATCH 3/5] Revert "[JSC] TimeZone Cache should be per-process level"
 | |
| 
 | |
| This reverts commit c779aa30eced87609c7c808d672a8f23c5c4821d.
 | |
| ---
 | |
|  Source/JavaScriptCore/runtime/JSDateMath.cpp | 61 +++++++-------------
 | |
|  1 file changed, 21 insertions(+), 40 deletions(-)
 | |
| 
 | |
| diff --git a/Source/JavaScriptCore/runtime/JSDateMath.cpp b/Source/JavaScriptCore/runtime/JSDateMath.cpp
 | |
| index 96993af52d33..c40f917fc99f 100644
 | |
| --- a/Source/JavaScriptCore/runtime/JSDateMath.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/JSDateMath.cpp
 | |
| @@ -453,12 +453,9 @@ String DateCache::timeZoneDisplayName(bool isDST)
 | |
|      return m_timeZoneStandardDisplayNameCache;
 | |
|  }
 | |
|  
 | |
| -static Lock timeZoneCacheLock;
 | |
| -
 | |
|  #if PLATFORM(COCOA)
 | |
|  static void timeZoneChangeNotification(CFNotificationCenterRef, void*, CFStringRef, const void*, CFDictionaryRef)
 | |
|  {
 | |
| -    Locker locker { timeZoneCacheLock };
 | |
|      ASSERT(isMainThread());
 | |
|      ++lastTimeZoneID;
 | |
|  }
 | |
| @@ -475,40 +472,6 @@ DateCache::DateCache()
 | |
|  #endif
 | |
|  }
 | |
|  
 | |
| -static std::tuple<String, Vector<char16_t, 32>> retrieveTimeZoneInformation()
 | |
| -{
 | |
| -    Locker locker { timeZoneCacheLock };
 | |
| -    static NeverDestroyed<std::tuple<String, Vector<char16_t, 32>, uint64_t>> globalCache;
 | |
| -
 | |
| -    bool isCacheStale = true;
 | |
| -    uint64_t currentID = 0;
 | |
| -#if PLATFORM(COCOA)
 | |
| -    currentID = lastTimeZoneID.load();
 | |
| -    isCacheStale = std::get<2>(globalCache.get()) != currentID;
 | |
| -#endif
 | |
| -    if (isCacheStale) {
 | |
| -        Vector<char16_t, 32> timeZoneID;
 | |
| -        getTimeZoneOverride(timeZoneID);
 | |
| -        String canonical;
 | |
| -        UErrorCode status = U_ZERO_ERROR;
 | |
| -        if (timeZoneID.isEmpty()) {
 | |
| -            status = callBufferProducingFunction(ucal_getHostTimeZone, timeZoneID);
 | |
| -            ASSERT_UNUSED(status, U_SUCCESS(status));
 | |
| -        }
 | |
| -        if (U_SUCCESS(status)) {
 | |
| -            Vector<char16_t, 32> canonicalBuffer;
 | |
| -            auto status = callBufferProducingFunction(ucal_getCanonicalTimeZoneID, timeZoneID.mutableSpan().data(), timeZoneID.size(), canonicalBuffer, nullptr);
 | |
| -            if (U_SUCCESS(status))
 | |
| -                canonical = String(canonicalBuffer);
 | |
| -        }
 | |
| -        if (canonical.isNull() || isUTCEquivalent(canonical))
 | |
| -            canonical = "UTC"_s;
 | |
| -
 | |
| -        globalCache.get() = std::tuple { canonical.isolatedCopy(), WTFMove(timeZoneID), currentID };
 | |
| -    }
 | |
| -    return std::tuple { std::get<0>(globalCache.get()).isolatedCopy(), std::get<1>(globalCache.get()) };
 | |
| -}
 | |
| -
 | |
|  DateCache::~DateCache() = default;
 | |
|  
 | |
|  Ref<DateInstanceData> DateCache::cachedDateInstanceData(double millisecondsFromEpoch)
 | |
| @@ -519,11 +482,29 @@ Ref<DateInstanceData> DateCache::cachedDateInstanceData(double millisecondsFromE
 | |
|  void DateCache::timeZoneCacheSlow()
 | |
|  {
 | |
|      ASSERT(!m_timeZoneCache);
 | |
| -    auto [canonical, timeZoneID] = retrieveTimeZoneInformation();
 | |
| +
 | |
| +    Vector<char16_t, 32> timeZoneID;
 | |
| +    getTimeZoneOverride(timeZoneID);
 | |
|      auto* cache = new OpaqueICUTimeZone;
 | |
| -    cache->m_canonicalTimeZoneID = WTFMove(canonical);
 | |
| +
 | |
| +    String canonical;
 | |
|      UErrorCode status = U_ZERO_ERROR;
 | |
| -    cache->m_calendar = std::unique_ptr<UCalendar, ICUDeleter<ucal_close>>(ucal_open(timeZoneID.span().data(), timeZoneID.size(), "", UCAL_DEFAULT, &status));
 | |
| +    if (timeZoneID.isEmpty()) {
 | |
| +        status = callBufferProducingFunction(ucal_getHostTimeZone, timeZoneID);
 | |
| +        ASSERT_UNUSED(status, U_SUCCESS(status));
 | |
| +    }
 | |
| +    if (U_SUCCESS(status)) {
 | |
| +        Vector<char16_t, 32> canonicalBuffer;
 | |
| +        auto status = callBufferProducingFunction(ucal_getCanonicalTimeZoneID, timeZoneID.data(), timeZoneID.size(), canonicalBuffer, nullptr);
 | |
| +        if (U_SUCCESS(status))
 | |
| +            canonical = String(canonicalBuffer);
 | |
| +    }
 | |
| +    if (canonical.isNull() || isUTCEquivalent(canonical))
 | |
| +        canonical = "UTC"_s;
 | |
| +    cache->m_canonicalTimeZoneID = WTFMove(canonical);
 | |
| +
 | |
| +    status = U_ZERO_ERROR;
 | |
| +    cache->m_calendar = std::unique_ptr<UCalendar, ICUDeleter<ucal_close>>(ucal_open(timeZoneID.data(), timeZoneID.size(), "", UCAL_DEFAULT, &status));
 | |
|      ASSERT_UNUSED(status, U_SUCCESS(status));
 | |
|      ucal_setGregorianChange(cache->m_calendar.get(), minECMAScriptTime, &status); // Ignore "unsupported" error.
 | |
|      m_timeZoneCache = std::unique_ptr<OpaqueICUTimeZone, OpaqueICUTimeZoneDeleter>(cache);
 | |
| -- 
 | |
| 2.51.0
 | |
| 
 | |
| 
 | |
| From c5fb088366f855f23a2920b31744740edb5506a2 Mon Sep 17 00:00:00 2001
 | |
| From: Michael Catanzaro <mcatanzaro@redhat.com>
 | |
| Date: Fri, 28 Mar 2025 15:01:11 -0500
 | |
| Subject: [PATCH 4/5] Revert "[JSC] Rebaseline Intl implementation based on
 | |
|  lowest dependency ICU 70"
 | |
| 
 | |
| This reverts commit 31a358087be7e5e70c7a03bdfcf89de35628a2a2.
 | |
| ---
 | |
|  .../runtime/IntlDateTimeFormat.cpp            |  27 ++++
 | |
|  .../runtime/IntlDateTimeFormat.h              |   6 +
 | |
|  .../runtime/IntlDateTimeFormatPrototype.cpp   |   6 +-
 | |
|  .../runtime/IntlDurationFormat.cpp            |  27 ++++
 | |
|  .../runtime/IntlDurationFormat.h              |   2 +
 | |
|  .../JavaScriptCore/runtime/IntlListFormat.cpp |  24 +++
 | |
|  .../JavaScriptCore/runtime/IntlListFormat.h   |   6 +
 | |
|  .../runtime/IntlNumberFormat.cpp              | 150 ++++++++++++++++++
 | |
|  .../JavaScriptCore/runtime/IntlNumberFormat.h |  35 ++++
 | |
|  .../runtime/IntlNumberFormatPrototype.cpp     |  35 +++-
 | |
|  Source/JavaScriptCore/runtime/IntlObject.cpp  |   9 +-
 | |
|  .../runtime/IntlPluralRules.cpp               |  41 +++++
 | |
|  .../JavaScriptCore/runtime/IntlPluralRules.h  |  12 +-
 | |
|  .../runtime/IntlPluralRulesPrototype.cpp      |   8 +-
 | |
|  .../JavaScriptCore/runtime/IntlWorkaround.cpp |  12 +-
 | |
|  Source/JavaScriptCore/runtime/JSDateMath.cpp  |  95 ++++++++++-
 | |
|  16 files changed, 479 insertions(+), 16 deletions(-)
 | |
| 
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp b/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp
 | |
| index 202e266c55da..9a413dc037a7 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp
 | |
| @@ -42,12 +42,16 @@
 | |
|  #include <wtf/unicode/CharacterNames.h>
 | |
|  #include <wtf/unicode/icu/ICUHelpers.h>
 | |
|  
 | |
| +#if HAVE(ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS)
 | |
|  #include <unicode/uformattedvalue.h>
 | |
|  #ifdef U_HIDE_DRAFT_API
 | |
|  #undef U_HIDE_DRAFT_API
 | |
|  #endif
 | |
| +#endif // HAVE(ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS)
 | |
|  #include <unicode/udateintervalformat.h>
 | |
| +#if HAVE(ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS)
 | |
|  #define U_HIDE_DRAFT_API 1
 | |
| +#endif // HAVE(ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS)
 | |
|  
 | |
|  WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN
 | |
|  
 | |
| @@ -1430,6 +1434,8 @@ UDateIntervalFormat* IntlDateTimeFormat::createDateIntervalFormatIfNecessary(JSG
 | |
|      return m_dateIntervalFormat.get();
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS)
 | |
| +
 | |
|  static std::unique_ptr<UFormattedDateInterval, ICUDeleter<udtitvfmt_closeResult>> formattedValueFromDateRange(UDateIntervalFormat& dateIntervalFormat, UDateFormat& dateFormat, double startDate, double endDate, UErrorCode& status)
 | |
|  {
 | |
|      auto result = std::unique_ptr<UFormattedDateInterval, ICUDeleter<udtitvfmt_closeResult>>(udtitvfmt_openResult(&status));
 | |
| @@ -1508,6 +1514,8 @@ static bool dateFieldsPracticallyEqual(const UFormattedValue* formattedValue, UE
 | |
|      return !hasSpan;
 | |
|  }
 | |
|  
 | |
| +#endif // HAVE(ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS)
 | |
| +
 | |
|  JSValue IntlDateTimeFormat::formatRange(JSGlobalObject* globalObject, double startDate, double endDate)
 | |
|  {
 | |
|      ASSERT(m_dateFormat);
 | |
| @@ -1526,6 +1534,7 @@ JSValue IntlDateTimeFormat::formatRange(JSGlobalObject* globalObject, double sta
 | |
|      auto* dateIntervalFormat = createDateIntervalFormatIfNecessary(globalObject);
 | |
|      RETURN_IF_EXCEPTION(scope, { });
 | |
|  
 | |
| +#if HAVE(ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS)
 | |
|      UErrorCode status = U_ZERO_ERROR;
 | |
|      auto result = formattedValueFromDateRange(*dateIntervalFormat, *m_dateFormat, startDate, endDate, status);
 | |
|      if (U_FAILURE(status)) {
 | |
| @@ -1563,6 +1572,17 @@ JSValue IntlDateTimeFormat::formatRange(JSGlobalObject* globalObject, double sta
 | |
|      replaceNarrowNoBreakSpaceOrThinSpaceWithNormalSpace(buffer);
 | |
|  
 | |
|      return jsString(vm, String(WTFMove(buffer)));
 | |
| +#else
 | |
| +    Vector<UChar, 32> buffer;
 | |
| +    auto status = callBufferProducingFunction(udtitvfmt_format, dateIntervalFormat, startDate, endDate, buffer, nullptr);
 | |
| +    if (U_FAILURE(status)) {
 | |
| +        throwTypeError(globalObject, scope, "Failed to format date interval"_s);
 | |
| +        return { };
 | |
| +    }
 | |
| +    replaceNarrowNoBreakSpaceOrThinSpaceWithNormalSpace(buffer);
 | |
| +
 | |
| +    return jsString(vm, String(WTFMove(buffer)));
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  JSValue IntlDateTimeFormat::formatRangeToParts(JSGlobalObject* globalObject, double startDate, double endDate)
 | |
| @@ -1572,6 +1592,7 @@ JSValue IntlDateTimeFormat::formatRangeToParts(JSGlobalObject* globalObject, dou
 | |
|      VM& vm = globalObject->vm();
 | |
|      auto scope = DECLARE_THROW_SCOPE(vm);
 | |
|  
 | |
| +#if HAVE(ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS)
 | |
|      // http://tc39.es/proposal-intl-DateTimeFormat-formatRange/#sec-partitiondatetimerangepattern
 | |
|      startDate = timeClip(startDate);
 | |
|      endDate = timeClip(endDate);
 | |
| @@ -1775,6 +1796,12 @@ JSValue IntlDateTimeFormat::formatRangeToParts(JSGlobalObject* globalObject, dou
 | |
|      }
 | |
|  
 | |
|      return parts;
 | |
| +#else
 | |
| +    UNUSED_PARAM(startDate);
 | |
| +    UNUSED_PARAM(endDate);
 | |
| +    throwTypeError(globalObject, scope, "Failed to format date interval"_s);
 | |
| +    return { };
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlDateTimeFormat.h b/Source/JavaScriptCore/runtime/IntlDateTimeFormat.h
 | |
| index 782b30b0d2b1..cc81be712327 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlDateTimeFormat.h
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlDateTimeFormat.h
 | |
| @@ -32,6 +32,12 @@
 | |
|  
 | |
|  struct UDateIntervalFormat;
 | |
|  
 | |
| +#if !defined(HAVE_ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS)
 | |
| +#if U_ICU_VERSION_MAJOR_NUM >= 64
 | |
| +#define HAVE_ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS 1
 | |
| +#endif
 | |
| +#endif
 | |
| +
 | |
|  namespace JSC {
 | |
|  
 | |
|  enum class RelevantExtensionKey : uint8_t;
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.cpp b/Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.cpp
 | |
| index 7a739f61e2c1..da457ad03212 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlDateTimeFormatPrototype.cpp
 | |
| @@ -56,7 +56,6 @@ const ClassInfo IntlDateTimeFormatPrototype::s_info = { "Intl.DateTimeFormat"_s,
 | |
|  @begin dateTimeFormatPrototypeTable
 | |
|    format                intlDateTimeFormatPrototypeGetterFormat              DontEnum|ReadOnly|CustomAccessor
 | |
|    formatRange           intlDateTimeFormatPrototypeFuncFormatRange           DontEnum|Function 2
 | |
| -  formatRangeToParts    intlDateTimeFormatPrototypeFuncFormatRangeToParts    DontEnum|Function 2
 | |
|    formatToParts         intlDateTimeFormatPrototypeFuncFormatToParts         DontEnum|Function 1
 | |
|    resolvedOptions       intlDateTimeFormatPrototypeFuncResolvedOptions       DontEnum|Function 0
 | |
|  @end
 | |
| @@ -83,7 +82,12 @@ void IntlDateTimeFormatPrototype::finishCreation(VM& vm, JSGlobalObject* globalO
 | |
|  {
 | |
|      Base::finishCreation(vm);
 | |
|      ASSERT(inherits(info()));
 | |
| +#if HAVE(ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS)
 | |
| +    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("formatRangeToParts"_s, intlDateTimeFormatPrototypeFuncFormatRangeToParts, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, ImplementationVisibility::Public);
 | |
| +#else
 | |
|      UNUSED_PARAM(globalObject);
 | |
| +    UNUSED_PARAM(&intlDateTimeFormatPrototypeFuncFormatRangeToParts);
 | |
| +#endif
 | |
|      JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
 | |
|  }
 | |
|  
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp b/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp
 | |
| index 17c5b1b848d7..c066e80ab130 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp
 | |
| @@ -36,14 +36,21 @@
 | |
|  // While UListFormatter APIs are draft in ICU 67, they are stable in ICU 68 with the same function signatures.
 | |
|  // So we can assume that these signatures of draft APIs are stable.
 | |
|  // If UListFormatter is available, UNumberFormatter is also available.
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|  #ifdef U_HIDE_DRAFT_API
 | |
|  #undef U_HIDE_DRAFT_API
 | |
|  #endif
 | |
| +#endif
 | |
|  #include <unicode/ulistformatter.h>
 | |
|  #include <unicode/unumberformatter.h>
 | |
|  #include <unicode/ures.h>
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|  #define U_HIDE_DRAFT_API 1
 | |
| +#endif
 | |
| +
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|  #include <unicode/uformattedvalue.h>
 | |
| +#endif
 | |
|  
 | |
|  WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN
 | |
|  
 | |
| @@ -240,6 +247,7 @@ void IntlDurationFormat::initializeDurationFormat(JSGlobalObject* globalObject,
 | |
|      m_fractionalDigits = intlNumberOption(globalObject, options, vm.propertyNames->fractionalDigits, 0, 9, fractionalDigitsUndefinedValue);
 | |
|      RETURN_IF_EXCEPTION(scope, void());
 | |
|  
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|      {
 | |
|          auto toUListFormatterWidth = [](Style style) {
 | |
|              // 6. Let listStyle be durationFormat.[[Style]].
 | |
| @@ -266,8 +274,15 @@ void IntlDurationFormat::initializeDurationFormat(JSGlobalObject* globalObject,
 | |
|              return;
 | |
|          }
 | |
|      }
 | |
| +#else
 | |
| +    UNUSED_PARAM(IntlDurationFormatInternal::verbose);
 | |
| +    throwTypeError(globalObject, scope, "Failed to initialize Intl.DurationFormat since this feature is not supported in the linked ICU version"_s);
 | |
| +    return;
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
| +
 | |
|  static String retrieveSeparator(const CString& locale, const String& numberingSystem)
 | |
|  {
 | |
|      ASCIILiteral fallbackTimeSeparator = ":"_s;
 | |
| @@ -635,12 +650,15 @@ static Vector<Element> collectElements(JSGlobalObject* globalObject, const IntlD
 | |
|      return elements;
 | |
|  }
 | |
|  
 | |
| +#endif
 | |
| +
 | |
|  // https://tc39.es/proposal-intl-duration-format/#sec-Intl.DurationFormat.prototype.format
 | |
|  JSValue IntlDurationFormat::format(JSGlobalObject* globalObject, ISO8601::Duration duration) const
 | |
|  {
 | |
|      VM& vm = globalObject->vm();
 | |
|      auto scope = DECLARE_THROW_SCOPE(vm);
 | |
|  
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|      auto elements = collectElements(globalObject, this, WTFMove(duration));
 | |
|      RETURN_IF_EXCEPTION(scope, { });
 | |
|  
 | |
| @@ -676,6 +694,10 @@ JSValue IntlDurationFormat::format(JSGlobalObject* globalObject, ISO8601::Durati
 | |
|          return throwTypeError(globalObject, scope, "failed to format list of strings"_s);
 | |
|  
 | |
|      return jsString(vm, String(WTFMove(result)));
 | |
| +#else
 | |
| +    UNUSED_PARAM(duration);
 | |
| +    return throwTypeError(globalObject, scope, "failed to format list of strings"_s);
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  // https://tc39.es/proposal-intl-duration-format/#sec-Intl.DurationFormat.prototype.formatToParts
 | |
| @@ -684,6 +706,7 @@ JSValue IntlDurationFormat::formatToParts(JSGlobalObject* globalObject, ISO8601:
 | |
|      VM& vm = globalObject->vm();
 | |
|      auto scope = DECLARE_THROW_SCOPE(vm);
 | |
|  
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|      auto elements = collectElements(globalObject, this, WTFMove(duration));
 | |
|      RETURN_IF_EXCEPTION(scope, { });
 | |
|  
 | |
| @@ -836,6 +859,10 @@ JSValue IntlDurationFormat::formatToParts(JSGlobalObject* globalObject, ISO8601:
 | |
|      }
 | |
|  
 | |
|      return parts;
 | |
| +#else
 | |
| +    UNUSED_PARAM(duration);
 | |
| +    return throwTypeError(globalObject, scope, "failed to format list of strings"_s);
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  // https://tc39.es/proposal-intl-duration-format/#sec-Intl.DurationFormat.prototype.resolvedOptions
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlDurationFormat.h b/Source/JavaScriptCore/runtime/IntlDurationFormat.h
 | |
| index 69e64f9c8332..b3f781a54ad4 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlDurationFormat.h
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlDurationFormat.h
 | |
| @@ -101,7 +101,9 @@ private:
 | |
|      static ASCIILiteral unitStyleString(UnitStyle);
 | |
|      static ASCIILiteral displayString(Display);
 | |
|  
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|      std::unique_ptr<UListFormatter, UListFormatterDeleter> m_listFormat;
 | |
| +#endif
 | |
|      String m_locale;
 | |
|      String m_numberingSystem;
 | |
|      CString m_dataLocaleWithExtensions;
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlListFormat.cpp b/Source/JavaScriptCore/runtime/IntlListFormat.cpp
 | |
| index 8bc64220a7c7..c24073ce3171 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlListFormat.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlListFormat.cpp
 | |
| @@ -33,12 +33,19 @@
 | |
|  
 | |
|  // While UListFormatter APIs are draft in ICU 67, they are stable in ICU 68 with the same function signatures.
 | |
|  // So we can assume that these signatures of draft APIs are stable.
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|  #ifdef U_HIDE_DRAFT_API
 | |
|  #undef U_HIDE_DRAFT_API
 | |
|  #endif
 | |
| +#endif
 | |
|  #include <unicode/ulistformatter.h>
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|  #define U_HIDE_DRAFT_API 1
 | |
| +#endif
 | |
| +
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|  #include <unicode/uformattedvalue.h>
 | |
| +#endif
 | |
|  
 | |
|  WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN
 | |
|  
 | |
| @@ -107,6 +114,7 @@ void IntlListFormat::initializeListFormat(JSGlobalObject* globalObject, JSValue
 | |
|      m_style = intlOption<Style>(globalObject, options, vm.propertyNames->style, { { "long"_s, Style::Long }, { "short"_s, Style::Short }, { "narrow"_s, Style::Narrow } }, "style must be either \"long\", \"short\", or \"narrow\""_s, Style::Long);
 | |
|      RETURN_IF_EXCEPTION(scope, void());
 | |
|  
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|      auto toUListFormatterType = [](Type type) {
 | |
|          switch (type) {
 | |
|          case Type::Conjunction:
 | |
| @@ -137,8 +145,13 @@ void IntlListFormat::initializeListFormat(JSGlobalObject* globalObject, JSValue
 | |
|          throwTypeError(globalObject, scope, "failed to initialize ListFormat"_s);
 | |
|          return;
 | |
|      }
 | |
| +#else
 | |
| +    throwTypeError(globalObject, scope, "Failed to initialize Intl.ListFormat since this feature is not supported in the linked ICU version"_s);
 | |
| +    return;
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|  static Vector<String, 4> stringListFromIterable(JSGlobalObject* globalObject, JSValue iterable)
 | |
|  {
 | |
|      Vector<String, 4> result;
 | |
| @@ -158,6 +171,7 @@ static Vector<String, 4> stringListFromIterable(JSGlobalObject* globalObject, JS
 | |
|      });
 | |
|      return result;
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
|  // https://tc39.es/proposal-intl-list-format/#sec-Intl.ListFormat.prototype.format
 | |
|  JSValue IntlListFormat::format(JSGlobalObject* globalObject, JSValue list) const
 | |
| @@ -165,6 +179,7 @@ JSValue IntlListFormat::format(JSGlobalObject* globalObject, JSValue list) const
 | |
|      VM& vm = globalObject->vm();
 | |
|      auto scope = DECLARE_THROW_SCOPE(vm);
 | |
|  
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|      auto stringList = stringListFromIterable(globalObject, list);
 | |
|      RETURN_IF_EXCEPTION(scope, { });
 | |
|  
 | |
| @@ -176,6 +191,10 @@ JSValue IntlListFormat::format(JSGlobalObject* globalObject, JSValue list) const
 | |
|          return throwTypeError(globalObject, scope, "failed to format list of strings"_s);
 | |
|  
 | |
|      return jsString(vm, String(WTFMove(result)));
 | |
| +#else
 | |
| +    UNUSED_PARAM(list);
 | |
| +    return throwTypeError(globalObject, scope, "failed to format list of strings"_s);
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  // https://tc39.es/proposal-intl-list-format/#sec-Intl.ListFormat.prototype.formatToParts
 | |
| @@ -184,6 +203,7 @@ JSValue IntlListFormat::formatToParts(JSGlobalObject* globalObject, JSValue list
 | |
|      VM& vm = globalObject->vm();
 | |
|      auto scope = DECLARE_THROW_SCOPE(vm);
 | |
|  
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
|      auto stringList = stringListFromIterable(globalObject, list);
 | |
|      RETURN_IF_EXCEPTION(scope, { });
 | |
|  
 | |
| @@ -269,6 +289,10 @@ JSValue IntlListFormat::formatToParts(JSGlobalObject* globalObject, JSValue list
 | |
|      }
 | |
|  
 | |
|      return parts;
 | |
| +#else
 | |
| +    UNUSED_PARAM(list);
 | |
| +    return throwTypeError(globalObject, scope, "failed to format list of strings"_s);
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  // https://tc39.es/proposal-intl-list-format/#sec-Intl.ListFormat.prototype.resolvedOptions
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlListFormat.h b/Source/JavaScriptCore/runtime/IntlListFormat.h
 | |
| index 2d071949e06f..af6f2c2e7b0a 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlListFormat.h
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlListFormat.h
 | |
| @@ -28,6 +28,12 @@
 | |
|  #include "JSObject.h"
 | |
|  #include <wtf/unicode/icu/ICUHelpers.h>
 | |
|  
 | |
| +#if !defined(HAVE_ICU_U_LIST_FORMATTER)
 | |
| +#if U_ICU_VERSION_MAJOR_NUM >= 67 || (U_ICU_VERSION_MAJOR_NUM >= 66 && USE(APPLE_INTERNAL_SDK))
 | |
| +#define HAVE_ICU_U_LIST_FORMATTER 1
 | |
| +#endif
 | |
| +#endif
 | |
| +
 | |
|  struct UListFormatter;
 | |
|  
 | |
|  namespace JSC {
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp b/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp
 | |
| index c16e6bcee859..1fc664fab1e5 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlNumberFormat.cpp
 | |
| @@ -43,8 +43,12 @@
 | |
|  #ifdef U_HIDE_DRAFT_API
 | |
|  #undef U_HIDE_DRAFT_API
 | |
|  #endif
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|  #include <unicode/unumberformatter.h>
 | |
| +#endif
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|  #include <unicode/unumberrangeformatter.h>
 | |
| +#endif
 | |
|  #define U_HIDE_DRAFT_API 1
 | |
|  
 | |
|  WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN
 | |
| @@ -57,17 +61,21 @@ namespace IntlNumberFormatInternal {
 | |
|  static constexpr bool verbose = false;
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|  void UNumberFormatterDeleter::operator()(UNumberFormatter* formatter)
 | |
|  {
 | |
|      if (formatter)
 | |
|          unumf_close(formatter);
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|  void UNumberRangeFormatterDeleter::operator()(UNumberRangeFormatter* formatter)
 | |
|  {
 | |
|      if (formatter)
 | |
|          unumrf_close(formatter);
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
|  IntlNumberFormat* IntlNumberFormat::create(VM& vm, Structure* structure)
 | |
|  {
 | |
| @@ -226,10 +234,12 @@ static std::optional<WellFormedUnit> wellFormedUnitIdentifier(StringView unitIde
 | |
|      return WellFormedUnit(numeratorUnit.value(), denominatorUnit.value());
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|  // We intentionally avoid using ICU's UNUM_APPROXIMATELY_SIGN_FIELD and define the same value here.
 | |
|  // UNUM_APPROXIMATELY_SIGN_FIELD can be defined in the header after ICU 71. But dylib ICU can be newer while ICU header version is old.
 | |
|  // We can define UNUM_APPROXIMATELY_SIGN_FIELD here so that we can support old ICU header + newer ICU library combination.
 | |
|  static constexpr UNumberFormatFields UNUM_APPROXIMATELY_SIGN_FIELD = static_cast<UNumberFormatFields>(UNUM_COMPACT_FIELD + 1);
 | |
| +#endif
 | |
|  
 | |
|  static ASCIILiteral partTypeString(UNumberFormatFields field, IntlNumberFormat::Style style, bool sign, IntlMathematicalValue::NumberType type)
 | |
|  {
 | |
| @@ -265,6 +275,7 @@ static ASCIILiteral partTypeString(UNumberFormatFields field, IntlNumberFormat::
 | |
|          return (style == IntlNumberFormat::Style::Unit) ? "unit"_s : "percentSign"_s;
 | |
|      case UNUM_SIGN_FIELD:
 | |
|          return sign ? "minusSign"_s : "plusSign"_s;
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      case UNUM_MEASURE_UNIT_FIELD:
 | |
|          return "unit"_s;
 | |
|      case UNUM_COMPACT_FIELD:
 | |
| @@ -273,6 +284,7 @@ IGNORE_GCC_WARNINGS_BEGIN("switch")
 | |
|      case UNUM_APPROXIMATELY_SIGN_FIELD:
 | |
|          return "approximatelySign"_s;
 | |
|  IGNORE_GCC_WARNINGS_END
 | |
| +#endif
 | |
|      // These should not show up because there is no way to specify them in NumberFormat options.
 | |
|      // If they do, they don't fit well into any of known part types, so consider it an "unknown".
 | |
|      case UNUM_PERMILL_FIELD:
 | |
| @@ -399,6 +411,7 @@ void IntlNumberFormat::initializeNumberFormat(JSGlobalObject* globalObject, JSVa
 | |
|  
 | |
|      // Options are obtained. Configure formatter here.
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      // Constructing ICU Number Skeletons to configure UNumberFormatter.
 | |
|      // https://github.com/unicode-org/icu/blob/master/docs/userguide/format_parse/numbers/skeletons.md
 | |
|  
 | |
| @@ -536,11 +549,120 @@ void IntlNumberFormat::initializeNumberFormat(JSGlobalObject* globalObject, JSVa
 | |
|          return;
 | |
|      }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|      m_numberRangeFormatter = std::unique_ptr<UNumberRangeFormatter, UNumberRangeFormatterDeleter>(unumrf_openForSkeletonWithCollapseAndIdentityFallback(upconverted.get(), skeletonView.length(), UNUM_RANGE_COLLAPSE_AUTO, UNUM_IDENTITY_FALLBACK_APPROXIMATELY, dataLocaleWithExtensions.data(), nullptr, &status));
 | |
|      if (U_FAILURE(status)) {
 | |
|          throwTypeError(globalObject, scope, "failed to initialize NumberFormat"_s);
 | |
|          return;
 | |
|      }
 | |
| +#endif
 | |
| +#else
 | |
| +    UNumberFormatStyle style = UNUM_DEFAULT;
 | |
| +    switch (m_style) {
 | |
| +    case Style::Decimal:
 | |
| +        style = UNUM_DECIMAL;
 | |
| +        break;
 | |
| +    case Style::Percent:
 | |
| +        style = UNUM_PERCENT;
 | |
| +        break;
 | |
| +    case Style::Currency:
 | |
| +        switch (m_currencyDisplay) {
 | |
| +        case CurrencyDisplay::Code:
 | |
| +            style = UNUM_CURRENCY_ISO;
 | |
| +            break;
 | |
| +        case CurrencyDisplay::Symbol:
 | |
| +            style = UNUM_CURRENCY;
 | |
| +            break;
 | |
| +        case CurrencyDisplay::NarrowSymbol:
 | |
| +            style = UNUM_CURRENCY; // Use the same option to "symbol" since linked-ICU does not support it.
 | |
| +            break;
 | |
| +        case CurrencyDisplay::Name:
 | |
| +            style = UNUM_CURRENCY_PLURAL;
 | |
| +            break;
 | |
| +        }
 | |
| +        switch (m_currencySign) {
 | |
| +        case CurrencySign::Standard:
 | |
| +            break;
 | |
| +        case CurrencySign::Accounting:
 | |
| +            // Ignore this case since linked ICU does not support it.
 | |
| +            break;
 | |
| +        }
 | |
| +        break;
 | |
| +    case Style::Unit:
 | |
| +        // Ignore this case since linked ICU does not support it.
 | |
| +        break;
 | |
| +    }
 | |
| +
 | |
| +    switch (m_notation) {
 | |
| +    case IntlNotation::Standard:
 | |
| +        break;
 | |
| +    case IntlNotation::Scientific:
 | |
| +    case IntlNotation::Engineering:
 | |
| +    case IntlNotation::Compact:
 | |
| +        // Ignore this case since linked ICU does not support it.
 | |
| +        break;
 | |
| +    }
 | |
| +
 | |
| +    switch (m_signDisplay) {
 | |
| +    case SignDisplay::Auto:
 | |
| +        break;
 | |
| +    case SignDisplay::Never:
 | |
| +    case SignDisplay::Always:
 | |
| +    case SignDisplay::ExceptZero:
 | |
| +    case SignDisplay::Negative:
 | |
| +        // Ignore this case since linked ICU does not support it.
 | |
| +        break;
 | |
| +    }
 | |
| +
 | |
| +    UErrorCode status = U_ZERO_ERROR;
 | |
| +    m_numberFormat = std::unique_ptr<UNumberFormat, ICUDeleter<unum_close>>(unum_open(style, nullptr, 0, dataLocaleWithExtensions.data(), nullptr, &status));
 | |
| +    if (U_FAILURE(status)) {
 | |
| +        throwTypeError(globalObject, scope, "failed to initialize NumberFormat"_s);
 | |
| +        return;
 | |
| +    }
 | |
| +
 | |
| +    if (m_style == Style::Currency) {
 | |
| +        unum_setTextAttribute(m_numberFormat.get(), UNUM_CURRENCY_CODE, StringView(m_currency).upconvertedCharacters(), m_currency.length(), &status);
 | |
| +        if (U_FAILURE(status)) {
 | |
| +            throwTypeError(globalObject, scope, "failed to initialize NumberFormat"_s);
 | |
| +            return;
 | |
| +        }
 | |
| +    }
 | |
| +
 | |
| +    switch (m_roundingType) {
 | |
| +    case IntlRoundingType::FractionDigits:
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_MIN_INTEGER_DIGITS, m_minimumIntegerDigits);
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_MIN_FRACTION_DIGITS, m_minimumFractionDigits);
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_MAX_FRACTION_DIGITS, m_maximumFractionDigits);
 | |
| +        break;
 | |
| +    case IntlRoundingType::SignificantDigits:
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_SIGNIFICANT_DIGITS_USED, true);
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_MIN_SIGNIFICANT_DIGITS, m_minimumSignificantDigits);
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_MAX_SIGNIFICANT_DIGITS, m_maximumSignificantDigits);
 | |
| +        break;
 | |
| +    case IntlRoundingType::MorePrecision:
 | |
| +        // Ignore this case since linked ICU does not support it.
 | |
| +        break;
 | |
| +    case IntlRoundingType::LessPrecision:
 | |
| +        // Ignore this case since linked ICU does not support it.
 | |
| +        break;
 | |
| +    }
 | |
| +
 | |
| +    switch (m_useGrouping) {
 | |
| +    case UseGrouping::False:
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_GROUPING_USED, false);
 | |
| +        break;
 | |
| +    case UseGrouping::Min2:
 | |
| +        // Ignore this case since linked ICU does not support it.
 | |
| +        break;
 | |
| +    case UseGrouping::Auto:
 | |
| +        break;
 | |
| +    case UseGrouping::Always:
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_GROUPING_USED, true);
 | |
| +        break;
 | |
| +    }
 | |
| +    unum_setAttribute(m_numberFormat.get(), UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP);
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  // https://tc39.es/ecma402/#sec-formatnumber
 | |
| @@ -552,6 +674,7 @@ JSValue IntlNumberFormat::format(JSGlobalObject* globalObject, double value) con
 | |
|      value = purifyNaN(value);
 | |
|  
 | |
|      Vector<char16_t, 32> buffer;
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      ASSERT(m_numberFormatter);
 | |
|      UErrorCode status = U_ZERO_ERROR;
 | |
|      auto formattedNumber = std::unique_ptr<UFormattedNumber, ICUDeleter<unumf_closeResult>>(unumf_openResult(&status));
 | |
| @@ -563,6 +686,12 @@ JSValue IntlNumberFormat::format(JSGlobalObject* globalObject, double value) con
 | |
|      status = callBufferProducingFunction(unumf_resultToString, formattedNumber.get(), buffer);
 | |
|      if (U_FAILURE(status))
 | |
|          return throwTypeError(globalObject, scope, "Failed to format a number."_s);
 | |
| +#else
 | |
| +    ASSERT(m_numberFormat);
 | |
| +    auto status = callBufferProducingFunction(unum_formatDouble, m_numberFormat.get(), value, buffer, nullptr);
 | |
| +    if (U_FAILURE(status))
 | |
| +        return throwTypeError(globalObject, scope, "Failed to format a number."_s);
 | |
| +#endif
 | |
|      return jsString(vm, String(WTFMove(buffer)));
 | |
|  }
 | |
|  
 | |
| @@ -576,6 +705,7 @@ JSValue IntlNumberFormat::format(JSGlobalObject* globalObject, IntlMathematicalV
 | |
|      const auto& string = value.getString();
 | |
|  
 | |
|      Vector<char16_t, 32> buffer;
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      ASSERT(m_numberFormatter);
 | |
|      UErrorCode status = U_ZERO_ERROR;
 | |
|      auto formattedNumber = std::unique_ptr<UFormattedNumber, ICUDeleter<unumf_closeResult>>(unumf_openResult(&status));
 | |
| @@ -587,9 +717,16 @@ JSValue IntlNumberFormat::format(JSGlobalObject* globalObject, IntlMathematicalV
 | |
|      status = callBufferProducingFunction(unumf_resultToString, formattedNumber.get(), buffer);
 | |
|      if (U_FAILURE(status))
 | |
|          return throwTypeError(globalObject, scope, "Failed to format a BigInt."_s);
 | |
| +#else
 | |
| +    ASSERT(m_numberFormat);
 | |
| +    auto status = callBufferProducingFunction(unum_formatDecimal, m_numberFormat.get(), string.data(), string.length(), buffer, nullptr);
 | |
| +    if (U_FAILURE(status))
 | |
| +        return throwTypeError(globalObject, scope, "Failed to format a BigInt."_s);
 | |
| +#endif
 | |
|      return jsString(vm, String(WTFMove(buffer)));
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|  JSValue IntlNumberFormat::formatRange(JSGlobalObject* globalObject, double start, double end) const
 | |
|  {
 | |
|      VM& vm = globalObject->vm();
 | |
| @@ -657,6 +794,7 @@ JSValue IntlNumberFormat::formatRange(JSGlobalObject* globalObject, IntlMathemat
 | |
|  
 | |
|      return jsString(vm, String({ string, static_cast<size_t>(length) }));
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
|  static constexpr int32_t literalField = -1;
 | |
|  struct IntlNumberFormatField {
 | |
| @@ -769,6 +907,7 @@ static Vector<IntlNumberFormatField> flattenFields(Vector<IntlNumberFormatField>
 | |
|      return flatten;
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER_FORMAT_RANGE_TO_PARTS)
 | |
|  static bool numberFieldsPracticallyEqual(const UFormattedValue* formattedValue, UErrorCode& status)
 | |
|  {
 | |
|      auto iterator = std::unique_ptr<UConstrainedFieldPosition, ICUDeleter<ucfpos_close>>(ucfpos_open(&status));
 | |
| @@ -1010,6 +1149,7 @@ JSValue IntlNumberFormat::formatRangeToParts(JSGlobalObject* globalObject, IntlM
 | |
|  
 | |
|      return parts;
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
|  ASCIILiteral IntlNumberFormat::styleString(Style style)
 | |
|  {
 | |
| @@ -1307,6 +1447,7 @@ JSValue IntlNumberFormat::formatToParts(JSGlobalObject* globalObject, double val
 | |
|          return throwTypeError(globalObject, scope, "failed to open field position iterator"_s);
 | |
|  
 | |
|      Vector<char16_t, 32> result;
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      ASSERT(m_numberFormatter);
 | |
|      auto formattedNumber = std::unique_ptr<UFormattedNumber, ICUDeleter<unumf_closeResult>>(unumf_openResult(&status));
 | |
|      if (U_FAILURE(status))
 | |
| @@ -1321,6 +1462,13 @@ JSValue IntlNumberFormat::formatToParts(JSGlobalObject* globalObject, double val
 | |
|      if (U_FAILURE(status))
 | |
|          return throwTypeError(globalObject, scope, "Failed to format a number."_s);
 | |
|      IntlFieldIterator iterator(*fieldItr.get());
 | |
| +#else
 | |
| +    ASSERT(m_numberFormat);
 | |
| +    status = callBufferProducingFunction(unum_formatDoubleForFields, m_numberFormat.get(), value, result, fieldItr.get());
 | |
| +    if (U_FAILURE(status))
 | |
| +        return throwTypeError(globalObject, scope, "failed to format a number."_s);
 | |
| +    IntlFieldIterator iterator(*fieldItr.get());
 | |
| +#endif
 | |
|  
 | |
|      auto resultString = String(WTFMove(result));
 | |
|  
 | |
| @@ -1334,6 +1482,7 @@ JSValue IntlNumberFormat::formatToParts(JSGlobalObject* globalObject, double val
 | |
|      return parts;
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|  JSValue IntlNumberFormat::formatToParts(JSGlobalObject* globalObject, IntlMathematicalValue&& value, JSString* sourceType) const
 | |
|  {
 | |
|      VM& vm = globalObject->vm();
 | |
| @@ -1378,6 +1527,7 @@ JSValue IntlNumberFormat::formatToParts(JSGlobalObject* globalObject, IntlMathem
 | |
|  
 | |
|      return parts;
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
|  IntlMathematicalValue IntlMathematicalValue::parseString(JSGlobalObject* globalObject, StringView view)
 | |
|  {
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormat.h b/Source/JavaScriptCore/runtime/IntlNumberFormat.h
 | |
| index 65a6795b013f..29d197e462fb 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlNumberFormat.h
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlNumberFormat.h
 | |
| @@ -34,6 +34,25 @@
 | |
|  #include <wtf/TZoneMalloc.h>
 | |
|  #include <wtf/unicode/icu/ICUHelpers.h>
 | |
|  
 | |
| +#if !defined(HAVE_ICU_U_NUMBER_FORMATTER)
 | |
| +// UNUM_COMPACT_FIELD and UNUM_MEASURE_UNIT_FIELD are available after ICU 64.
 | |
| +#if U_ICU_VERSION_MAJOR_NUM >= 64
 | |
| +#define HAVE_ICU_U_NUMBER_FORMATTER 1
 | |
| +#endif
 | |
| +#endif
 | |
| +
 | |
| +#if !defined(HAVE_ICU_U_NUMBER_RANGE_FORMATTER)
 | |
| +#if U_ICU_VERSION_MAJOR_NUM >= 68
 | |
| +#define HAVE_ICU_U_NUMBER_RANGE_FORMATTER 1
 | |
| +#endif
 | |
| +#endif
 | |
| +
 | |
| +#if !defined(HAVE_ICU_U_NUMBER_RANGE_FORMATTER_FORMAT_RANGE_TO_PARTS)
 | |
| +#if U_ICU_VERSION_MAJOR_NUM >= 69
 | |
| +#define HAVE_ICU_U_NUMBER_RANGE_FORMATTER_FORMAT_RANGE_TO_PARTS 1
 | |
| +#endif
 | |
| +#endif
 | |
| +
 | |
|  struct UFormattedValue;
 | |
|  struct UNumberFormatter;
 | |
|  struct UNumberRangeFormatter;
 | |
| @@ -52,13 +71,17 @@ template<typename IntlType> void setNumberFormatDigitOptions(JSGlobalObject*, In
 | |
|  template<typename IntlType> void appendNumberFormatDigitOptionsToSkeleton(IntlType*, StringBuilder&);
 | |
|  template<typename IntlType> void appendNumberFormatNotationOptionsToSkeleton(IntlType*, StringBuilder&);
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|  struct UNumberFormatterDeleter {
 | |
|      JS_EXPORT_PRIVATE void operator()(UNumberFormatter*);
 | |
|  };
 | |
| +#endif
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|  struct UNumberRangeFormatterDeleter {
 | |
|      JS_EXPORT_PRIVATE void operator()(UNumberRangeFormatter*);
 | |
|  };
 | |
| +#endif
 | |
|  
 | |
|  class IntlMathematicalValue {
 | |
|      WTF_MAKE_TZONE_ALLOCATED(IntlMathematicalValue);
 | |
| @@ -162,14 +185,20 @@ public:
 | |
|      JSValue format(JSGlobalObject*, double) const;
 | |
|      JSValue format(JSGlobalObject*, IntlMathematicalValue&&) const;
 | |
|      JSValue formatToParts(JSGlobalObject*, double, JSString* sourceType = nullptr) const;
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      JSValue formatToParts(JSGlobalObject*, IntlMathematicalValue&&, JSString* sourceType = nullptr) const;
 | |
| +#endif
 | |
|      JSObject* resolvedOptions(JSGlobalObject*) const;
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|      JSValue formatRange(JSGlobalObject*, double, double) const;
 | |
|      JSValue formatRange(JSGlobalObject*, IntlMathematicalValue&&, IntlMathematicalValue&&) const;
 | |
| +#endif
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER_FORMAT_RANGE_TO_PARTS)
 | |
|      JSValue formatRangeToParts(JSGlobalObject*, double, double) const;
 | |
|      JSValue formatRangeToParts(JSGlobalObject*, IntlMathematicalValue&&, IntlMathematicalValue&&) const;
 | |
| +#endif
 | |
|  
 | |
|      JSBoundFunction* boundFormat() const { return m_boundFormat.get(); }
 | |
|      void setBoundFormat(VM&, JSBoundFunction*);
 | |
| @@ -216,8 +245,14 @@ private:
 | |
|      static JSValue useGroupingValue(VM&, UseGrouping);
 | |
|  
 | |
|      WriteBarrier<JSBoundFunction> m_boundFormat;
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      std::unique_ptr<UNumberFormatter, UNumberFormatterDeleter> m_numberFormatter;
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|      std::unique_ptr<UNumberRangeFormatter, UNumberRangeFormatterDeleter> m_numberRangeFormatter;
 | |
| +#endif
 | |
| +#else
 | |
| +    std::unique_ptr<UNumberFormat, ICUDeleter<unum_close>> m_numberFormat;
 | |
| +#endif
 | |
|  
 | |
|      String m_locale;
 | |
|      String m_numberingSystem;
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp b/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp
 | |
| index d951aa27c715..4e3615ba7242 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlNumberFormatPrototype.cpp
 | |
| @@ -35,12 +35,18 @@
 | |
|  namespace JSC {
 | |
|  
 | |
|  static JSC_DECLARE_CUSTOM_GETTER(intlNumberFormatPrototypeGetterFormat);
 | |
| -static JSC_DECLARE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatRange);
 | |
| -static JSC_DECLARE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatRangeToParts);
 | |
|  static JSC_DECLARE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatToParts);
 | |
|  static JSC_DECLARE_HOST_FUNCTION(intlNumberFormatPrototypeFuncResolvedOptions);
 | |
|  static JSC_DECLARE_HOST_FUNCTION(intlNumberFormatFuncFormat);
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
| +static JSC_DECLARE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatRange);
 | |
| +#endif
 | |
| +
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER_FORMAT_RANGE_TO_PARTS)
 | |
| +static JSC_DECLARE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatRangeToParts);
 | |
| +#endif
 | |
| +
 | |
|  }
 | |
|  
 | |
|  #include "IntlNumberFormatPrototype.lut.h"
 | |
| @@ -51,11 +57,9 @@ const ClassInfo IntlNumberFormatPrototype::s_info = { "Intl.NumberFormat"_s, &Ba
 | |
|  
 | |
|  /* Source for IntlNumberFormatPrototype.lut.h
 | |
|  @begin numberFormatPrototypeTable
 | |
| -  format              intlNumberFormatPrototypeGetterFormat            DontEnum|ReadOnly|CustomAccessor
 | |
| -  formatRange         intlNumberFormatPrototypeFuncFormatRange         DontEnum|Function 2
 | |
| -  formatRangeToParts  intlNumberFormatPrototypeFuncFormatRangeToParts  DontEnum|Function 2
 | |
| -  formatToParts       intlNumberFormatPrototypeFuncFormatToParts       DontEnum|Function 1
 | |
| -  resolvedOptions     intlNumberFormatPrototypeFuncResolvedOptions     DontEnum|Function 0
 | |
| +  format           intlNumberFormatPrototypeGetterFormat         DontEnum|ReadOnly|CustomAccessor
 | |
| +  formatToParts    intlNumberFormatPrototypeFuncFormatToParts    DontEnum|Function 1
 | |
| +  resolvedOptions  intlNumberFormatPrototypeFuncResolvedOptions  DontEnum|Function 0
 | |
|  @end
 | |
|  */
 | |
|  
 | |
| @@ -82,6 +86,12 @@ void IntlNumberFormatPrototype::finishCreation(VM& vm, JSGlobalObject* globalObj
 | |
|      ASSERT(inherits(info()));
 | |
|      JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
 | |
|      UNUSED_PARAM(globalObject);
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
| +    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("formatRange"_s, intlNumberFormatPrototypeFuncFormatRange, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, ImplementationVisibility::Public);
 | |
| +#endif
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER_FORMAT_RANGE_TO_PARTS)
 | |
| +    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("formatRangeToParts"_s, intlNumberFormatPrototypeFuncFormatRangeToParts, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, ImplementationVisibility::Public);
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  // https://tc39.es/ecma402/#sec-number-format-functions
 | |
| @@ -134,6 +144,7 @@ JSC_DEFINE_CUSTOM_GETTER(intlNumberFormatPrototypeGetterFormat, (JSGlobalObject*
 | |
|      return JSValue::encode(boundFormat);
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|  JSC_DEFINE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatRange, (JSGlobalObject* globalObject, CallFrame* callFrame))
 | |
|  {
 | |
|      VM& vm = globalObject->vm();
 | |
| @@ -163,6 +174,7 @@ JSC_DEFINE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatRange, (JSGlobalObje
 | |
|  
 | |
|      RELEASE_AND_RETURN(scope, JSValue::encode(numberFormat->formatRange(globalObject, WTFMove(start), WTFMove(end))));
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
|  JSC_DEFINE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatToParts, (JSGlobalObject* globalObject, CallFrame* callFrame))
 | |
|  {
 | |
| @@ -177,6 +189,7 @@ JSC_DEFINE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatToParts, (JSGlobalOb
 | |
|      if (!numberFormat) [[unlikely]]
 | |
|          return JSValue::encode(throwTypeError(globalObject, scope, "Intl.NumberFormat.prototype.formatToParts called on value that's not a NumberFormat"_s));
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      auto value = toIntlMathematicalValue(globalObject, callFrame->argument(0));
 | |
|      RETURN_IF_EXCEPTION(scope, { });
 | |
|  
 | |
| @@ -184,8 +197,15 @@ JSC_DEFINE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatToParts, (JSGlobalOb
 | |
|          RELEASE_AND_RETURN(scope, JSValue::encode(numberFormat->formatToParts(globalObject, number.value())));
 | |
|  
 | |
|      RELEASE_AND_RETURN(scope, JSValue::encode(numberFormat->formatToParts(globalObject, WTFMove(value))));
 | |
| +#else
 | |
| +    double value = callFrame->argument(0).toNumber(globalObject);
 | |
| +    RETURN_IF_EXCEPTION(scope, { });
 | |
| +
 | |
| +    RELEASE_AND_RETURN(scope, JSValue::encode(numberFormat->formatToParts(globalObject, value)));
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER_FORMAT_RANGE_TO_PARTS)
 | |
|  JSC_DEFINE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatRangeToParts, (JSGlobalObject* globalObject, CallFrame* callFrame))
 | |
|  {
 | |
|      VM& vm = globalObject->vm();
 | |
| @@ -215,6 +235,7 @@ JSC_DEFINE_HOST_FUNCTION(intlNumberFormatPrototypeFuncFormatRangeToParts, (JSGlo
 | |
|  
 | |
|      RELEASE_AND_RETURN(scope, JSValue::encode(numberFormat->formatRangeToParts(globalObject, WTFMove(start), WTFMove(end))));
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
|  JSC_DEFINE_HOST_FUNCTION(intlNumberFormatPrototypeFuncResolvedOptions, (JSGlobalObject* globalObject, CallFrame* callFrame))
 | |
|  {
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlObject.cpp b/Source/JavaScriptCore/runtime/IntlObject.cpp
 | |
| index 8ef1ee678063..f8413f12dec7 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlObject.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlObject.cpp
 | |
| @@ -169,8 +169,6 @@ namespace JSC {
 | |
|    Collator              createCollatorConstructor                    DontEnum|PropertyCallback
 | |
|    DateTimeFormat        createDateTimeFormatConstructor              DontEnum|PropertyCallback
 | |
|    DisplayNames          createDisplayNamesConstructor                DontEnum|PropertyCallback
 | |
| -  DurationFormat        createDurationFormatConstructor              DontEnum|PropertyCallback
 | |
| -  ListFormat            createListFormatConstructor                  DontEnum|PropertyCallback
 | |
|    Locale                createLocaleConstructor                      DontEnum|PropertyCallback
 | |
|    NumberFormat          createNumberFormatConstructor                DontEnum|PropertyCallback
 | |
|    PluralRules           createPluralRulesConstructor                 DontEnum|PropertyCallback
 | |
| @@ -258,6 +256,13 @@ void IntlObject::finishCreation(VM& vm, JSGlobalObject*)
 | |
|      Base::finishCreation(vm);
 | |
|      ASSERT(inherits(info()));
 | |
|      JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
 | |
| +#if HAVE(ICU_U_LIST_FORMATTER)
 | |
| +    putDirectWithoutTransition(vm, vm.propertyNames->DurationFormat, createDurationFormatConstructor(vm, this), static_cast<unsigned>(PropertyAttribute::DontEnum));
 | |
| +    putDirectWithoutTransition(vm, vm.propertyNames->ListFormat, createListFormatConstructor(vm, this), static_cast<unsigned>(PropertyAttribute::DontEnum));
 | |
| +#else
 | |
| +    UNUSED_PARAM(&createDurationFormatConstructor);
 | |
| +    UNUSED_PARAM(&createListFormatConstructor);
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  Structure* IntlObject::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlPluralRules.cpp b/Source/JavaScriptCore/runtime/IntlPluralRules.cpp
 | |
| index b3e58fc6453f..c451478086f7 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlPluralRules.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlPluralRules.cpp
 | |
| @@ -36,8 +36,12 @@
 | |
|  #undef U_HIDE_DRAFT_API
 | |
|  #endif
 | |
|  #include <unicode/upluralrules.h>
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|  #include <unicode/unumberformatter.h>
 | |
| +#endif
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|  #include <unicode/unumberrangeformatter.h>
 | |
| +#endif
 | |
|  #define U_HIDE_DRAFT_API 1
 | |
|  
 | |
|  namespace JSC {
 | |
| @@ -121,6 +125,7 @@ void IntlPluralRules::initializePluralRules(JSGlobalObject* globalObject, JSValu
 | |
|      auto locale = m_locale.utf8();
 | |
|      UErrorCode status = U_ZERO_ERROR;
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      StringBuilder skeletonBuilder;
 | |
|  
 | |
|      appendNumberFormatDigitOptionsToSkeleton(this, skeletonBuilder);
 | |
| @@ -135,11 +140,36 @@ void IntlPluralRules::initializePluralRules(JSGlobalObject* globalObject, JSValu
 | |
|          return;
 | |
|      }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|      m_numberRangeFormatter = std::unique_ptr<UNumberRangeFormatter, UNumberRangeFormatterDeleter>(unumrf_openForSkeletonWithCollapseAndIdentityFallback(upconverted.get(), skeletonView.length(), UNUM_RANGE_COLLAPSE_NONE, UNUM_IDENTITY_FALLBACK_RANGE, locale.data(), nullptr, &status));
 | |
|      if (U_FAILURE(status)) {
 | |
|          throwTypeError(globalObject, scope, "failed to initialize PluralRules"_s);
 | |
|          return;
 | |
|      }
 | |
| +#endif
 | |
| +#else
 | |
| +    m_numberFormat = std::unique_ptr<UNumberFormat, UNumberFormatDeleter>(unum_open(UNUM_DECIMAL, nullptr, 0, locale.data(), nullptr, &status));
 | |
| +    if (U_FAILURE(status)) {
 | |
| +        throwTypeError(globalObject, scope, "failed to initialize PluralRules"_s);
 | |
| +        return;
 | |
| +    }
 | |
| +
 | |
| +    switch (m_roundingType) {
 | |
| +    case IntlRoundingType::FractionDigits:
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_MIN_INTEGER_DIGITS, m_minimumIntegerDigits);
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_MIN_FRACTION_DIGITS, m_minimumFractionDigits);
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_MAX_FRACTION_DIGITS, m_maximumFractionDigits);
 | |
| +        break;
 | |
| +    case IntlRoundingType::SignificantDigits:
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_SIGNIFICANT_DIGITS_USED, true);
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_MIN_SIGNIFICANT_DIGITS, m_minimumSignificantDigits);
 | |
| +        unum_setAttribute(m_numberFormat.get(), UNUM_MAX_SIGNIFICANT_DIGITS, m_maximumSignificantDigits);
 | |
| +        break;
 | |
| +    case IntlRoundingType::MorePrecision:
 | |
| +    case IntlRoundingType::LessPrecision:
 | |
| +        break;
 | |
| +    }
 | |
| +#endif
 | |
|  
 | |
|      m_pluralRules = std::unique_ptr<UPluralRules, UPluralRulesDeleter>(uplrules_openForType(locale.data(), m_type == Type::Ordinal ? UPLURAL_TYPE_ORDINAL : UPLURAL_TYPE_CARDINAL, &status));
 | |
|      if (U_FAILURE(status)) {
 | |
| @@ -234,6 +264,7 @@ JSValue IntlPluralRules::select(JSGlobalObject* globalObject, double value) cons
 | |
|  
 | |
|      UErrorCode status = U_ZERO_ERROR;
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      auto formattedNumber = std::unique_ptr<UFormattedNumber, ICUDeleter<unumf_closeResult>>(unumf_openResult(&status));
 | |
|      if (U_FAILURE(status))
 | |
|          return throwTypeError(globalObject, scope, "failed to select plural value"_s);
 | |
| @@ -245,8 +276,17 @@ JSValue IntlPluralRules::select(JSGlobalObject* globalObject, double value) cons
 | |
|      if (U_FAILURE(status))
 | |
|          return throwTypeError(globalObject, scope, "failed to select plural value"_s);
 | |
|      return jsString(vm, String(WTFMove(buffer)));
 | |
| +#else
 | |
| +    Vector<UChar, 8> result(8);
 | |
| +    auto length = uplrules_selectWithFormat(m_pluralRules.get(), value, m_numberFormat.get(), result.data(), result.size(), &status);
 | |
| +    if (U_FAILURE(status))
 | |
| +        return throwTypeError(globalObject, scope, "failed to select plural value"_s);
 | |
| +
 | |
| +    return jsString(vm, String({ result.data(), static_cast<size_t>(length) }));
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|  JSValue IntlPluralRules::selectRange(JSGlobalObject* globalObject, double start, double end) const
 | |
|  {
 | |
|      ASSERT(m_numberRangeFormatter);
 | |
| @@ -272,5 +312,6 @@ JSValue IntlPluralRules::selectRange(JSGlobalObject* globalObject, double start,
 | |
|          return throwTypeError(globalObject, scope, "failed to select plural value"_s);
 | |
|      return jsString(vm, String(WTFMove(buffer)));
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
|  } // namespace JSC
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlPluralRules.h b/Source/JavaScriptCore/runtime/IntlPluralRules.h
 | |
| index 0f95f5e5eab5..848def6a50d4 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlPluralRules.h
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlPluralRules.h
 | |
| @@ -73,9 +73,12 @@ public:
 | |
|  
 | |
|      void initializePluralRules(JSGlobalObject*, JSValue locales, JSValue options);
 | |
|      JSValue select(JSGlobalObject*, double value) const;
 | |
| -    JSValue selectRange(JSGlobalObject*, double start, double end) const;
 | |
|      JSObject* resolvedOptions(JSGlobalObject*) const;
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
| +    JSValue selectRange(JSGlobalObject*, double start, double end) const;
 | |
| +#endif
 | |
| +
 | |
|  private:
 | |
|      IntlPluralRules(VM&, Structure*);
 | |
|      DECLARE_DEFAULT_FINISH_CREATION;
 | |
| @@ -85,8 +88,15 @@ private:
 | |
|      enum class Type : bool { Cardinal, Ordinal };
 | |
|  
 | |
|      std::unique_ptr<UPluralRules, UPluralRulesDeleter> m_pluralRules;
 | |
| +#if HAVE(ICU_U_NUMBER_FORMATTER)
 | |
|      std::unique_ptr<UNumberFormatter, UNumberFormatterDeleter> m_numberFormatter;
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|      std::unique_ptr<UNumberRangeFormatter, UNumberRangeFormatterDeleter> m_numberRangeFormatter;
 | |
| +#endif
 | |
| +#else
 | |
| +    using UNumberFormatDeleter = ICUDeleter<unum_close>;
 | |
| +    std::unique_ptr<UNumberFormat, UNumberFormatDeleter> m_numberFormat;
 | |
| +#endif
 | |
|  
 | |
|      String m_locale;
 | |
|      unsigned m_minimumIntegerDigits { 1 };
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlPluralRulesPrototype.cpp b/Source/JavaScriptCore/runtime/IntlPluralRulesPrototype.cpp
 | |
| index 48688c0dedbb..9854d9a7d363 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlPluralRulesPrototype.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlPluralRulesPrototype.cpp
 | |
| @@ -33,7 +33,9 @@
 | |
|  namespace JSC {
 | |
|  
 | |
|  static JSC_DECLARE_HOST_FUNCTION(intlPluralRulesPrototypeFuncSelect);
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|  static JSC_DECLARE_HOST_FUNCTION(intlPluralRulesPrototypeFuncSelectRange);
 | |
| +#endif
 | |
|  static JSC_DECLARE_HOST_FUNCTION(intlPluralRulesPrototypeFuncResolvedOptions);
 | |
|  
 | |
|  }
 | |
| @@ -47,7 +49,6 @@ const ClassInfo IntlPluralRulesPrototype::s_info = { "Intl.PluralRules"_s, &Base
 | |
|  /* Source for IntlPluralRulesPrototype.lut.h
 | |
|  @begin pluralRulesPrototypeTable
 | |
|    select           intlPluralRulesPrototypeFuncSelect           DontEnum|Function 1
 | |
| -  selectRange      intlPluralRulesPrototypeFuncSelectRange      DontEnum|Function 2
 | |
|    resolvedOptions  intlPluralRulesPrototypeFuncResolvedOptions  DontEnum|Function 0
 | |
|  @end
 | |
|  */
 | |
| @@ -75,6 +76,9 @@ void IntlPluralRulesPrototype::finishCreation(VM& vm, JSGlobalObject* globalObje
 | |
|      ASSERT(inherits(info()));
 | |
|      JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
 | |
|      UNUSED_PARAM(globalObject);
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
| +    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->selectRange, intlPluralRulesPrototypeFuncSelectRange, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, ImplementationVisibility::Public);
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  JSC_DEFINE_HOST_FUNCTION(intlPluralRulesPrototypeFuncSelect, (JSGlobalObject* globalObject, CallFrame* callFrame))
 | |
| @@ -95,6 +99,7 @@ JSC_DEFINE_HOST_FUNCTION(intlPluralRulesPrototypeFuncSelect, (JSGlobalObject* gl
 | |
|      RELEASE_AND_RETURN(scope, JSValue::encode(pluralRules->select(globalObject, value)));
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_NUMBER_RANGE_FORMATTER)
 | |
|  JSC_DEFINE_HOST_FUNCTION(intlPluralRulesPrototypeFuncSelectRange, (JSGlobalObject* globalObject, CallFrame* callFrame))
 | |
|  {
 | |
|      VM& vm = globalObject->vm();
 | |
| @@ -119,6 +124,7 @@ JSC_DEFINE_HOST_FUNCTION(intlPluralRulesPrototypeFuncSelectRange, (JSGlobalObjec
 | |
|  
 | |
|      RELEASE_AND_RETURN(scope, JSValue::encode(pluralRules->selectRange(globalObject, start, end)));
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
|  JSC_DEFINE_HOST_FUNCTION(intlPluralRulesPrototypeFuncResolvedOptions, (JSGlobalObject* globalObject, CallFrame* callFrame))
 | |
|  {
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlWorkaround.cpp b/Source/JavaScriptCore/runtime/IntlWorkaround.cpp
 | |
| index c6c1fa0cd228..85d7e85469a7 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlWorkaround.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlWorkaround.cpp
 | |
| @@ -26,17 +26,25 @@
 | |
|  #include "config.h"
 | |
|  #include "IntlWorkaround.h"
 | |
|  
 | |
| -#ifdef U_HIDE_DRAFT_API
 | |
| +// ICU 69 introduces draft API ubrk_clone and deprecates ubrk_safeClone.
 | |
| +#if defined(U_HIDE_DRAFT_API)
 | |
|  #undef U_HIDE_DRAFT_API
 | |
|  #endif
 | |
|  #include <unicode/ubrk.h>
 | |
| -#define U_HIDE_DRAFT_API 1
 | |
| +
 | |
| +#if U_ICU_VERSION_MAJOR_NUM >= 69
 | |
| +#define HAVE_ICU_UBRK_CLONE 1
 | |
| +#endif
 | |
|  
 | |
|  namespace JSC {
 | |
|  
 | |
|  UBreakIterator* cloneUBreakIterator(const UBreakIterator* iterator, UErrorCode* status)
 | |
|  {
 | |
| +#if HAVE(ICU_UBRK_CLONE)
 | |
|      return ubrk_clone(iterator, status);
 | |
| +#else
 | |
| +    return ubrk_safeClone(iterator, nullptr, nullptr, status);
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  } // namespace JSC
 | |
| diff --git a/Source/JavaScriptCore/runtime/JSDateMath.cpp b/Source/JavaScriptCore/runtime/JSDateMath.cpp
 | |
| index c40f917fc99f..755337022004 100644
 | |
| --- a/Source/JavaScriptCore/runtime/JSDateMath.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/JSDateMath.cpp
 | |
| @@ -74,6 +74,7 @@
 | |
|  
 | |
|  #include "ExceptionHelpers.h"
 | |
|  #include "VM.h"
 | |
| +#include <bit>
 | |
|  #include <limits>
 | |
|  #include <wtf/DateMath.h>
 | |
|  #include <wtf/Language.h>
 | |
| @@ -81,11 +82,24 @@
 | |
|  #include <wtf/unicode/CharacterNames.h>
 | |
|  #include <wtf/unicode/icu/ICUHelpers.h>
 | |
|  
 | |
| +#if U_ICU_VERSION_MAJOR_NUM >= 69 || (U_ICU_VERSION_MAJOR_NUM == 68 && USE(APPLE_INTERNAL_SDK))
 | |
| +#define HAVE_ICU_C_TIMEZONE_API 1
 | |
|  #ifdef U_HIDE_DRAFT_API
 | |
|  #undef U_HIDE_DRAFT_API
 | |
|  #endif
 | |
|  #include <unicode/ucal.h>
 | |
| -#define U_HIDE_DRAFT_API 1
 | |
| +#else
 | |
| +// icu::TimeZone and icu::BasicTimeZone features are only available in ICU C++ APIs.
 | |
| +// We use these C++ APIs as an exception.
 | |
| +#undef U_SHOW_CPLUSPLUS_API
 | |
| +#define U_SHOW_CPLUSPLUS_API 1
 | |
| +#include <unicode/basictz.h>
 | |
| +#include <unicode/locid.h>
 | |
| +#include <unicode/timezone.h>
 | |
| +#include <unicode/unistr.h>
 | |
| +#undef U_SHOW_CPLUSPLUS_API
 | |
| +#define U_SHOW_CPLUSPLUS_API 0
 | |
| +#endif
 | |
|  
 | |
|  namespace JSC {
 | |
|  namespace JSDateMathInternal {
 | |
| @@ -96,6 +110,7 @@ static constexpr bool verbose = false;
 | |
|  std::atomic<uint64_t> lastTimeZoneID { 1 };
 | |
|  #endif
 | |
|  
 | |
| +#if HAVE(ICU_C_TIMEZONE_API)
 | |
|  class OpaqueICUTimeZone {
 | |
|      WTF_MAKE_TZONE_ALLOCATED(OpaqueICUTimeZone);
 | |
|  public:
 | |
| @@ -105,10 +120,26 @@ public:
 | |
|  
 | |
|  WTF_MAKE_TZONE_ALLOCATED_IMPL(OpaqueICUTimeZone);
 | |
|  
 | |
| +#else
 | |
| +static icu::TimeZone* toICUTimeZone(OpaqueICUTimeZone* timeZone)
 | |
| +{
 | |
| +    return std::bit_cast<icu::TimeZone*>(timeZone);
 | |
| +}
 | |
| +static OpaqueICUTimeZone* toOpaqueICUTimeZone(icu::TimeZone* timeZone)
 | |
| +{
 | |
| +    return std::bit_cast<OpaqueICUTimeZone*>(timeZone);
 | |
| +}
 | |
| +#endif
 | |
| +
 | |
|  void OpaqueICUTimeZoneDeleter::operator()(OpaqueICUTimeZone* timeZone)
 | |
|  {
 | |
| -    if (timeZone)
 | |
| +    if (timeZone) {
 | |
| +#if HAVE(ICU_C_TIMEZONE_API)
 | |
|          delete timeZone;
 | |
| +#else
 | |
| +        delete toICUTimeZone(timeZone);
 | |
| +#endif
 | |
| +    }
 | |
|  }
 | |
|  
 | |
|  // Get the combined UTC + DST offset for the time passed in.
 | |
| @@ -126,6 +157,7 @@ LocalTimeOffset DateCache::calculateLocalTimeOffset(double millisecondsFromEpoch
 | |
|      // We can return any values in this case since later we fail when computing non timezone offset part anyway.
 | |
|      constexpr LocalTimeOffset failed { false, 0 };
 | |
|  
 | |
| +#if HAVE(ICU_C_TIMEZONE_API)
 | |
|      auto& timeZoneCache = *this->timeZoneCache();
 | |
|      ucal_setMillis(timeZoneCache.m_calendar.get(), millisecondsFromEpoch, &status);
 | |
|      if (U_FAILURE(status))
 | |
| @@ -143,6 +175,21 @@ LocalTimeOffset DateCache::calculateLocalTimeOffset(double millisecondsFromEpoch
 | |
|          if (U_FAILURE(status))
 | |
|              return failed;
 | |
|      }
 | |
| +#else
 | |
| +    auto& timeZoneCache = *toICUTimeZone(this->timeZoneCache());
 | |
| +    if (inputTimeType != WTF::LocalTime) {
 | |
| +        constexpr bool isLocalTime = false;
 | |
| +        timeZoneCache.getOffset(millisecondsFromEpoch, isLocalTime, rawOffset, dstOffset, status);
 | |
| +        if (U_FAILURE(status))
 | |
| +            return failed;
 | |
| +    } else {
 | |
| +        // icu::TimeZone is a timezone instance which inherits icu::BasicTimeZone.
 | |
| +        // https://unicode-org.atlassian.net/browse/ICU-13705 will move getOffsetFromLocal to icu::TimeZone.
 | |
| +        static_cast<const icu::BasicTimeZone&>(timeZoneCache).getOffsetFromLocal(millisecondsFromEpoch, icu::BasicTimeZone::kFormer, icu::BasicTimeZone::kFormer, rawOffset, dstOffset, status);
 | |
| +        if (U_FAILURE(status))
 | |
| +            return failed;
 | |
| +    }
 | |
| +#endif
 | |
|  
 | |
|      return { !!dstOffset, rawOffset + dstOffset };
 | |
|  }
 | |
| @@ -427,12 +474,32 @@ double DateCache::parseDate(JSGlobalObject* globalObject, VM& vm, const String&
 | |
|  // https://tc39.es/ecma402/#sec-defaulttimezone
 | |
|  String DateCache::defaultTimeZone()
 | |
|  {
 | |
| +#if HAVE(ICU_C_TIMEZONE_API)
 | |
|      return timeZoneCache()->m_canonicalTimeZoneID;
 | |
| +#else
 | |
| +    icu::UnicodeString timeZoneID;
 | |
| +    icu::UnicodeString canonicalTimeZoneID;
 | |
| +    auto& timeZone = *toICUTimeZone(timeZoneCache());
 | |
| +    timeZone.getID(timeZoneID);
 | |
| +
 | |
| +    UErrorCode status = U_ZERO_ERROR;
 | |
| +    UBool isSystem = false;
 | |
| +    icu::TimeZone::getCanonicalID(timeZoneID, canonicalTimeZoneID, isSystem, status);
 | |
| +    if (U_FAILURE(status))
 | |
| +        return "UTC"_s;
 | |
| +
 | |
| +    String canonical = String({ canonicalTimeZoneID.getBuffer(), static_cast<size_t>(canonicalTimeZoneID.length()) });
 | |
| +    if (isUTCEquivalent(canonical))
 | |
| +        return "UTC"_s;
 | |
| +
 | |
| +    return canonical;
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  String DateCache::timeZoneDisplayName(bool isDST)
 | |
|  {
 | |
|      if (m_timeZoneStandardDisplayNameCache.isNull()) {
 | |
| +#if HAVE(ICU_C_TIMEZONE_API)
 | |
|          auto& timeZoneCache = *this->timeZoneCache();
 | |
|          CString language = defaultLanguage().utf8();
 | |
|          {
 | |
| @@ -447,6 +514,21 @@ String DateCache::timeZoneDisplayName(bool isDST)
 | |
|              if (U_SUCCESS(status))
 | |
|                  m_timeZoneDSTDisplayNameCache = String::adopt(WTFMove(dstDisplayNameBuffer));
 | |
|          }
 | |
| +#else
 | |
| +        auto& timeZoneCache = *toICUTimeZone(this->timeZoneCache());
 | |
| +        String language = defaultLanguage();
 | |
| +        icu::Locale locale(language.utf8().data());
 | |
| +        {
 | |
| +            icu::UnicodeString standardDisplayName;
 | |
| +            timeZoneCache.getDisplayName(false /* inDaylight */, icu::TimeZone::LONG, locale, standardDisplayName);
 | |
| +            m_timeZoneStandardDisplayNameCache = String({ standardDisplayName.getBuffer(), static_cast<size_t>(standardDisplayName.length()) });
 | |
| +        }
 | |
| +        {
 | |
| +            icu::UnicodeString dstDisplayName;
 | |
| +            timeZoneCache.getDisplayName(true /* inDaylight */, icu::TimeZone::LONG, locale, dstDisplayName);
 | |
| +            m_timeZoneDSTDisplayNameCache = String({ dstDisplayName.getBuffer(), static_cast<size_t>(dstDisplayName.length()) });
 | |
| +        }
 | |
| +#endif
 | |
|      }
 | |
|      if (isDST)
 | |
|          return m_timeZoneDSTDisplayNameCache;
 | |
| @@ -485,6 +567,7 @@ void DateCache::timeZoneCacheSlow()
 | |
|  
 | |
|      Vector<char16_t, 32> timeZoneID;
 | |
|      getTimeZoneOverride(timeZoneID);
 | |
| +#if HAVE(ICU_C_TIMEZONE_API)
 | |
|      auto* cache = new OpaqueICUTimeZone;
 | |
|  
 | |
|      String canonical;
 | |
| @@ -508,6 +591,14 @@ void DateCache::timeZoneCacheSlow()
 | |
|      ASSERT_UNUSED(status, U_SUCCESS(status));
 | |
|      ucal_setGregorianChange(cache->m_calendar.get(), minECMAScriptTime, &status); // Ignore "unsupported" error.
 | |
|      m_timeZoneCache = std::unique_ptr<OpaqueICUTimeZone, OpaqueICUTimeZoneDeleter>(cache);
 | |
| +#else
 | |
| +    if (!timeZoneID.isEmpty()) {
 | |
| +        m_timeZoneCache = std::unique_ptr<OpaqueICUTimeZone, OpaqueICUTimeZoneDeleter>(toOpaqueICUTimeZone(icu::TimeZone::createTimeZone(icu::UnicodeString(timeZoneID.data(), timeZoneID.size()))));
 | |
| +        return;
 | |
| +    }
 | |
| +    // Do not use icu::TimeZone::createDefault. ICU internally has a cache for timezone and createDefault returns this cached value.
 | |
| +    m_timeZoneCache = std::unique_ptr<OpaqueICUTimeZone, OpaqueICUTimeZoneDeleter>(toOpaqueICUTimeZone(icu::TimeZone::detectHostTimeZone()));
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  void DateCache::resetIfNecessarySlow()
 | |
| -- 
 | |
| 2.51.0
 | |
| 
 | |
| 
 | |
| From 294fbe4a1e2d4a142696597ee8f2d1f748480691 Mon Sep 17 00:00:00 2001
 | |
| From: Michael Catanzaro <mcatanzaro@redhat.com>
 | |
| Date: Wed, 2 Apr 2025 15:56:18 -0500
 | |
| Subject: [PATCH 5/5] Build against ICU 60
 | |
| 
 | |
| ---
 | |
|  Source/JavaScriptCore/runtime/IntlCache.cpp          |  3 +++
 | |
|  Source/JavaScriptCore/runtime/IntlCache.h            |  3 +++
 | |
|  Source/JavaScriptCore/runtime/IntlDisplayNames.cpp   | 11 +++++++++++
 | |
|  Source/JavaScriptCore/runtime/IntlDisplayNames.h     |  7 +++++++
 | |
|  Source/JavaScriptCore/runtime/IntlDurationFormat.cpp |  2 +-
 | |
|  Source/JavaScriptCore/runtime/IntlObject.cpp         |  6 +++++-
 | |
|  Source/JavaScriptCore/runtime/IntlPluralRules.cpp    |  4 ++--
 | |
|  Source/JavaScriptCore/runtime/JSDateMath.cpp         |  4 ++--
 | |
|  Source/WTF/wtf/URLHelpers.cpp                        |  4 +++-
 | |
|  Source/WTF/wtf/URLParser.cpp                         |  6 +++---
 | |
|  Source/WTF/wtf/unicode/UTF8Conversion.cpp            |  4 +++-
 | |
|  Source/WebCore/dom/TextEncoder.cpp                   |  2 +-
 | |
|  Source/cmake/OptionsGTK.cmake                        |  2 +-
 | |
|  13 files changed, 45 insertions(+), 13 deletions(-)
 | |
| 
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlCache.cpp b/Source/JavaScriptCore/runtime/IntlCache.cpp
 | |
| index d099acc9f2f4..26331b92cf84 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlCache.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlCache.cpp
 | |
| @@ -26,6 +26,7 @@
 | |
|  #include "config.h"
 | |
|  #include "IntlCache.h"
 | |
|  
 | |
| +#include "IntlDisplayNames.h"
 | |
|  #include <wtf/TZoneMallocInlines.h>
 | |
|  #include <wtf/Vector.h>
 | |
|  
 | |
| @@ -56,6 +57,7 @@ Vector<char16_t, 32> IntlCache::getBestDateTimePattern(const CString& locale, st
 | |
|      return patternBuffer;
 | |
|  }
 | |
|  
 | |
| +#if HAVE(ICU_U_LOCALE_DISPLAY_NAMES)
 | |
|  Vector<char16_t, 32> IntlCache::getFieldDisplayName(const CString& locale, UDateTimePatternField field, UDateTimePGDisplayWidth width, UErrorCode& status)
 | |
|  {
 | |
|      auto sharedGenerator = getSharedPatternGenerator(locale, status);
 | |
| @@ -67,5 +69,6 @@ Vector<char16_t, 32> IntlCache::getFieldDisplayName(const CString& locale, UDate
 | |
|          return { };
 | |
|      return buffer;
 | |
|  }
 | |
| +#endif
 | |
|  
 | |
|  } // namespace JSC
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlCache.h b/Source/JavaScriptCore/runtime/IntlCache.h
 | |
| index 2b31dc1bdd9c..36adaac47e9a 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlCache.h
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlCache.h
 | |
| @@ -25,6 +25,7 @@
 | |
|  
 | |
|  #pragma once
 | |
|  
 | |
| +#include "IntlDisplayNames.h"
 | |
|  #include <unicode/udatpg.h>
 | |
|  #include <wtf/Noncopyable.h>
 | |
|  #include <wtf/TZoneMalloc.h>
 | |
| @@ -40,7 +41,9 @@ public:
 | |
|      IntlCache() = default;
 | |
|  
 | |
|      Vector<char16_t, 32> getBestDateTimePattern(const CString& locale, std::span<const char16_t> skeleton, UErrorCode&);
 | |
| +#if HAVE(ICU_U_LOCALE_DISPLAY_NAMES)
 | |
|      Vector<char16_t, 32> getFieldDisplayName(const CString& locale, UDateTimePatternField, UDateTimePGDisplayWidth, UErrorCode&);
 | |
| +#endif
 | |
|  
 | |
|  private:
 | |
|      UDateTimePatternGenerator* getSharedPatternGenerator(const CString& locale, UErrorCode& status)
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlDisplayNames.cpp b/Source/JavaScriptCore/runtime/IntlDisplayNames.cpp
 | |
| index 3742d05523f6..e8f6317d68b9 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlDisplayNames.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlDisplayNames.cpp
 | |
| @@ -106,6 +106,7 @@ void IntlDisplayNames::initializeDisplayNames(JSGlobalObject* globalObject, JSVa
 | |
|      m_languageDisplay = intlOption<LanguageDisplay>(globalObject, options, vm.propertyNames->languageDisplay, { { "dialect"_s, LanguageDisplay::Dialect }, { "standard"_s, LanguageDisplay::Standard } }, "languageDisplay must be either \"dialect\" or \"standard\""_s, LanguageDisplay::Dialect);
 | |
|      RETURN_IF_EXCEPTION(scope, void());
 | |
|  
 | |
| +#if HAVE(ICU_U_LOCALE_DISPLAY_NAMES)
 | |
|      UErrorCode status = U_ZERO_ERROR;
 | |
|  
 | |
|      UDisplayContext contexts[] = {
 | |
| @@ -133,6 +134,10 @@ void IntlDisplayNames::initializeDisplayNames(JSGlobalObject* globalObject, JSVa
 | |
|          throwTypeError(globalObject, scope, "failed to initialize DisplayNames"_s);
 | |
|          return;
 | |
|      }
 | |
| +#else
 | |
| +    throwTypeError(globalObject, scope, "failed to initialize Intl.DisplayNames since feature is not supported by the ICU version"_s);
 | |
| +    return;
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  // https://tc39.es/proposal-intl-displaynames/#sec-Intl.DisplayNames.prototype.of
 | |
| @@ -142,6 +147,7 @@ JSValue IntlDisplayNames::of(JSGlobalObject* globalObject, JSValue codeValue) co
 | |
|      VM& vm = globalObject->vm();
 | |
|      auto scope = DECLARE_THROW_SCOPE(vm);
 | |
|  
 | |
| +#if HAVE(ICU_U_LOCALE_DISPLAY_NAMES)
 | |
|      ASSERT(m_displayNames);
 | |
|      auto code = codeValue.toWTFString(globalObject);
 | |
|      RETURN_IF_EXCEPTION(scope, { });
 | |
| @@ -346,6 +352,11 @@ JSValue IntlDisplayNames::of(JSGlobalObject* globalObject, JSValue codeValue) co
 | |
|          return throwTypeError(globalObject, scope, "Failed to query a display name."_s);
 | |
|      }
 | |
|      return jsString(vm, String(WTFMove(buffer)));
 | |
| +#else
 | |
| +    UNUSED_PARAM(codeValue);
 | |
| +    throwTypeError(globalObject, scope, "failed to initialize Intl.DisplayNames since feature is not supported by the ICU version"_s);
 | |
| +    return { };
 | |
| +#endif
 | |
|  }
 | |
|  
 | |
|  // https://tc39.es/proposal-intl-displaynames/#sec-Intl.DisplayNames.prototype.resolvedOptions
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlDisplayNames.h b/Source/JavaScriptCore/runtime/IntlDisplayNames.h
 | |
| index 17f257cb04a5..883825ed93f4 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlDisplayNames.h
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlDisplayNames.h
 | |
| @@ -29,6 +29,13 @@
 | |
|  #include <unicode/uldnames.h>
 | |
|  #include <wtf/unicode/icu/ICUHelpers.h>
 | |
|  
 | |
| +#if !defined(HAVE_ICU_U_LOCALE_DISPLAY_NAMES)
 | |
| +// We need 61 or later since part of implementation uses UCURR_NARROW_SYMBOL_NAME.
 | |
| +#if U_ICU_VERSION_MAJOR_NUM >= 61
 | |
| +#define HAVE_ICU_U_LOCALE_DISPLAY_NAMES 1
 | |
| +#endif
 | |
| +#endif
 | |
| +
 | |
|  namespace JSC {
 | |
|  
 | |
|  enum class RelevantExtensionKey : uint8_t;
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp b/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp
 | |
| index c066e80ab130..6161997d3461 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlDurationFormat.cpp
 | |
| @@ -42,7 +42,6 @@
 | |
|  #endif
 | |
|  #endif
 | |
|  #include <unicode/ulistformatter.h>
 | |
| -#include <unicode/unumberformatter.h>
 | |
|  #include <unicode/ures.h>
 | |
|  #if HAVE(ICU_U_LIST_FORMATTER)
 | |
|  #define U_HIDE_DRAFT_API 1
 | |
| @@ -50,6 +49,7 @@
 | |
|  
 | |
|  #if HAVE(ICU_U_LIST_FORMATTER)
 | |
|  #include <unicode/uformattedvalue.h>
 | |
| +#include <unicode/unumberformatter.h>
 | |
|  #endif
 | |
|  
 | |
|  WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlObject.cpp b/Source/JavaScriptCore/runtime/IntlObject.cpp
 | |
| index f8413f12dec7..7041f19dfbbd 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlObject.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlObject.cpp
 | |
| @@ -168,7 +168,6 @@ namespace JSC {
 | |
|    supportedValuesOf     intlObjectFuncSupportedValuesOf              DontEnum|Function 1
 | |
|    Collator              createCollatorConstructor                    DontEnum|PropertyCallback
 | |
|    DateTimeFormat        createDateTimeFormatConstructor              DontEnum|PropertyCallback
 | |
| -  DisplayNames          createDisplayNamesConstructor                DontEnum|PropertyCallback
 | |
|    Locale                createLocaleConstructor                      DontEnum|PropertyCallback
 | |
|    NumberFormat          createNumberFormatConstructor                DontEnum|PropertyCallback
 | |
|    PluralRules           createPluralRulesConstructor                 DontEnum|PropertyCallback
 | |
| @@ -256,6 +255,11 @@ void IntlObject::finishCreation(VM& vm, JSGlobalObject*)
 | |
|      Base::finishCreation(vm);
 | |
|      ASSERT(inherits(info()));
 | |
|      JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
 | |
| +#if HAVE(ICU_U_LOCALE_DISPLAY_NAMES)
 | |
| +    putDirectWithoutTransition(vm, vm.propertyNames->DisplayNames, createDisplayNamesConstructor(vm, this), static_cast<unsigned>(PropertyAttribute::DontEnum));
 | |
| +#else
 | |
| +    UNUSED_PARAM(&createDisplayNamesConstructor);
 | |
| +#endif
 | |
|  #if HAVE(ICU_U_LIST_FORMATTER)
 | |
|      putDirectWithoutTransition(vm, vm.propertyNames->DurationFormat, createDurationFormatConstructor(vm, this), static_cast<unsigned>(PropertyAttribute::DontEnum));
 | |
|      putDirectWithoutTransition(vm, vm.propertyNames->ListFormat, createListFormatConstructor(vm, this), static_cast<unsigned>(PropertyAttribute::DontEnum));
 | |
| diff --git a/Source/JavaScriptCore/runtime/IntlPluralRules.cpp b/Source/JavaScriptCore/runtime/IntlPluralRules.cpp
 | |
| index c451478086f7..76461d3c8bcd 100644
 | |
| --- a/Source/JavaScriptCore/runtime/IntlPluralRules.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/IntlPluralRules.cpp
 | |
| @@ -278,11 +278,11 @@ JSValue IntlPluralRules::select(JSGlobalObject* globalObject, double value) cons
 | |
|      return jsString(vm, String(WTFMove(buffer)));
 | |
|  #else
 | |
|      Vector<UChar, 8> result(8);
 | |
| -    auto length = uplrules_selectWithFormat(m_pluralRules.get(), value, m_numberFormat.get(), result.data(), result.size(), &status);
 | |
| +    auto length = uplrules_selectWithFormat(m_pluralRules.get(), value, m_numberFormat.get(), const_cast<UChar*>(result.span().data()), result.span().size(), &status);
 | |
|      if (U_FAILURE(status))
 | |
|          return throwTypeError(globalObject, scope, "failed to select plural value"_s);
 | |
|  
 | |
| -    return jsString(vm, String({ result.data(), static_cast<size_t>(length) }));
 | |
| +    return jsString(vm, String({ result.span().data(), static_cast<size_t>(length) }));
 | |
|  #endif
 | |
|  }
 | |
|  
 | |
| diff --git a/Source/JavaScriptCore/runtime/JSDateMath.cpp b/Source/JavaScriptCore/runtime/JSDateMath.cpp
 | |
| index 755337022004..d4b61acb3d13 100644
 | |
| --- a/Source/JavaScriptCore/runtime/JSDateMath.cpp
 | |
| +++ b/Source/JavaScriptCore/runtime/JSDateMath.cpp
 | |
| @@ -177,7 +177,7 @@ LocalTimeOffset DateCache::calculateLocalTimeOffset(double millisecondsFromEpoch
 | |
|      }
 | |
|  #else
 | |
|      auto& timeZoneCache = *toICUTimeZone(this->timeZoneCache());
 | |
| -    if (inputTimeType != WTF::LocalTime) {
 | |
| +    if (inputTimeType != TimeType::LocalTime) {
 | |
|          constexpr bool isLocalTime = false;
 | |
|          timeZoneCache.getOffset(millisecondsFromEpoch, isLocalTime, rawOffset, dstOffset, status);
 | |
|          if (U_FAILURE(status))
 | |
| @@ -593,7 +593,7 @@ void DateCache::timeZoneCacheSlow()
 | |
|      m_timeZoneCache = std::unique_ptr<OpaqueICUTimeZone, OpaqueICUTimeZoneDeleter>(cache);
 | |
|  #else
 | |
|      if (!timeZoneID.isEmpty()) {
 | |
| -        m_timeZoneCache = std::unique_ptr<OpaqueICUTimeZone, OpaqueICUTimeZoneDeleter>(toOpaqueICUTimeZone(icu::TimeZone::createTimeZone(icu::UnicodeString(timeZoneID.data(), timeZoneID.size()))));
 | |
| +        m_timeZoneCache = std::unique_ptr<OpaqueICUTimeZone, OpaqueICUTimeZoneDeleter>(toOpaqueICUTimeZone(icu::TimeZone::createTimeZone(icu::UnicodeString(timeZoneID.span().data(), timeZoneID.size()))));
 | |
|          return;
 | |
|      }
 | |
|      // Do not use icu::TimeZone::createDefault. ICU internally has a cache for timezone and createDefault returns this cached value.
 | |
| diff --git a/Source/WTF/wtf/URLHelpers.cpp b/Source/WTF/wtf/URLHelpers.cpp
 | |
| index e984a4356715..7e0748f03722 100644
 | |
| --- a/Source/WTF/wtf/URLHelpers.cpp
 | |
| +++ b/Source/WTF/wtf/URLHelpers.cpp
 | |
| @@ -866,7 +866,9 @@ static String escapeUnsafeCharacters(const String& sourceBuffer)
 | |
|              std::array<uint8_t, 4> utf8Buffer;
 | |
|              size_t offset = 0;
 | |
|              UBool failure = false;
 | |
| -            U8_APPEND(utf8Buffer, offset, 4, c, failure);
 | |
| +            int32_t narrowedOffset = offset;
 | |
| +            U8_APPEND(utf8Buffer.data(), narrowedOffset, 4, c, failure);
 | |
| +            offset = narrowedOffset;
 | |
|              ASSERT_UNUSED(failure, !failure);
 | |
|  
 | |
|              for (size_t j = 0; j < offset; ++j) {
 | |
| diff --git a/Source/WTF/wtf/URLParser.cpp b/Source/WTF/wtf/URLParser.cpp
 | |
| index 6325d2a26ba3..b3b292536be5 100644
 | |
| --- a/Source/WTF/wtf/URLParser.cpp
 | |
| +++ b/Source/WTF/wtf/URLParser.cpp
 | |
| @@ -516,7 +516,7 @@ ALWAYS_INLINE void URLParser::utf8PercentEncode(const CodePointIterator<Characte
 | |
|      std::array<uint8_t, U8_MAX_LENGTH> buffer;
 | |
|      int32_t offset = 0;
 | |
|      UBool isError = false;
 | |
| -    U8_APPEND(buffer, offset, U8_MAX_LENGTH, codePoint, isError);
 | |
| +    U8_APPEND(buffer.data(), offset, U8_MAX_LENGTH, codePoint, isError);
 | |
|      if (isError) {
 | |
|          appendToASCIIBuffer(replacementCharacterUTF8PercentEncoded.span8());
 | |
|          return;
 | |
| @@ -544,7 +544,7 @@ ALWAYS_INLINE void URLParser::utf8QueryEncode(const CodePointIterator<CharacterT
 | |
|      std::array<uint8_t, U8_MAX_LENGTH> buffer;
 | |
|      int32_t offset = 0;
 | |
|      UBool isError = false;
 | |
| -    U8_APPEND(buffer, offset, U8_MAX_LENGTH, codePoint, isError);
 | |
| +    U8_APPEND(buffer.data(), offset, U8_MAX_LENGTH, codePoint, isError);
 | |
|      if (isError) {
 | |
|          appendToASCIIBuffer(replacementCharacterUTF8PercentEncoded.span8());
 | |
|          return;
 | |
| @@ -2878,7 +2878,7 @@ auto URLParser::parseHostAndPort(CodePointIterator<CharacterType> iterator) -> H
 | |
|          std::array<uint8_t, U8_MAX_LENGTH> buffer;
 | |
|          size_t offset = 0;
 | |
|          UBool isError = false;
 | |
| -        U8_APPEND(buffer, offset, U8_MAX_LENGTH, *iterator, isError);
 | |
| +        U8_APPEND(buffer.data(), offset, U8_MAX_LENGTH, *iterator, isError);
 | |
|          if (isError)
 | |
|              return HostParsingResult::InvalidHost;
 | |
|          utf8Encoded.append(std::span { buffer }.first(offset));
 | |
| diff --git a/Source/WTF/wtf/unicode/UTF8Conversion.cpp b/Source/WTF/wtf/unicode/UTF8Conversion.cpp
 | |
| index a86aefecd09c..c599c6727b53 100644
 | |
| --- a/Source/WTF/wtf/unicode/UTF8Conversion.cpp
 | |
| +++ b/Source/WTF/wtf/unicode/UTF8Conversion.cpp
 | |
| @@ -78,7 +78,9 @@ template<> char32_t next<Replacement::ReplaceInvalidSequences, char16_t>(std::sp
 | |
|  template<> bool append<Replacement::None, char8_t>(std::span<char8_t> characters, size_t& offset, char32_t character)
 | |
|  {
 | |
|      UBool sawError = false;
 | |
| -    U8_APPEND(characters, offset, characters.size(), character, sawError);
 | |
| +    int32_t narrowedOffset = offset;
 | |
| +    U8_APPEND((uint8_t*)characters.data(), narrowedOffset, static_cast<int32_t>(characters.size()), character, sawError);
 | |
| +    offset = narrowedOffset;
 | |
|      return sawError;
 | |
|  }
 | |
|  
 | |
| diff --git a/Source/WebCore/dom/TextEncoder.cpp b/Source/WebCore/dom/TextEncoder.cpp
 | |
| index fe8694cfce9d..7d8b5ad2e3c9 100644
 | |
| --- a/Source/WebCore/dom/TextEncoder.cpp
 | |
| +++ b/Source/WebCore/dom/TextEncoder.cpp
 | |
| @@ -55,7 +55,7 @@ auto TextEncoder::encodeInto(String&& input, Ref<Uint8Array>&& array) -> EncodeI
 | |
|              break;
 | |
|          }
 | |
|          UBool sawError = false;
 | |
| -        U8_APPEND(destinationBytes, written, destinationBytes.size(), token, sawError);
 | |
| +        U8_APPEND(destinationBytes.data(), written, destinationBytes.size(), token, sawError);
 | |
|          if (sawError)
 | |
|              break;
 | |
|          if (U_IS_BMP(token))
 | |
| diff --git a/Source/cmake/OptionsGTK.cmake b/Source/cmake/OptionsGTK.cmake
 | |
| index 83377867f3b1..aa0941950559 100644
 | |
| --- a/Source/cmake/OptionsGTK.cmake
 | |
| +++ b/Source/cmake/OptionsGTK.cmake
 | |
| @@ -11,7 +11,7 @@ find_package(Cairo 1.14.0 REQUIRED)
 | |
|  find_package(LibGcrypt 1.7.0 REQUIRED)
 | |
|  find_package(Libtasn1 REQUIRED)
 | |
|  find_package(HarfBuzz 1.4.2 REQUIRED COMPONENTS ICU)
 | |
| -find_package(ICU 61.2 REQUIRED COMPONENTS data i18n uc)
 | |
| +find_package(ICU 60 REQUIRED COMPONENTS data i18n uc)
 | |
|  find_package(JPEG REQUIRED)
 | |
|  find_package(Epoxy 1.5.4 REQUIRED)
 | |
|  find_package(LibXml2 2.8.0 REQUIRED)
 | |
| -- 
 | |
| 2.51.0
 | |
| 
 |