]> git.kaiwu.me - quickjs.git/commitdiff
fixed 6 test262 errors: Intl.Locale now recognizes u-extension keys starting with...
authorFabrice Bellard <fabrice@bellard.org>
Mon, 19 Oct 2026 16:30:29 +0000 (18:30 +0200)
committerFabrice Bellard <fabrice@bellard.org>
Mon, 19 Oct 2026 16:30:29 +0000 (18:30 +0200)
intl/intl.c
intl/locale.c
test262_errors.txt

index 2ce1e14e3a6973414101c77c1c2c1431ef2aeca3..d1cfbbd6ba9ad517059369f7ec80692bd1179921 100644 (file)
@@ -1194,8 +1194,8 @@ char *canonicalize_locale(JSContext *ctx, const char *locale)
                 const char *sub = subtags[ext_blocks[e].start + j];
                 int slen = (int)strlen(sub);
                 /* u-extension: canonicalize yes -> true for specific keys, then strip true */
-                if (singleton == 'u' && slen == 2 && is_alpha(sub, 1) &&
-                    (is_alpha(sub + 1, 1) || is_digit(sub + 1, 1))) {
+                if (singleton == 'u' && slen == 2 && is_alphanum(sub, 1) &&
+                    is_alpha(sub + 1, 1)) {
                     int skip_value = 0;
                     const char *value = NULL;
                     if (j + 1 < ext_blocks[e].len) {
index 2f271a4aee2d988ad385d69a2e0e1c09438456b5..507e2a12c1ac34e12cd3d017151872014bb055e3 100644 (file)
@@ -252,6 +252,31 @@ static const char *canonicalize_keyword_value(const char *key, const char *value
     return value;
 }
 
+/* CanonicalizeUValue (ecma402): ASCII-lowercase the value, then canonicalize
+   it as a value of key per UTS35 Annex C. A value which canonicalizes to
+   "true" is removed, i.e. the empty String is returned.
+   Returns a newly allocated string. */
+static char *canonicalize_u_value(JSContext *ctx, const char *key, const char *value)
+{
+    char *lower = js_strdup(ctx, value);
+    const char *canon;
+    if (!lower)
+        return NULL;
+    to_lower_case(lower);
+    canon = canonicalize_keyword_value(key, lower);
+    if (canon != lower) {
+        js_free(ctx, lower);
+        lower = js_strdup(ctx, canon);
+        if (!lower)
+            return NULL;
+    }
+    if (!strcmp(lower, "true")) {
+        js_free(ctx, lower);
+        lower = js_strdup(ctx, "");
+    }
+    return lower;
+}
+
 static int locale_parse_u_keywords(JSContext *ctx, const char *u_ext, int u_len,
                                    UKeyword *keywords, int max_kw,
                                    char **attributes)
@@ -263,8 +288,8 @@ static int locale_parse_u_keywords(JSContext *ctx, const char *u_ext, int u_len,
         int j = i;
         while (j < u_len && u_ext[j] != '-') j++;
         int slen = j - i;
-        if (slen == 2 && isalpha((unsigned char)u_ext[i]) &&
-            (isalpha((unsigned char)u_ext[i+1]) || isdigit((unsigned char)u_ext[i+1]))) {
+        if (slen == 2 && isalnum((unsigned char)u_ext[i]) &&
+            isalpha((unsigned char)u_ext[i+1])) {
             /* keyword key */
             char key[3] = {u_ext[i], u_ext[i+1], '\0'};
             i = j + 1;
@@ -637,7 +662,12 @@ if (!str) goto fail;
                 goto fail;
             }
             {
-                const char *cal_val = canonicalize_keyword_value("ca", str);
+                const char *cal_val_raw = canonicalize_keyword_value("ca", str);
+                char *cal_val = canonicalize_u_value(ctx, "ca", cal_val_raw);
+                if (!cal_val) {
+                    JS_FreeCString(ctx, str);
+                    goto fail;
+                }
                 /* replace or add keyword */
                 for (i = 0; i < num_u_keywords; i++) {
                     if (!strcmp(u_keywords[i].key, "ca")) {
@@ -651,7 +681,9 @@ if (!str) goto fail;
                     u_keywords[num_u_keywords].value = js_strdup(ctx, cal_val);
                     num_u_keywords++;
                 }
+                js_free(ctx, out->calendar);
                 out->calendar = js_strdup(ctx, cal_val);
+                js_free(ctx, cal_val);
                 JS_FreeCString(ctx, str);
             }
         }
@@ -668,19 +700,33 @@ if (!str) goto fail;
                 JS_ThrowRangeError(ctx, "invalid collation");
                 goto fail;
             }
-            for (i = 0; i < num_u_keywords; i++) {
-                if (!strcmp(u_keywords[i].key, "co")) {
-                    js_free(ctx, u_keywords[i].value);
-                    u_keywords[i].value = js_strdup(ctx, str);
-                    break;
-                }
+            if (!is_valid_type(str)) {
+                JS_FreeCString(ctx, str);
+                JS_ThrowRangeError(ctx, "invalid collation");
+                goto fail;
             }
-            if (i == num_u_keywords && num_u_keywords < 16) {
-                u_keywords[num_u_keywords].key = js_strdup(ctx, "co");
-                u_keywords[num_u_keywords].value = js_strdup(ctx, str);
-                num_u_keywords++;
+            {
+                char *co_val = canonicalize_u_value(ctx, "co", str);
+                if (!co_val) {
+                    JS_FreeCString(ctx, str);
+                    goto fail;
+                }
+                for (i = 0; i < num_u_keywords; i++) {
+                    if (!strcmp(u_keywords[i].key, "co")) {
+                        js_free(ctx, u_keywords[i].value);
+                        u_keywords[i].value = js_strdup(ctx, co_val);
+                        break;
+                    }
+                }
+                if (i == num_u_keywords && num_u_keywords < 16) {
+                    u_keywords[num_u_keywords].key = js_strdup(ctx, "co");
+                    u_keywords[num_u_keywords].value = js_strdup(ctx, co_val);
+                    num_u_keywords++;
+                }
+                js_free(ctx, out->collation);
+                out->collation = js_strdup(ctx, co_val);
+                js_free(ctx, co_val);
             }
-            out->collation = js_strdup(ctx, str);
             JS_FreeCString(ctx, str);
         }
 
@@ -693,6 +739,7 @@ JS_FreeValue(ctx, val);
 if (!str) goto fail;
             {
                 const char *fw_val = weekday_to_u_value(str);
+                char *fw_canon = NULL;
                 if (!fw_val) {
                     /* not a weekday name, validate as type */
                     if (!is_valid_type(str)) {
@@ -700,7 +747,12 @@ if (!str) goto fail;
                         JS_ThrowRangeError(ctx, "invalid firstDayOfWeek");
                         goto fail;
                     }
-                    fw_val = str;
+                    fw_canon = canonicalize_u_value(ctx, "fw", str);
+                    if (!fw_canon) {
+                        JS_FreeCString(ctx, str);
+                        goto fail;
+                    }
+                    fw_val = fw_canon;
                 }
                 for (i = 0; i < num_u_keywords; i++) {
                     if (!strcmp(u_keywords[i].key, "fw")) {
@@ -714,7 +766,9 @@ if (!str) goto fail;
                     u_keywords[num_u_keywords].value = js_strdup(ctx, fw_val);
                     num_u_keywords++;
                 }
+                js_free(ctx, out->firstDayOfWeek);
                 out->firstDayOfWeek = js_strdup(ctx, fw_val);
+                js_free(ctx, fw_canon);
             }
             JS_FreeCString(ctx, str);
         }
@@ -811,19 +865,33 @@ if (!str) goto fail;
                 JS_ThrowRangeError(ctx, "invalid numberingSystem");
                 goto fail;
             }
-            for (i = 0; i < num_u_keywords; i++) {
-                if (!strcmp(u_keywords[i].key, "nu")) {
-                    js_free(ctx, u_keywords[i].value);
-                    u_keywords[i].value = js_strdup(ctx, str);
-                    break;
-                }
+            if (!is_valid_type(str)) {
+                JS_FreeCString(ctx, str);
+                JS_ThrowRangeError(ctx, "invalid numberingSystem");
+                goto fail;
             }
-            if (i == num_u_keywords && num_u_keywords < 16) {
-                u_keywords[num_u_keywords].key = js_strdup(ctx, "nu");
-                u_keywords[num_u_keywords].value = js_strdup(ctx, str);
-                num_u_keywords++;
+            {
+                char *nu_val = canonicalize_u_value(ctx, "nu", str);
+                if (!nu_val) {
+                    JS_FreeCString(ctx, str);
+                    goto fail;
+                }
+                for (i = 0; i < num_u_keywords; i++) {
+                    if (!strcmp(u_keywords[i].key, "nu")) {
+                        js_free(ctx, u_keywords[i].value);
+                        u_keywords[i].value = js_strdup(ctx, nu_val);
+                        break;
+                    }
+                }
+                if (i == num_u_keywords && num_u_keywords < 16) {
+                    u_keywords[num_u_keywords].key = js_strdup(ctx, "nu");
+                    u_keywords[num_u_keywords].value = js_strdup(ctx, nu_val);
+                    num_u_keywords++;
+                }
+                js_free(ctx, out->numberingSystem);
+                out->numberingSystem = js_strdup(ctx, nu_val);
+                js_free(ctx, nu_val);
             }
-            out->numberingSystem = js_strdup(ctx, str);
             JS_FreeCString(ctx, str);
         }
     }
index 5100cd41b04ebbe51bfaeb61b0129ff50518fa65..657b4d9271581d60b69e79743e1a1d90e449ec90 100644 (file)
@@ -1,8 +1,3 @@
-test262/test/intl402/Locale/constructor-unicode-extension-uvalue-true.js:20: Test262Error: new Intl.Locale("en-u-0a-true").toString() must be "en-u-0a" Expected SameValue(«"en-u-true"», «"en-u-0a"») to be true
-test262/test/intl402/Locale/constructor-unicode-extension-uvalue-yes.js:20: Test262Error: new Intl.Locale("en-u-0a-yes").toString() must be "en-u-0a-yes" Expected SameValue(«"en-u-yes"», «"en-u-0a-yes"») to be true
-test262/test/intl402/Locale/prototype/calendar/canonicalize-case.js:26: Test262Error: new Intl.Locale("de", {calendar: "GREGORY"}).calendar Expected SameValue(«"GREGORY"», «"gregory"») to be true
-test262/test/intl402/Locale/prototype/collation/canonicalize-case.js:26: Test262Error: new Intl.Locale("de", {collation: "PHONEBK"}).collation Expected SameValue(«"PHONEBK"», «"phonebk"») to be true
-test262/test/intl402/Locale/prototype/firstDayOfWeek/valid-options.js:15: Test262Error: new Intl.Locale("en", { firstDayOfWeek: MON }).firstDayOfWeek returns "mon" Expected SameValue(«"MON"», «"mon"») to be true
 test262/test/intl402/Locale/prototype/getCalendars/likely-subtags-region.js:42: Test262Error: No suitable test data found in this implementation. Consider updating the candidate list
 test262/test/intl402/Locale/prototype/getCalendars/region-override.js:41: Test262Error: No suitable test data found in this implementation. Consider updating the candidate list
 test262/test/intl402/Locale/prototype/getCalendars/region-priority.js:60: Test262Error: Inconclusive: the "rg" region override and the region subtag select regions with identical calendars in this implementation. Consider updating the test data
@@ -18,7 +13,6 @@ test262/test/intl402/Locale/prototype/getWeekInfo/likely-subtags-region.js:46: T
 test262/test/intl402/Locale/prototype/getWeekInfo/region-override.js:45: Test262Error: No suitable test data found in this implementation. Consider updating the candidate list
 test262/test/intl402/Locale/prototype/getWeekInfo/region-priority.js:59: Test262Error: Inconclusive: the "rg" region override and the region subtag selected regions with identical week info in this implementation. Consider updating the test data
 test262/test/intl402/Locale/prototype/getWeekInfo/subdivision-region.js:39: Test262Error: No suitable test data found in this implementation. Consider updating the candidate list
-test262/test/intl402/Locale/prototype/numberingSystem/canonicalize-case.js:26: Test262Error: new Intl.Locale("de", {numberingSystem: "LATN"}).numberingSystem Expected SameValue(«"LATN"», «"latn"») to be true
 test262/test/language/statements/await-using/initializer-Symbol.asyncDispose-disposed-at-end-of-imported-module.js:11: SyntaxError: exported variable 'resource' does not exist
 test262/test/language/statements/await-using/initializer-Symbol.dispose-disposed-at-end-of-imported-module.js:11: SyntaxError: exported variable 'resource' does not exist
 test262/test/language/statements/using/initializer-disposed-at-end-of-imported-module.js:11: SyntaxError: exported variable 'resource' does not exist