<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[281374] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/281374">281374</a></dd>
<dt>Author</dt> <dd>ysuzuki@apple.com</dd>
<dt>Date</dt> <dd>2021-08-21 07:33:08 -0700 (Sat, 21 Aug 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>[JSC] Intl Locale Info
https://bugs.webkit.org/show_bug.cgi?id=227830

Reviewed by Ross Kirsling.

JSTests:

* stress/intl-locale-info.js: Added.
(shouldBe):
(throw.new.Error):
(let.enGB.new.Intl.Locale.shouldBe):
(let.l.new.Intl.Locale.shouldBe):
* test262/config.yaml:

Source/JavaScriptCore:

This patch implements Intl.Locale's extension (Intl Locale Info proposal)[1], which is already stage 3.
Intl.Locale#{calendars,collations,hourCycles,numberingSystems,timeZones} can return array of preferred
configuration for the given locale. And Intl.Locale#textInfo can return text layout direction and Intl.Locale#weekInfo
can return weekday information (e.g. when weekend starts).

[1]: https://github.com/tc39/proposal-intl-locale-info

* runtime/IntlLocale.cpp:
(JSC::createArrayFromStringVector):
(JSC::IntlLocale::calendars):
(JSC::IntlLocale::collations):
(JSC::IntlLocale::hourCycles):
(JSC::IntlLocale::numberingSystems):
(JSC::IntlLocale::timeZones):
(JSC::IntlLocale::textInfo):
(JSC::IntlLocale::weekInfo):
* runtime/IntlLocale.h:
* runtime/IntlLocalePrototype.cpp:
(JSC::JSC_DEFINE_CUSTOM_GETTER):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkJSTeststest262configyaml">trunk/JSTests/test262/config.yaml</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntlCollatorcpp">trunk/Source/JavaScriptCore/runtime/IntlCollator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntlDateTimeFormatcpp">trunk/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntlLocalecpp">trunk/Source/JavaScriptCore/runtime/IntlLocale.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntlLocaleh">trunk/Source/JavaScriptCore/runtime/IntlLocale.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntlLocalePrototypecpp">trunk/Source/JavaScriptCore/runtime/IntlLocalePrototype.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntlObjectcpp">trunk/Source/JavaScriptCore/runtime/IntlObject.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntlObjecth">trunk/Source/JavaScriptCore/runtime/IntlObject.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestsstressintllocaleinfojs">trunk/JSTests/stress/intl-locale-info.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (281373 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog  2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/JSTests/ChangeLog     2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -1,5 +1,19 @@
</span><span class="cx"> 2021-08-21  Yusuke Suzuki  <ysuzuki@apple.com>
</span><span class="cx"> 
</span><ins>+        [JSC] Intl Locale Info
+        https://bugs.webkit.org/show_bug.cgi?id=227830
+
+        Reviewed by Ross Kirsling.
+
+        * stress/intl-locale-info.js: Added.
+        (shouldBe):
+        (throw.new.Error):
+        (let.enGB.new.Intl.Locale.shouldBe):
+        (let.l.new.Intl.Locale.shouldBe):
+        * test262/config.yaml:
+
+2021-08-21  Yusuke Suzuki  <ysuzuki@apple.com>
+
</ins><span class="cx">         [JSC] Extend Intl TimeZoneName Option
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=227831
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkJSTestsstressintllocaleinfojs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/intl-locale-info.js (0 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/intl-locale-info.js                         (rev 0)
+++ trunk/JSTests/stress/intl-locale-info.js    2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -0,0 +1,106 @@
</span><ins>+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`expected ${expected} but got ${actual}`);
+}
+
+{
+    let he = new Intl.Locale("he")
+    shouldBe(JSON.stringify(he.weekInfo), `{"firstDay":7,"weekendStart":5,"weekendEnd":6,"minimalDays":1}`);
+    let af = new Intl.Locale("af")
+    shouldBe(JSON.stringify(af.weekInfo), `{"firstDay":7,"weekendStart":6,"weekendEnd":7,"minimalDays":1}`);
+    let enGB = new Intl.Locale("en-GB")
+    shouldBe(JSON.stringify(enGB.weekInfo), `{"firstDay":1,"weekendStart":6,"weekendEnd":7,"minimalDays":4}`);
+}
+{
+    let l = new Intl.Locale("ar")
+    shouldBe(JSON.stringify(l.textInfo), `{"direction":"rtl"}`);
+}
+{
+    let locale = new Intl.Locale("ar")
+    shouldBe(JSON.stringify(locale.calendars), `["gregory","coptic","islamic","islamicc","islamic-tbla"]`);
+    shouldBe(JSON.stringify(locale.collations), `["compat","emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+    let ns = JSON.stringify(locale.numberingSystems);
+    shouldBe(ns === `["arab"]` || ns === `["latn"]`, true);
+    shouldBe(locale.timeZones, undefined);
+}
+{
+    let locale = new Intl.Locale("ar-EG")
+    shouldBe(JSON.stringify(locale.calendars), `["gregory","coptic","islamic","islamicc","islamic-tbla"]`);
+    shouldBe(JSON.stringify(locale.collations), `["compat","emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+    shouldBe(JSON.stringify(locale.numberingSystems), `["arab"]`);
+    shouldBe(JSON.stringify(locale.timeZones), `["Africa/Cairo"]`);
+}
+{
+    let locale = new Intl.Locale("ar-SA")
+    shouldBe(JSON.stringify(locale.calendars), `["islamic-umalqura","islamic-rgsa","islamic","gregory"]`);
+    shouldBe(JSON.stringify(locale.collations), `["compat","emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+    shouldBe(JSON.stringify(locale.numberingSystems), `["arab"]`);
+    shouldBe(JSON.stringify(locale.timeZones), `["Asia/Riyadh"]`);
+}
+{
+    let locale = new Intl.Locale("ja")
+    shouldBe(JSON.stringify(locale.calendars), `["gregory","japanese"]`);
+    shouldBe(JSON.stringify(locale.collations), `["unihan","emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h23"]`);
+    shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+    shouldBe(locale.timeZones, undefined);
+}
+{
+    let locale = new Intl.Locale("ja-JP")
+    shouldBe(JSON.stringify(locale.calendars), `["gregory","japanese"]`);
+    shouldBe(JSON.stringify(locale.collations), `["unihan","emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h23"]`);
+    shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+    shouldBe(JSON.stringify(locale.timeZones), `["Asia/Tokyo"]`);
+}
+{
+    let locale = new Intl.Locale("en-US")
+    shouldBe(JSON.stringify(locale.calendars), `["gregory"]`);
+    shouldBe(JSON.stringify(locale.collations), `["emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+    shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+    shouldBe(JSON.stringify(locale.timeZones), `["America/Adak","America/Anchorage","America/Boise","America/Chicago","America/Denver","America/Detroit","America/Indiana/Knox","America/Indiana/Marengo","America/Indiana/Petersburg","America/Indiana/Tell_City","America/Indiana/Vevay","America/Indiana/Vincennes","America/Indiana/Winamac","America/Indianapolis","America/Juneau","America/Kentucky/Monticello","America/Los_Angeles","America/Louisville","America/Menominee","America/Metlakatla","America/New_York","America/Nome","America/North_Dakota/Beulah","America/North_Dakota/Center","America/North_Dakota/New_Salem","America/Phoenix","America/Sitka","America/Yakutat","Pacific/Honolulu"]`);
+}
+{
+    let locale = new Intl.Locale("en-NZ")
+    shouldBe(JSON.stringify(locale.calendars), `["gregory"]`);
+    shouldBe(JSON.stringify(locale.collations), `["emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+    shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+    shouldBe(JSON.stringify(locale.timeZones), `["Pacific/Auckland","Pacific/Chatham"]`);
+}
+{
+    let locale = new Intl.Locale("zh")
+    shouldBe(JSON.stringify(locale.calendars), `["gregory","chinese"]`);
+    shouldBe(JSON.stringify(locale.collations), `["pinyin","big5han","gb2312","stroke","unihan","zhuyin","emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+    shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+    shouldBe(locale.timeZones, undefined);
+}
+{
+    let locale = new Intl.Locale("zh-TW")
+    shouldBe(JSON.stringify(locale.calendars), `["gregory","roc","chinese"]`);
+    shouldBe(JSON.stringify(locale.collations), `["stroke","big5han","gb2312","pinyin","unihan","zhuyin","emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+    shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+    shouldBe(JSON.stringify(locale.timeZones), `["Asia/Taipei"]`);
+}
+{
+    let locale = new Intl.Locale("zh-HK")
+    shouldBe(JSON.stringify(locale.calendars), `["gregory","chinese"]`);
+    shouldBe(JSON.stringify(locale.collations), `["stroke","big5han","gb2312","pinyin","unihan","zhuyin","emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h12"]`);
+    shouldBe(JSON.stringify(locale.numberingSystems), `["latn"]`);
+    shouldBe(JSON.stringify(locale.timeZones), `["Asia/Hong_Kong"]`);
+}
+{
+    let locale = new Intl.Locale("fa")
+    shouldBe(JSON.stringify(locale.calendars), `["persian","gregory","islamic","islamicc","islamic-tbla"]`);
+    shouldBe(JSON.stringify(locale.collations), `["emoji","eor"]`);
+    shouldBe(JSON.stringify(locale.hourCycles), `["h23"]`);
+    shouldBe(JSON.stringify(locale.numberingSystems), `["arabext"]`);
+    shouldBe(locale.timeZones, undefined);
+}
</ins></span></pre></div>
<a id="trunkJSTeststest262configyaml"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/test262/config.yaml (281373 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/test262/config.yaml        2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/JSTests/test262/config.yaml   2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -24,7 +24,6 @@
</span><span class="cx">     # https://bugs.webkit.org/show_bug.cgi?id=174931
</span><span class="cx">     - regexp-lookbehind
</span><span class="cx">     - cleanupSome
</span><del>-    - Intl.Locale-info
</del><span class="cx">     - resizable-arraybuffer
</span><span class="cx">     - Object.hasOwn
</span><span class="cx">     - Temporal
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (281373 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog    2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/JavaScriptCore/ChangeLog       2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -1,5 +1,32 @@
</span><span class="cx"> 2021-08-21  Yusuke Suzuki  <ysuzuki@apple.com>
</span><span class="cx"> 
</span><ins>+        [JSC] Intl Locale Info
+        https://bugs.webkit.org/show_bug.cgi?id=227830
+
+        Reviewed by Ross Kirsling.
+
+        This patch implements Intl.Locale's extension (Intl Locale Info proposal)[1], which is already stage 3.
+        Intl.Locale#{calendars,collations,hourCycles,numberingSystems,timeZones} can return array of preferred
+        configuration for the given locale. And Intl.Locale#textInfo can return text layout direction and Intl.Locale#weekInfo
+        can return weekday information (e.g. when weekend starts).
+
+        [1]: https://github.com/tc39/proposal-intl-locale-info
+
+        * runtime/IntlLocale.cpp:
+        (JSC::createArrayFromStringVector):
+        (JSC::IntlLocale::calendars):
+        (JSC::IntlLocale::collations):
+        (JSC::IntlLocale::hourCycles):
+        (JSC::IntlLocale::numberingSystems):
+        (JSC::IntlLocale::timeZones):
+        (JSC::IntlLocale::textInfo):
+        (JSC::IntlLocale::weekInfo):
+        * runtime/IntlLocale.h:
+        * runtime/IntlLocalePrototype.cpp:
+        (JSC::JSC_DEFINE_CUSTOM_GETTER):
+
+2021-08-21  Yusuke Suzuki  <ysuzuki@apple.com>
+
</ins><span class="cx">         [JSC] Extend Intl TimeZoneName Option
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=227831
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntlCollatorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/IntlCollator.cpp (281373 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/IntlCollator.cpp     2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/JavaScriptCore/runtime/IntlCollator.cpp        2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -90,23 +90,17 @@
</span><span class="cx">         UErrorCode status = U_ZERO_ERROR;
</span><span class="cx">         auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucol_getKeywordValuesForLocale("collation", locale.utf8().data(), false, &status));
</span><span class="cx">         if (U_SUCCESS(status)) {
</span><del>-            const char* collation;
-            while ((collation = uenum_next(enumeration.get(), nullptr, &status)) && U_SUCCESS(status)) {
</del><ins>+            const char* pointer;
+            int32_t length = 0;
+            while ((pointer = uenum_next(enumeration.get(), &length, &status)) && U_SUCCESS(status)) {
</ins><span class="cx">                 // 10.2.3 "The values "standard" and "search" must not be used as elements in any [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co array."
</span><del>-                if (!strcmp(collation, "standard") || !strcmp(collation, "search"))
</del><ins>+                String collation(pointer, length);
+                if (collation == "standard"_s || collation == "search"_s)
</ins><span class="cx">                     continue;
</span><del>-
-                // Map keyword values to BCP 47 equivalents.
-                if (!strcmp(collation, "dictionary"))
-                    keyLocaleData.append("dict"_s);
-                else if (!strcmp(collation, "gb2312han"))
-                    keyLocaleData.append("gb2312"_s);
-                else if (!strcmp(collation, "phonebook"))
-                    keyLocaleData.append("phonebk"_s);
-                else if (!strcmp(collation, "traditional"))
-                    keyLocaleData.append("trad"_s);
</del><ins>+                if (auto mapped = mapICUCollationKeywordToBCP47(collation))
+                    keyLocaleData.append(WTFMove(mapped.value()));
</ins><span class="cx">                 else
</span><del>-                    keyLocaleData.append(collation);
</del><ins>+                    keyLocaleData.append(WTFMove(collation));
</ins><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx">         break;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntlDateTimeFormatcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp (281373 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp       2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp  2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -165,13 +165,8 @@
</span><span class="cx">             ASSERT(U_SUCCESS(status));
</span><span class="cx">             String calendar = String(availableName, nameLength);
</span><span class="cx">             keyLocaleData.append(calendar);
</span><del>-            // Ensure aliases used in language tag are allowed.
-            if (calendar == "gregorian")
-                keyLocaleData.append("gregory"_s);
-            else if (calendar == "islamic-civil")
-                keyLocaleData.append("islamicc"_s);
-            else if (calendar == "ethiopic-amete-alem")
-                keyLocaleData.append("ethioaa"_s);
</del><ins>+            if (auto mapped = mapICUCalendarKeywordToBCP47(calendar))
+                keyLocaleData.append(WTFMove(mapped.value()));
</ins><span class="cx">         }
</span><span class="cx">         uenum_close(calendars);
</span><span class="cx">         break;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntlLocalecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/IntlLocale.cpp (281373 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/IntlLocale.cpp       2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/JavaScriptCore/runtime/IntlLocale.cpp  2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -29,7 +29,12 @@
</span><span class="cx"> 
</span><span class="cx"> #include "IntlObjectInlines.h"
</span><span class="cx"> #include "JSCInlines.h"
</span><ins>+#include <unicode/ucal.h>
+#include <unicode/ucol.h>
+#include <unicode/udat.h>
+#include <unicode/udatpg.h>
</ins><span class="cx"> #include <unicode/uloc.h>
</span><ins>+#include <unicode/unumsys.h>
</ins><span class="cx"> #include <wtf/unicode/icu/ICUHelpers.h>
</span><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="lines">@@ -532,4 +537,333 @@
</span><span class="cx">     return m_numeric;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static inline JSArray* createArrayFromStringVector(JSGlobalObject* globalObject, Vector<String, 1>&& elements)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSArray* result = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), elements.size());
+    if (!result) {
+        throwOutOfMemoryError(globalObject, scope);
+        return nullptr;
+    }
+    for (unsigned index = 0; index < elements.size(); ++index) {
+        result->putDirectIndex(globalObject, index, jsString(vm, WTFMove(elements[index])));
+        RETURN_IF_EXCEPTION(scope, { });
+    }
+    return result;
+}
+
+JSArray* IntlLocale::calendars(JSGlobalObject* globalObject)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    Vector<String, 1> elements;
+
+    String preferred = calendar();
+    if (!preferred.isEmpty()) {
+        elements.append(WTFMove(preferred));
+        RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+    }
+
+    UErrorCode status = U_ZERO_ERROR;
+    constexpr bool commonlyUsed = true;
+    auto calendars = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucal_getKeywordValuesForLocale("calendar", m_localeID.data(), commonlyUsed, &status));
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return nullptr;
+    }
+
+    const char* pointer;
+    int32_t length = 0;
+    while ((pointer = uenum_next(calendars.get(), &length, &status)) && U_SUCCESS(status)) {
+        String calendar(pointer, length);
+        if (auto mapped = mapICUCalendarKeywordToBCP47(calendar))
+            elements.append(WTFMove(mapped.value()));
+        else
+            elements.append(WTFMove(calendar));
+    }
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return nullptr;
+    }
+
+    RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+}
+
+JSArray* IntlLocale::collations(JSGlobalObject* globalObject)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    Vector<String, 1> elements;
+
+    String preferred = collation();
+    if (!preferred.isEmpty()) {
+        elements.append(WTFMove(preferred));
+        RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+    }
+
+    UErrorCode status = U_ZERO_ERROR;
+    constexpr bool commonlyUsed = true;
+    auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucol_getKeywordValuesForLocale("collation", m_localeID.data(), commonlyUsed, &status));
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return nullptr;
+    }
+
+    const char* pointer;
+    int32_t length = 0;
+    while ((pointer = uenum_next(enumeration.get(), &length, &status)) && U_SUCCESS(status)) {
+        String collation(pointer, length);
+        // 1.1.3 step 4, The values "standard" and "search" must be excluded from list.
+        if (collation == "standard"_s || collation == "search"_s)
+            continue;
+        if (auto mapped = mapICUCollationKeywordToBCP47(collation))
+            elements.append(WTFMove(mapped.value()));
+        else
+            elements.append(WTFMove(collation));
+    }
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return nullptr;
+    }
+
+    RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+}
+
+JSArray* IntlLocale::hourCycles(JSGlobalObject* globalObject)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    Vector<String, 1> elements;
+
+    String preferred = hourCycle();
+    if (!preferred.isEmpty()) {
+        elements.append(WTFMove(preferred));
+        RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+    }
+
+    UErrorCode status = U_ZERO_ERROR;
+    auto generator = std::unique_ptr<UDateTimePatternGenerator, ICUDeleter<udatpg_close>>(udatpg_open(m_localeID.data(), &status));
+    if (U_FAILURE(status))
+        return nullptr;
+
+    // Use "j" skeleton and parse pattern to retrieve the configured hour-cycle information.
+    constexpr const UChar skeleton[] = { 'j', 0 };
+    Vector<UChar, 32> pattern;
+    status = callBufferProducingFunction(udatpg_getBestPatternWithOptions, generator.get(), skeleton, 1, UDATPG_MATCH_HOUR_FIELD_LENGTH, pattern);
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return nullptr;
+    }
+
+    for (unsigned i = 0; i < pattern.size(); ++i) {
+        UChar currentCharacter = pattern[i];
+        if (!isASCIIAlpha(currentCharacter))
+            continue;
+
+        while (i + 1 < pattern.size() && pattern[i + 1] == currentCharacter)
+            ++i;
+
+        switch (currentCharacter) {
+        case 'h': {
+            elements.append("h12"_s);
+            RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+        }
+        case 'H': {
+            elements.append("h23"_s);
+            RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+        }
+        case 'k': {
+            elements.append("h24"_s);
+            RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+        }
+        case 'K': {
+            elements.append("h11"_s);
+            RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+        }
+        default:
+            break;
+        }
+    }
+
+    RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+}
+
+JSArray* IntlLocale::numberingSystems(JSGlobalObject* globalObject)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    Vector<String, 1> elements;
+    String preferred = numberingSystem();
+    if (!preferred.isEmpty()) {
+        elements.append(WTFMove(preferred));
+        RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+    }
+
+    UErrorCode status = U_ZERO_ERROR;
+    auto numberingSystem = std::unique_ptr<UNumberingSystem, ICUDeleter<unumsys_close>>(unumsys_open(m_localeID.data(), &status));
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return nullptr;
+    }
+    elements.append(unumsys_getName(numberingSystem.get()));
+
+    RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+}
+
+JSValue IntlLocale::timeZones(JSGlobalObject* globalObject)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    Vector<String, 1> elements;
+
+    // 11.6-3 Let region be the substring of locale corresponding to the unicode_region_subtag production of the unicode_language_id.
+    String region = this->region();
+    if (region.isEmpty())
+        return jsUndefined();
+
+    UErrorCode status = U_ZERO_ERROR;
+    auto enumeration = std::unique_ptr<UEnumeration, ICUDeleter<uenum_close>>(ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, region.utf8().data(), nullptr, &status));
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return { };
+    }
+
+    int32_t length;
+    const char* collation;
+    while ((collation = uenum_next(enumeration.get(), &length, &status)) && U_SUCCESS(status))
+        elements.constructAndAppend(collation, length);
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return { };
+    }
+
+    RELEASE_AND_RETURN(scope, createArrayFromStringVector(globalObject, WTFMove(elements)));
+}
+
+JSObject* IntlLocale::textInfo(JSGlobalObject* globalObject)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    UErrorCode status = U_ZERO_ERROR;
+    ULayoutType layout = uloc_getCharacterOrientation(m_localeID.data(), &status);
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return nullptr;
+    }
+
+    JSString* layoutString = nullptr;
+    switch (layout) {
+    default:
+    case ULOC_LAYOUT_LTR:
+        layoutString = jsString(vm, "ltr"_s);
+        break;
+    case ULOC_LAYOUT_RTL:
+        layoutString = jsString(vm, "rtl"_s);
+        break;
+    case ULOC_LAYOUT_TTB:
+        layoutString = jsString(vm, "ttb"_s);
+        break;
+    case ULOC_LAYOUT_BTT:
+        layoutString = jsString(vm, "btt"_s);
+        break;
+    }
+
+    JSObject* result = constructEmptyObject(globalObject);
+    result->putDirect(vm, Identifier::fromString(vm, "direction"), layoutString);
+    return result;
+}
+
+JSObject* IntlLocale::weekInfo(JSGlobalObject* globalObject)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    UErrorCode status = U_ZERO_ERROR;
+    auto calendar = std::unique_ptr<UCalendar, ICUDeleter<ucal_close>>(ucal_open(nullptr, 0, m_localeID.data(), UCAL_DEFAULT, &status));
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return nullptr;
+    }
+
+    int32_t firstDayOfWeek = ucal_getAttribute(calendar.get(), UCAL_FIRST_DAY_OF_WEEK);
+    int32_t minimalDays = ucal_getAttribute(calendar.get(), UCAL_MINIMAL_DAYS_IN_FIRST_WEEK);
+
+    auto canonicalizeDayOfWeekType = [](UCalendarWeekdayType type) {
+        switch (type) {
+        // UCAL_WEEKEND_ONSET is a day that starts as a weekday and transitions to the weekend. It means this is WeekDay.
+        case UCAL_WEEKEND_ONSET:
+        case UCAL_WEEKDAY:
+            return UCAL_WEEKDAY;
+        // UCAL_WEEKEND_CEASE is a day that starts as the weekend and transitions to a weekday. It means this is WeekEnd.
+        case UCAL_WEEKEND_CEASE:
+        case UCAL_WEEKEND:
+            return UCAL_WEEKEND;
+        default:
+            return UCAL_WEEKEND;
+        }
+    };
+
+    static_assert(UCAL_SUNDAY == 1);
+    static_assert(UCAL_SATURDAY == 7);
+    UCalendarWeekdayType previous = canonicalizeDayOfWeekType(ucal_getDayOfWeekType(calendar.get(), UCAL_SATURDAY, &status));
+    if (!U_SUCCESS(status)) {
+        throwTypeError(globalObject, scope, "invalid locale"_s);
+        return nullptr;
+    }
+
+    int32_t weekendStart = 0;
+    int32_t weekendEnd = 0;
+    for (int32_t day = UCAL_SUNDAY; day <= UCAL_SATURDAY; ++day) {
+        UCalendarWeekdayType type = canonicalizeDayOfWeekType(ucal_getDayOfWeekType(calendar.get(), static_cast<UCalendarDaysOfWeek>(day), &status));
+        if (!U_SUCCESS(status)) {
+            throwTypeError(globalObject, scope, "invalid locale"_s);
+            return nullptr;
+        }
+        if (previous != type) {
+            switch (type) {
+            case UCAL_WEEKDAY: // WeekEnd => WeekDay
+                if (day == UCAL_SUNDAY)
+                    weekendEnd = UCAL_SATURDAY;
+                else
+                    weekendEnd = day - 1;
+                break;
+            case UCAL_WEEKEND: // WeekDay => WeekEnd
+                weekendStart = day;
+                break;
+            default:
+                ASSERT_NOT_REACHED();
+                break;
+            }
+        }
+        previous = type;
+    }
+
+    auto convertUCalendarDaysOfWeekToMondayBasedDay = [](int32_t day) -> int32_t {
+        // Convert from
+        //     Sunday => 1
+        //     Saturday => 7
+        // to
+        //     Monday => 1
+        //     Sunday => 7
+        if (day == UCAL_SUNDAY)
+            return 7;
+        return day - 1;
+    };
+
+    JSObject* result = constructEmptyObject(globalObject);
+    result->putDirect(vm, Identifier::fromString(vm, "firstDay"), jsNumber(convertUCalendarDaysOfWeekToMondayBasedDay(firstDayOfWeek)));
+    result->putDirect(vm, Identifier::fromString(vm, "weekendStart"), jsNumber(convertUCalendarDaysOfWeekToMondayBasedDay(weekendStart)));
+    result->putDirect(vm, Identifier::fromString(vm, "weekendEnd"), jsNumber(convertUCalendarDaysOfWeekToMondayBasedDay(weekendEnd)));
+    result->putDirect(vm, Identifier::fromString(vm, "minimalDays"), jsNumber(minimalDays));
+    return result;
+}
+
</ins><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntlLocaleh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/IntlLocale.h (281373 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/IntlLocale.h 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/JavaScriptCore/runtime/IntlLocale.h    2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -69,6 +69,14 @@
</span><span class="cx">     const String& numberingSystem();
</span><span class="cx">     TriState numeric();
</span><span class="cx"> 
</span><ins>+    JSArray* calendars(JSGlobalObject*);
+    JSArray* collations(JSGlobalObject*);
+    JSArray* hourCycles(JSGlobalObject*);
+    JSArray* numberingSystems(JSGlobalObject*);
+    JSValue timeZones(JSGlobalObject*);
+    JSObject* textInfo(JSGlobalObject*);
+    JSObject* weekInfo(JSGlobalObject*);
+
</ins><span class="cx"> private:
</span><span class="cx">     IntlLocale(VM&, Structure*);
</span><span class="cx">     void finishCreation(VM&);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntlLocalePrototypecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/IntlLocalePrototype.cpp (281373 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/IntlLocalePrototype.cpp      2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/JavaScriptCore/runtime/IntlLocalePrototype.cpp 2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -36,14 +36,21 @@
</span><span class="cx"> static JSC_DECLARE_HOST_FUNCTION(IntlLocalePrototypeFuncToString);
</span><span class="cx"> static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterBaseName);
</span><span class="cx"> static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterCalendar);
</span><ins>+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterCalendars);
</ins><span class="cx"> static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterCaseFirst);
</span><span class="cx"> static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterCollation);
</span><ins>+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterCollations);
</ins><span class="cx"> static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterHourCycle);
</span><ins>+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterHourCycles);
</ins><span class="cx"> static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterNumeric);
</span><span class="cx"> static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterNumberingSystem);
</span><ins>+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterNumberingSystems);
</ins><span class="cx"> static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterLanguage);
</span><span class="cx"> static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterScript);
</span><span class="cx"> static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterRegion);
</span><ins>+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterTimeZones);
+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterTextInfo);
+static JSC_DECLARE_CUSTOM_GETTER(IntlLocalePrototypeGetterWeekInfo);
</ins><span class="cx"> 
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -60,14 +67,21 @@
</span><span class="cx">   toString         IntlLocalePrototypeFuncToString           DontEnum|Function 0
</span><span class="cx">   baseName         IntlLocalePrototypeGetterBaseName         DontEnum|ReadOnly|CustomAccessor
</span><span class="cx">   calendar         IntlLocalePrototypeGetterCalendar         DontEnum|ReadOnly|CustomAccessor
</span><ins>+  calendars        IntlLocalePrototypeGetterCalendars        DontEnum|ReadOnly|CustomAccessor
</ins><span class="cx">   caseFirst        IntlLocalePrototypeGetterCaseFirst        DontEnum|ReadOnly|CustomAccessor
</span><span class="cx">   collation        IntlLocalePrototypeGetterCollation        DontEnum|ReadOnly|CustomAccessor
</span><ins>+  collations       IntlLocalePrototypeGetterCollations       DontEnum|ReadOnly|CustomAccessor
</ins><span class="cx">   hourCycle        IntlLocalePrototypeGetterHourCycle        DontEnum|ReadOnly|CustomAccessor
</span><ins>+  hourCycles       IntlLocalePrototypeGetterHourCycles       DontEnum|ReadOnly|CustomAccessor
</ins><span class="cx">   numeric          IntlLocalePrototypeGetterNumeric          DontEnum|ReadOnly|CustomAccessor
</span><span class="cx">   numberingSystem  IntlLocalePrototypeGetterNumberingSystem  DontEnum|ReadOnly|CustomAccessor
</span><ins>+  numberingSystems IntlLocalePrototypeGetterNumberingSystems DontEnum|ReadOnly|CustomAccessor
</ins><span class="cx">   language         IntlLocalePrototypeGetterLanguage         DontEnum|ReadOnly|CustomAccessor
</span><span class="cx">   script           IntlLocalePrototypeGetterScript           DontEnum|ReadOnly|CustomAccessor
</span><span class="cx">   region           IntlLocalePrototypeGetterRegion           DontEnum|ReadOnly|CustomAccessor
</span><ins>+  timeZones        IntlLocalePrototypeGetterTimeZones        DontEnum|ReadOnly|CustomAccessor
+  textInfo         IntlLocalePrototypeGetterTextInfo         DontEnum|ReadOnly|CustomAccessor
+  weekInfo         IntlLocalePrototypeGetterWeekInfo         DontEnum|ReadOnly|CustomAccessor
</ins><span class="cx"> @end
</span><span class="cx"> */
</span><span class="cx"> 
</span><span class="lines">@@ -169,6 +183,19 @@
</span><span class="cx">     RELEASE_AND_RETURN(scope, JSValue::encode(calendar.isNull() ? jsUndefined() : jsString(vm, calendar)));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.calendars
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterCalendars, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+    if (!locale)
+        return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.calendars called on value that's not an object initialized as a Locale"_s);
+
+    RELEASE_AND_RETURN(scope, JSValue::encode(locale->calendars(globalObject)));
+}
+
</ins><span class="cx"> // https://tc39.es/ecma402/#sec-Intl.Locale.prototype.caseFirst
</span><span class="cx"> JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterCaseFirst, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
</span><span class="cx"> {
</span><span class="lines">@@ -197,6 +224,19 @@
</span><span class="cx">     RELEASE_AND_RETURN(scope, JSValue::encode(collation.isNull() ? jsUndefined() : jsString(vm, collation)));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.collations
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterCollations, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+    if (!locale)
+        return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.collations called on value that's not an object initialized as a Locale"_s);
+
+    RELEASE_AND_RETURN(scope, JSValue::encode(locale->collations(globalObject)));
+}
+
</ins><span class="cx"> // https://tc39.es/ecma402/#sec-Intl.Locale.prototype.hourCycle
</span><span class="cx"> JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterHourCycle, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
</span><span class="cx"> {
</span><span class="lines">@@ -211,6 +251,19 @@
</span><span class="cx">     RELEASE_AND_RETURN(scope, JSValue::encode(hourCycle.isNull() ? jsUndefined() : jsString(vm, hourCycle)));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.hourcycles
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterHourCycles, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+    if (!locale)
+        return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.hourCycles called on value that's not an object initialized as a Locale"_s);
+
+    RELEASE_AND_RETURN(scope, JSValue::encode(locale->hourCycles(globalObject)));
+}
+
</ins><span class="cx"> // https://tc39.es/ecma402/#sec-Intl.Locale.prototype.numeric
</span><span class="cx"> JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterNumeric, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
</span><span class="cx"> {
</span><span class="lines">@@ -238,6 +291,19 @@
</span><span class="cx">     RELEASE_AND_RETURN(scope, JSValue::encode(numberingSystem.isNull() ? jsUndefined() : jsString(vm, numberingSystem)));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.numberingSystems
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterNumberingSystems, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+    if (!locale)
+        return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.numberingSystems called on value that's not an object initialized as a Locale"_s);
+
+    RELEASE_AND_RETURN(scope, JSValue::encode(locale->numberingSystems(globalObject)));
+}
+
</ins><span class="cx"> // https://tc39.es/ecma402/#sec-Intl.Locale.prototype.language
</span><span class="cx"> JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterLanguage, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
</span><span class="cx"> {
</span><span class="lines">@@ -280,4 +346,43 @@
</span><span class="cx">     RELEASE_AND_RETURN(scope, JSValue::encode(region.isEmpty() ? jsUndefined() : jsString(vm, region)));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.timezones
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterTimeZones, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+    if (!locale)
+        return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.timeZones called on value that's not an object initialized as a Locale"_s);
+
+    RELEASE_AND_RETURN(scope, JSValue::encode(locale->timeZones(globalObject)));
+}
+
+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.textInfo
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterTextInfo, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+    if (!locale)
+        return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.textInfo called on value that's not an object initialized as a Locale"_s);
+
+    RELEASE_AND_RETURN(scope, JSValue::encode(locale->textInfo(globalObject)));
+}
+
+// https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.weekInfo
+JSC_DEFINE_CUSTOM_GETTER(IntlLocalePrototypeGetterWeekInfo, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* locale = jsDynamicCast<IntlLocale*>(vm, JSValue::decode(thisValue));
+    if (!locale)
+        return throwVMTypeError(globalObject, scope, "Intl.Locale.prototype.weekInfo called on value that's not an object initialized as a Locale"_s);
+
+    RELEASE_AND_RETURN(scope, JSValue::encode(locale->weekInfo(globalObject)));
+}
+
</ins><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntlObjectcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/IntlObject.cpp (281373 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/IntlObject.cpp       2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/JavaScriptCore/runtime/IntlObject.cpp  2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -1458,6 +1458,31 @@
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+std::optional<String> mapICUCalendarKeywordToBCP47(const String& calendar)
+{
+    if (calendar == "gregorian"_s)
+        return "gregory"_s;
+    if (calendar == "islamic-civil"_s)
+        return "islamicc"_s;
+    if (calendar == "ethiopic-amete-alem"_s)
+        return "ethioaa"_s;
+    return std::nullopt;
+}
+
+std::optional<String> mapICUCollationKeywordToBCP47(const String& collation)
+{
+    // Map keyword values to BCP 47 equivalents.
+    if (collation == "dictionary"_s)
+        return "dict"_s;
+    if (collation == "gb2312han"_s)
+        return "gb2312"_s;
+    if (collation == "phonebook"_s)
+        return "phonebk"_s;
+    if (collation == "traditional"_s)
+        return "trad"_s;
+    return std::nullopt;
+}
+
</ins><span class="cx"> JSC_DEFINE_HOST_FUNCTION(intlObjectFuncGetCanonicalLocales, (JSGlobalObject* globalObject, CallFrame* callFrame))
</span><span class="cx"> {
</span><span class="cx">     // Intl.getCanonicalLocales(locales)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntlObjecth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/IntlObject.h (281373 => 281374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/IntlObject.h 2021-08-21 13:49:32 UTC (rev 281373)
+++ trunk/Source/JavaScriptCore/runtime/IntlObject.h    2021-08-21 14:33:08 UTC (rev 281374)
</span><span class="lines">@@ -136,4 +136,7 @@
</span><span class="cx">     void operator()(UFieldPositionIterator*) const;
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+std::optional<String> mapICUCollationKeywordToBCP47(const String&);
+std::optional<String> mapICUCalendarKeywordToBCP47(const String&);
+
</ins><span class="cx"> } // namespace JSC
</span></span></pre>
</div>
</div>

</body>
</html>