OLD | NEW |
1 // Copyright 2013 the V8 project authors. All rights reserved. | 1 // Copyright 2013 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // ECMAScript 402 API implementation. | 5 // ECMAScript 402 API implementation. |
6 | 6 |
7 /** | 7 /** |
8 * Intl object is a single object that has some named properties, | 8 * Intl object is a single object that has some named properties, |
9 * all of which are constructors. | 9 * all of which are constructors. |
10 */ | 10 */ |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 'numberformat': UNDEFINED, | 135 'numberformat': UNDEFINED, |
136 'dateformat': UNDEFINED, | 136 'dateformat': UNDEFINED, |
137 'breakiterator': UNDEFINED | 137 'breakiterator': UNDEFINED |
138 }; | 138 }; |
139 | 139 |
140 /** | 140 /** |
141 * Caches default ICU locale. | 141 * Caches default ICU locale. |
142 */ | 142 */ |
143 var DEFAULT_ICU_LOCALE = UNDEFINED; | 143 var DEFAULT_ICU_LOCALE = UNDEFINED; |
144 | 144 |
| 145 function GetDefaultICULocaleJS() { |
| 146 if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) { |
| 147 DEFAULT_ICU_LOCALE = %GetDefaultICULocale(); |
| 148 } |
| 149 return DEFAULT_ICU_LOCALE; |
| 150 } |
| 151 |
145 /** | 152 /** |
146 * Unicode extension regular expression. | 153 * Unicode extension regular expression. |
147 */ | 154 */ |
148 var UNICODE_EXTENSION_RE = UNDEFINED; | 155 var UNICODE_EXTENSION_RE = UNDEFINED; |
149 | 156 |
150 function GetUnicodeExtensionRE() { | 157 function GetUnicodeExtensionRE() { |
151 if (IS_UNDEFINED(UNDEFINED)) { | 158 if (IS_UNDEFINED(UNDEFINED)) { |
152 UNICODE_EXTENSION_RE = new GlobalRegExp('-u(-[a-z0-9]{2,8})+', 'g'); | 159 UNICODE_EXTENSION_RE = new GlobalRegExp('-u(-[a-z0-9]{2,8})+', 'g'); |
153 } | 160 } |
154 return UNICODE_EXTENSION_RE; | 161 return UNICODE_EXTENSION_RE; |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
439 // Truncate locale if possible. | 446 // Truncate locale if possible. |
440 var pos = %_Call(StringLastIndexOf, locale, '-'); | 447 var pos = %_Call(StringLastIndexOf, locale, '-'); |
441 if (pos === -1) { | 448 if (pos === -1) { |
442 break; | 449 break; |
443 } | 450 } |
444 locale = %_Call(StringSubstring, locale, 0, pos); | 451 locale = %_Call(StringSubstring, locale, 0, pos); |
445 } while (true); | 452 } while (true); |
446 } | 453 } |
447 | 454 |
448 // Didn't find a match, return default. | 455 // Didn't find a match, return default. |
449 if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) { | 456 return {'locale': GetDefaultICULocaleJS(), 'extension': '', 'position': -1}; |
450 DEFAULT_ICU_LOCALE = %GetDefaultICULocale(); | |
451 } | |
452 | |
453 return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1}; | |
454 } | 457 } |
455 | 458 |
456 | 459 |
457 /** | 460 /** |
458 * Returns best matched supported locale and extension info using | 461 * Returns best matched supported locale and extension info using |
459 * implementation dependend algorithm. | 462 * implementation dependend algorithm. |
460 */ | 463 */ |
461 function bestFitMatcher(service, requestedLocales) { | 464 function bestFitMatcher(service, requestedLocales) { |
462 // TODO(cira): implement better best fit algorithm. | 465 // TODO(cira): implement better best fit algorithm. |
463 return lookupMatcher(service, requestedLocales); | 466 return lookupMatcher(service, requestedLocales); |
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
713 } | 716 } |
714 } | 717 } |
715 return result; | 718 return result; |
716 } | 719 } |
717 | 720 |
718 /** | 721 /** |
719 * Canonicalizes the language tag, or throws in case the tag is invalid. | 722 * Canonicalizes the language tag, or throws in case the tag is invalid. |
720 */ | 723 */ |
721 function canonicalizeLanguageTag(localeID) { | 724 function canonicalizeLanguageTag(localeID) { |
722 // null is typeof 'object' so we have to do extra check. | 725 // null is typeof 'object' so we have to do extra check. |
723 if (typeof localeID !== 'string' && typeof localeID !== 'object' || | 726 if ((!IS_STRING(localeID) && !IS_RECEIVER(localeID)) || |
724 IS_NULL(localeID)) { | 727 IS_NULL(localeID)) { |
725 throw MakeTypeError(kLanguageID); | 728 throw MakeTypeError(kLanguageID); |
726 } | 729 } |
727 | 730 |
| 731 // Optimize for the most common case; a language code alone in |
| 732 // the canonical form/lowercase (e.g. "en", "fil"). |
| 733 if (IS_STRING(localeID) && |
| 734 !IS_NULL(InternalRegExpMatch(/^[a-z]{2,3}$/, localeID))) { |
| 735 return localeID; |
| 736 } |
| 737 |
728 var localeString = GlobalString(localeID); | 738 var localeString = GlobalString(localeID); |
729 | 739 |
730 if (isValidLanguageTag(localeString) === false) { | 740 if (isValidLanguageTag(localeString) === false) { |
731 throw MakeRangeError(kInvalidLanguageTag, localeString); | 741 throw MakeRangeError(kInvalidLanguageTag, localeString); |
732 } | 742 } |
733 | 743 |
734 // This call will strip -kn but not -kn-true extensions. | |
735 // ICU bug filled - http://bugs.icu-project.org/trac/ticket/9265. | |
736 // TODO(cira): check if -u-kn-true-kc-true-kh-true still throws after | |
737 // upgrade to ICU 4.9. | |
738 var tag = %CanonicalizeLanguageTag(localeString); | 744 var tag = %CanonicalizeLanguageTag(localeString); |
739 if (tag === 'invalid-tag') { | 745 if (tag === 'invalid-tag') { |
740 throw MakeRangeError(kInvalidLanguageTag, localeString); | 746 throw MakeRangeError(kInvalidLanguageTag, localeString); |
741 } | 747 } |
742 | 748 |
743 return tag; | 749 return tag; |
744 } | 750 } |
745 | 751 |
746 | 752 |
747 /** | 753 /** |
(...skipping 1235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1983 var useOptions = (IS_UNDEFINED(defaults)) ? options : defaults; | 1989 var useOptions = (IS_UNDEFINED(defaults)) ? options : defaults; |
1984 if (IS_UNDEFINED(locales) && IS_UNDEFINED(options)) { | 1990 if (IS_UNDEFINED(locales) && IS_UNDEFINED(options)) { |
1985 if (IS_UNDEFINED(defaultObjects[service])) { | 1991 if (IS_UNDEFINED(defaultObjects[service])) { |
1986 defaultObjects[service] = new savedObjects[service](locales, useOptions); | 1992 defaultObjects[service] = new savedObjects[service](locales, useOptions); |
1987 } | 1993 } |
1988 return defaultObjects[service]; | 1994 return defaultObjects[service]; |
1989 } | 1995 } |
1990 return new savedObjects[service](locales, useOptions); | 1996 return new savedObjects[service](locales, useOptions); |
1991 } | 1997 } |
1992 | 1998 |
| 1999 function LocaleConvertCase(s, locales, isToUpper) { |
| 2000 // ECMA 402 section 13.1.2 steps 1 through 12. |
| 2001 var language; |
| 2002 // Optimize for the most common two cases. initializeLocaleList() can handle |
| 2003 // them as well, but it's rather slow accounting for over 60% of |
| 2004 // toLocale{U,L}Case() and about 40% of toLocale{U,L}Case("<locale>"). |
| 2005 if (IS_UNDEFINED(locales)) { |
| 2006 language = GetDefaultICULocaleJS(); |
| 2007 } else if (IS_STRING(locales)) { |
| 2008 language = canonicalizeLanguageTag(locales); |
| 2009 } else { |
| 2010 var locales = initializeLocaleList(locales); |
| 2011 language = locales.length > 0 ? locales[0] : GetDefaultICULocaleJS(); |
| 2012 } |
| 2013 |
| 2014 // StringSplit is slower than this. |
| 2015 var pos = %_Call(StringIndexOf, language, '-'); |
| 2016 if (pos != -1) { |
| 2017 language = %_Call(StringSubstring, language, 0, pos); |
| 2018 } |
| 2019 |
| 2020 var CUSTOM_CASE_LANGUAGES = ['az', 'el', 'lt', 'tr']; |
| 2021 var langIndex = %_Call(ArrayIndexOf, CUSTOM_CASE_LANGUAGES, language); |
| 2022 if (langIndex == -1) { |
| 2023 // language-independent case conversion. |
| 2024 return isToUpper ? %StringToUpperCaseI18N(s) : %StringToLowerCaseI18N(s); |
| 2025 } |
| 2026 return %StringLocaleConvertCase(s, isToUpper, |
| 2027 CUSTOM_CASE_LANGUAGES[langIndex]); |
| 2028 } |
| 2029 |
1993 /** | 2030 /** |
1994 * Compares this and that, and returns less than 0, 0 or greater than 0 value. | 2031 * Compares this and that, and returns less than 0, 0 or greater than 0 value. |
1995 * Overrides the built-in method. | 2032 * Overrides the built-in method. |
1996 */ | 2033 */ |
1997 OverrideFunction(GlobalString.prototype, 'localeCompare', function(that) { | 2034 OverrideFunction(GlobalString.prototype, 'localeCompare', function(that) { |
1998 if (!IS_UNDEFINED(new.target)) { | 2035 if (!IS_UNDEFINED(new.target)) { |
1999 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | 2036 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
2000 } | 2037 } |
2001 | 2038 |
2002 if (IS_NULL_OR_UNDEFINED(this)) { | 2039 if (IS_NULL_OR_UNDEFINED(this)) { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2035 var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form); | 2072 var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form); |
2036 if (normalizationForm === -1) { | 2073 if (normalizationForm === -1) { |
2037 throw MakeRangeError(kNormalizationForm, | 2074 throw MakeRangeError(kNormalizationForm, |
2038 %_Call(ArrayJoin, NORMALIZATION_FORMS, ', ')); | 2075 %_Call(ArrayJoin, NORMALIZATION_FORMS, ', ')); |
2039 } | 2076 } |
2040 | 2077 |
2041 return %StringNormalize(s, normalizationForm); | 2078 return %StringNormalize(s, normalizationForm); |
2042 } | 2079 } |
2043 ); | 2080 ); |
2044 | 2081 |
| 2082 /* |
| 2083 OverrideFunction(GlobalString.prototype, 'toLowerCase', function() { |
| 2084 if (!IS_UNDEFINED(new.target)) { |
| 2085 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
| 2086 } |
| 2087 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase"); |
| 2088 var s = TO_STRING(this); |
| 2089 return %StringToLowerCaseI18N(s); |
| 2090 } |
| 2091 ); |
| 2092 |
| 2093 OverrideFunction(GlobalString.prototype, 'toUpperCase', function() { |
| 2094 if (!IS_UNDEFINED(new.target)) { |
| 2095 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
| 2096 } |
| 2097 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase"); |
| 2098 var s = TO_STRING(this); |
| 2099 return %StringToUpperCaseI18N(s); |
| 2100 } |
| 2101 ); |
| 2102 */ |
| 2103 |
| 2104 OverrideFunction(GlobalString.prototype, 'toLocaleLowerCase', function() { |
| 2105 if (!IS_UNDEFINED(new.target)) { |
| 2106 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
| 2107 } |
| 2108 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase"); |
| 2109 return LocaleConvertCase(TO_STRING(this), arguments[0], false); |
| 2110 } |
| 2111 ); |
| 2112 |
| 2113 |
| 2114 OverrideFunction(GlobalString.prototype, 'toLocaleUpperCase', function() { |
| 2115 if (!IS_UNDEFINED(new.target)) { |
| 2116 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
| 2117 } |
| 2118 CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase"); |
| 2119 return LocaleConvertCase(TO_STRING(this), arguments[0], true); |
| 2120 } |
| 2121 ); |
| 2122 |
2045 | 2123 |
2046 /** | 2124 /** |
2047 * Formats a Number object (this) using locale and options values. | 2125 * Formats a Number object (this) using locale and options values. |
2048 * If locale or options are omitted, defaults are used. | 2126 * If locale or options are omitted, defaults are used. |
2049 */ | 2127 */ |
2050 OverrideFunction(GlobalNumber.prototype, 'toLocaleString', function() { | 2128 OverrideFunction(GlobalNumber.prototype, 'toLocaleString', function() { |
2051 if (!IS_UNDEFINED(new.target)) { | 2129 if (!IS_UNDEFINED(new.target)) { |
2052 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | 2130 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
2053 } | 2131 } |
2054 | 2132 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2130 } | 2208 } |
2131 | 2209 |
2132 var locales = arguments[0]; | 2210 var locales = arguments[0]; |
2133 var options = arguments[1]; | 2211 var options = arguments[1]; |
2134 return toLocaleDateTime( | 2212 return toLocaleDateTime( |
2135 this, locales, options, 'time', 'time', 'dateformattime'); | 2213 this, locales, options, 'time', 'time', 'dateformattime'); |
2136 } | 2214 } |
2137 ); | 2215 ); |
2138 | 2216 |
2139 }) | 2217 }) |
OLD | NEW |