Mitigate buffer overflow in ICU
[hiphop-php.git] / hphp / runtime / ext / icu / ext_icu_locale.cpp
blobcbe724ed2f91ed843070e00808208dd3d571248c
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
17 #include "hphp/runtime/ext/icu/ext_icu_locale.h"
18 #include "hphp/runtime/ext/icu/icu.h"
19 #include "hphp/runtime/ext/ext_string.h"
21 #include <unicode/ures.h>
22 #include <unicode/uloc.h>
23 #include <algorithm>
24 #include <utility>
25 #include <vector>
27 namespace HPHP { namespace Intl {
28 //////////////////////////////////////////////////////////////////////////////
29 // class Locale
31 #define ULOC_CHECK(err, ret) \
32 if (U_FAILURE(err)) { \
33 s_intl_error->setError(err); \
34 return ret; \
37 #define MAX_LOCALE_LEN 80
39 /*returns TRUE if a is an ID separator FALSE otherwise*/
40 #define isIDSeparator(a) (a == '_' || a == '-')
41 #define isKeywordSeparator(a) (a == '@' )
42 #define isEndOfTag(a) (a == '\0' )
44 #define isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I'))
46 /*returns TRUE if one of the special prefixes is here (s=string)
47 * 'x-' or 'i-' */
48 #define isIDPrefix(s) (isPrefixLetter(s[0])&&isIDSeparator(s[1]))
49 #define isKeywordPrefix(s) ( isKeywordSeparator(s[0]) )
51 /* Dot terminates it because of POSIX form where dot precedes the codepage
52 * * except for variant */
53 #define isTerminator(a) ((a==0)||(a=='.')||(a=='@'))
55 static std::vector<std::string> g_grandfathered = {
56 "art-lojban", "i-klingon", "i-lux", "i-navajo", "no-bok", "no-nyn",
57 "cel-gaulish", "en-GB-oed", "i-ami", "i-bnn", "i-default", "i-enochian",
58 "i-mingo", "i-pwn", "i-tao", "i-tay", "i-tsu", "sgn-BE-fr", "sgn-BE-nl",
59 "sgn-CH-de", "zh-cmn", "zh-cmn-Hans", "zh-cmn-Hant", "zh-gan", "zh-guoyu",
60 "zh-hakka", "zh-min", "zh-min-nan", "zh-wuu", "zh-xiang", "zh-yue"
63 /* Preferred locale codes for the first N entries of g_grandfathered
64 * Must be kept in sync with above.
66 static std::vector<std::string> g_grandfathered_preferred = {
67 "jbo", "tlh", "lb", "nv", "nb", "nn"
70 static int getGrandfatheredOffset(const String& locale) {
71 auto it = std::find(g_grandfathered.begin(),
72 g_grandfathered.end(), locale.data());
73 if (it == g_grandfathered.end()) return -1;
74 return (it - g_grandfathered.begin());
77 static String getGrandfatheredPreferred(int ofs) {
78 if ((ofs < 0) || (ofs >= g_grandfathered.size())) {
79 return null_string;
81 if (ofs < g_grandfathered_preferred.size()) {
82 return g_grandfathered_preferred[ofs];
84 return g_grandfathered[ofs];
87 enum LocaleTag {
88 LOC_SCRIPT,
89 LOC_LANG,
90 LOC_REGION,
91 LOC_VARIANT,
92 LOC_CANONICALIZE,
93 LOC_PRIVATE,
94 LOC_DISPLAY,
95 LOC_EXTLANG,
98 const StaticString
99 s_DEFAULT_LOCALE("DEFAULT_LOCALE"),
100 s_LANG_TAG("LANG_TAG"),
101 s_EXTLANG_TAG("EXTLANG_TAG"),
102 s_SCRIPT_TAG("SCRIPT_TAG"),
103 s_REGION_TAG("REGION_TAG"),
104 s_VARIANT_TAG("VARIANT_TAG"),
105 s_GRANDFATHERED_LANG_TAG("GRANDFATHERED_LANG_TAG"),
106 s_PRIVATE_TAG("PRIVATE_TAG"),
107 s_LOC_SCRIPT("script"),
108 s_LOC_LANG("language"),
109 s_LOC_REGION("region"),
110 s_LOC_VARIANT("variant"),
111 s_LOC_CANONICALIZE("canonicalize"),
112 s_LOC_PRIVATE("private"),
113 s_LOC_DISPLAY("display"),
114 s_LOC_EXTLANG("extlang"),
115 s_GRANDFATHERED("grandfathered"),
116 s_SEPARATOR("_"),
117 s_SEPARATOR_x("_x");
119 static const StaticString LocaleName(LocaleTag tag) {
120 switch (tag) {
121 case LOC_SCRIPT: return s_LOC_SCRIPT;
122 case LOC_LANG: return s_LOC_LANG;
123 case LOC_REGION: return s_LOC_REGION;
124 case LOC_VARIANT: return s_LOC_VARIANT;
125 case LOC_CANONICALIZE: return s_LOC_CANONICALIZE;
126 case LOC_PRIVATE: return s_LOC_PRIVATE;
127 case LOC_DISPLAY: return s_LOC_DISPLAY;
128 case LOC_EXTLANG: return s_LOC_EXTLANG;
130 not_reached();
133 static int singleton_pos(const String& str) {
134 auto len = str.size();
135 for (int i = 0; i < (len - 2); ++i) {
136 if (!isIDSeparator(str[i])) continue;
137 if (i == 1) return 0;
138 if (isIDSeparator(str[i+2])) return i+1;
140 return -1;
144 static Variant get_icu_value(const String &locale, LocaleTag tag,
145 bool fromParseLocale = false) {
146 String locale_name(locale);
147 if (tag != LOC_CANONICALIZE) {
148 if (getGrandfatheredOffset(locale) >= 0) {
149 if (tag == LOC_LANG) {
150 return locale;
152 return false;
154 if (fromParseLocale) {
155 auto localecstr = locale.c_str();
156 if (tag == LOC_LANG && locale.size() > 1 && isIDPrefix(localecstr)) {
157 return locale;
159 int pos = singleton_pos(locale);
160 if (pos == 0) {
161 return null_string;
162 } else if (pos > 0) {
163 locale_name = f_substr(locale, 0, pos - 1);
168 int32_t (*ulocfunc)(const char *loc, char *val, int32_t len, UErrorCode *err);
169 switch (tag) {
170 case LOC_SCRIPT: ulocfunc = uloc_getScript; break;
171 case LOC_LANG: ulocfunc = uloc_getLanguage; break;
172 case LOC_REGION: ulocfunc = uloc_getCountry; break;
173 case LOC_VARIANT: ulocfunc = uloc_getVariant; break;
174 case LOC_CANONICALIZE: ulocfunc = uloc_canonicalize; break;
175 default:
176 assert(false);
177 return false;
180 String buf(64, ReserveString);
181 do {
182 UErrorCode error = U_ZERO_ERROR;
183 int32_t len = ulocfunc(locale_name.c_str(),
184 buf.get()->mutableData(), buf.get()->capacity(),
185 &error);
186 if (error != U_BUFFER_OVERFLOW_ERROR &&
187 error != U_STRING_NOT_TERMINATED_WARNING) {
188 if (U_FAILURE(error)) {
189 s_intl_error->setError(error, "unable to get locale info");
190 return false;
192 buf.setSize(len);
193 return buf;
195 if (len <= buf.get()->capacity()) {
196 // Avoid infinite loop
197 s_intl_error->setError(U_INTERNAL_PROGRAM_ERROR,
198 "Got invalid response from ICU");
199 return false;
201 buf = String(len, ReserveString);
202 } while (true);
204 not_reached();
205 return false;
208 static Variant get_icu_display_value(const String& locale,
209 const String& disp_locale,
210 LocaleTag tag) {
211 String locname(locale);
212 if (tag != LOC_DISPLAY) {
213 int ofs = getGrandfatheredOffset(locale);
214 if (ofs >= 0) {
215 if (tag == LOC_LANG) {
216 locname = getGrandfatheredPreferred(ofs);
217 } else {
218 return false;
223 // Hack around buffer overflow in libicu. ures_getByKeyWithFallback is a
224 // silly function.
225 if (locname.size() >= 255 || disp_locale.size() >= 255) return false;
227 int32_t (*ulocfunc)(const char *loc, const char *dloc,
228 UChar *dest, int32_t destcap, UErrorCode *err);
229 switch (tag) {
230 case LOC_LANG: ulocfunc = uloc_getDisplayLanguage; break;
231 case LOC_SCRIPT: ulocfunc = uloc_getDisplayScript; break;
232 case LOC_REGION: ulocfunc = uloc_getDisplayCountry; break;
233 case LOC_VARIANT: ulocfunc = uloc_getDisplayVariant; break;
234 case LOC_DISPLAY: ulocfunc = uloc_getDisplayName; break;
235 default:
236 assert(false);
237 return false;
240 icu::UnicodeString buf;
241 auto ubuf = buf.getBuffer(64);
242 do {
243 UErrorCode error = U_ZERO_ERROR;
244 int32_t len = ulocfunc(locname.c_str(), disp_locale.c_str(),
245 ubuf, buf.getCapacity(), &error);
246 if (error != U_BUFFER_OVERFLOW_ERROR &&
247 error != U_STRING_NOT_TERMINATED_WARNING) {
248 if (U_FAILURE(error)) {
249 s_intl_error->setError(error, "locale_get_display_%s : unable to "
250 "get locale %s",
251 LocaleName(tag).c_str(),
252 LocaleName(tag).c_str());
253 return false;
255 buf.releaseBuffer(len);
257 error = U_ZERO_ERROR;
258 String out(u8(buf, error));
259 if (U_FAILURE(error)) {
260 s_intl_error->setError(error, "Unable to convert result from "
261 "locale_get_display_%s to UTF-8",
262 LocaleName(tag).c_str());
263 return false;
265 return out;
267 if (len <= buf.getCapacity()) {
268 // Avoid infinite loop
269 buf.releaseBuffer(0);
270 s_intl_error->setError(U_INTERNAL_PROGRAM_ERROR,
271 "Got invalid response from ICU");
272 return false;
275 // Grow the buffer to sufficient size
276 buf.releaseBuffer(0);
277 ubuf = buf.getBuffer(len);
278 } while (true);
280 not_reached();
281 return false;
284 static Variant HHVM_STATIC_METHOD(Locale, acceptFromHttp,
285 const String& header) {
286 UErrorCode error = U_ZERO_ERROR;
287 UEnumeration *avail = ures_openAvailableLocales(nullptr, &error);
288 ULOC_CHECK(error, false);
289 char out[MAX_LOCALE_LEN];
290 UAcceptResult result;
291 error = U_ZERO_ERROR;
292 int len = uloc_acceptLanguageFromHTTP(out, sizeof(out), &result,
293 header.c_str(), avail, &error);
294 uenum_close(avail);
295 ULOC_CHECK(error, false);
296 if (len < 0 || result == ULOC_ACCEPT_FAILED) {
297 return false;
299 return String(out, len, CopyString);
302 static Variant HHVM_STATIC_METHOD(Locale, canonicalize, const String& locale) {
303 return get_icu_value(localeOrDefault(locale), LOC_CANONICALIZE);
306 inline void element_not_string() {
307 s_intl_error->setError(U_ILLEGAL_ARGUMENT_ERROR,
308 "locale_compose: parameter array element is "
309 "not a string");
312 static bool append_key_value(String& ret,
313 const Array& subtags,
314 LocaleTag tag) {
315 auto name = LocaleName(tag);
316 if (!subtags.exists(name)) return true;
317 auto val = subtags[name];
318 if (!val.isString()) {
319 element_not_string();
320 return false;
322 ret += s_SEPARATOR + val.toString();
323 return true;
326 static bool append_multiple_key_values(String& ret,
327 const Array& subtags,
328 LocaleTag tag) {
329 auto name = LocaleName(tag);
330 if (subtags.exists(name)) {
331 // Sane version: [tag] => string, [tag] => array<tring>
332 auto val = subtags[name];
333 if (val.isString()) {
334 if (tag == LOC_PRIVATE) {
335 ret += s_SEPARATOR_x;
337 ret += s_SEPARATOR + val.toString();
338 return true;
340 if (!val.isArray()) {
341 return false;
343 bool first = true;
344 for (ArrayIter it(val.toArray()); it; ++it) {
345 auto v = it.second();
346 if (!v.isString()) {
347 element_not_string();
348 return false;
350 if (first) {
351 if (tag == LOC_PRIVATE) {
352 ret += s_SEPARATOR + "x";
354 first = false;
356 ret += s_SEPARATOR + v.toString();
358 return true;
361 // clowny version [tag$n] => string
362 // Only extlang, variant, and private
363 if (tag != LOC_EXTLANG &&
364 tag != LOC_VARIANT &&
365 tag != LOC_PRIVATE) {
366 return true;
369 int max = (tag == LOC_EXTLANG) ? 3 : 15;
370 bool first = true;
371 for (int i = 0; i < max; ++i) {
372 auto namenum = name + String(i, CopyString);
373 if (!subtags.exists(namenum)) continue;
374 auto val = subtags[namenum];
375 if (!val.isString()) {
376 element_not_string();
377 return false;
379 if (first) {
380 if (tag == LOC_PRIVATE) {
381 ret += s_SEPARATOR + "x";
383 first = false;
385 ret += s_SEPARATOR + val.toString();
387 return true;
390 static Variant HHVM_STATIC_METHOD(Locale, composeLocale, const Array& subtags) {
391 s_intl_error->clearError();
393 if (subtags.exists(s_GRANDFATHERED)) {
394 auto val = subtags[s_GRANDFATHERED];
395 if (val.isString()) {
396 return val;
400 if (!subtags.exists(s_LOC_LANG)) {
401 s_intl_error->setError(U_ILLEGAL_ARGUMENT_ERROR, "locale_compose: "
402 "parameter array does not contain 'language' tag.");
403 return false;
405 String ret(subtags[s_LOC_LANG].toString());
406 if (!append_multiple_key_values(ret, subtags, LOC_EXTLANG) ||
407 !append_key_value(ret, subtags, LOC_SCRIPT) ||
408 !append_key_value(ret, subtags, LOC_REGION) ||
409 !append_multiple_key_values(ret, subtags, LOC_VARIANT) ||
410 !append_multiple_key_values(ret, subtags, LOC_PRIVATE)) {
411 return false;
413 return ret;
416 static Array HHVM_STATIC_METHOD(Locale, getAllVariants, const String& locale) {
417 Variant val = get_icu_value(localeOrDefault(locale), LOC_VARIANT);
418 String strval = val.toString();
419 if (strval.empty()) {
420 return null_array;
422 Array ret = Array::Create();
423 const char *s = strval.c_str(), *e = s + strval.size(), *p;
424 for (p = s; p < e; ++p) {
425 if (!isIDSeparator(*p)) continue;
426 if ((p - s) <= 1) {
427 return ret;
429 ret.append(String(s, p - s, CopyString));
430 s = p + 1;
432 if ((e - s) > 1) {
433 ret.append(String(s, e - s, CopyString));
435 return ret;
438 static String HHVM_STATIC_METHOD(Locale, getDefault) {
439 return GetDefaultLocale();
442 static String HHVM_STATIC_METHOD(Locale, getDisplayLanguage,
443 const String& locale,
444 const String& in_locale) {
445 return get_icu_display_value(localeOrDefault(locale),
446 localeOrDefault(in_locale), LOC_LANG);
449 static String HHVM_STATIC_METHOD(Locale, getDisplayName,
450 const String& locale,
451 const String& in_locale) {
452 return get_icu_display_value(localeOrDefault(locale),
453 localeOrDefault(in_locale), LOC_DISPLAY);
456 static String HHVM_STATIC_METHOD(Locale, getDisplayRegion,
457 const String& locale,
458 const String& in_locale) {
459 return get_icu_display_value(localeOrDefault(locale),
460 localeOrDefault(in_locale), LOC_REGION);
463 static String HHVM_STATIC_METHOD(Locale, getDisplayScript,
464 const String& locale,
465 const String& in_locale) {
466 return get_icu_display_value(localeOrDefault(locale),
467 localeOrDefault(in_locale), LOC_SCRIPT);
470 static String HHVM_STATIC_METHOD(Locale, getDisplayVariant,
471 const String& locale,
472 const String& in_locale) {
473 return get_icu_display_value(localeOrDefault(locale),
474 localeOrDefault(in_locale), LOC_VARIANT);
477 static Array HHVM_STATIC_METHOD(Locale, getKeywords, const String& locale) {
478 UErrorCode error = U_ZERO_ERROR;
479 String locname = localeOrDefault(locale);
480 UEnumeration *e = uloc_openKeywords(locname.c_str(), &error);
481 if (!e) return null_array;
483 Array ret = Array::Create();
484 const char *key;
485 int key_len;
486 String val(128, ReserveString);
487 char *ptr = val.get()->mutableData();
488 error = U_ZERO_ERROR;
489 while ((key = uenum_next(e, &key_len, &error))) {
490 tryagain:
491 error = U_ZERO_ERROR;
492 int val_len = uloc_getKeywordValue(locname.c_str(), key,
493 ptr, val.get()->capacity(), &error);
494 if (error == U_BUFFER_OVERFLOW_ERROR) {
495 val = String(val_len + 128, ReserveString);
496 ptr = val.get()->mutableData();
497 goto tryagain;
499 if (U_FAILURE(error)) {
500 s_intl_error->setError(error, "locale_get_keywords: Error encountered "
501 "while getting the keyword value for the "
502 " keyword");
503 return null_array;
505 ret.set(String(key, key_len, CopyString), String(ptr, val_len, CopyString));
507 return ret;
510 static String HHVM_STATIC_METHOD(Locale, getPrimaryLanguage,
511 const String& locale) {
512 return get_icu_value(localeOrDefault(locale), LOC_LANG);
515 static Variant HHVM_STATIC_METHOD(Locale, getRegion, const String& locale) {
516 return get_icu_value(localeOrDefault(locale), LOC_REGION);
519 static Variant HHVM_STATIC_METHOD(Locale, getScript, const String& locale) {
520 return get_icu_value(localeOrDefault(locale), LOC_SCRIPT);
523 static String locale_suffix_strip(const String& locale) {
524 for (int i = locale.size(); i >= 0; --i) {
525 if (isIDSeparator(locale[i])) {
526 if ((i>=2) && isIDSeparator(locale[i-2])) {
527 return f_substr(locale, 0, i - 2);
528 } else {
529 return f_substr(locale, 0, i);
533 return null_string;
536 inline void normalize_for_match(String& v) {
537 for (char *ptr = v.get()->mutableData(), *end = ptr + v.size(); ptr < end;
538 ++ptr) {
539 if (*ptr == '-') {
540 *ptr = '_';
541 } else {
542 *ptr = tolower(*ptr);
545 v.get()->invalidateHash();
548 static String HHVM_STATIC_METHOD(Locale, lookup, const Array& langtag,
549 const String& locale,
550 bool canonicalize, const String& def) {
551 String locname(localeOrDefault(locale), CopyString);
552 std::vector<std::pair<String,String>> cur_arr;
553 for (ArrayIter iter(langtag); iter; ++iter) {
554 auto val = iter.second();
555 if (!val.isString()) {
556 s_intl_error->setError(U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: "
557 "locale array element is not a string");
558 return def;
560 String normalized(val.toString(), CopyString);
561 normalize_for_match(normalized);
562 if (canonicalize) {
563 normalized = get_icu_value(normalized, LOC_CANONICALIZE);
564 if (normalized.isNull()) {
565 s_intl_error->setError(U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: "
566 "unable to canonicalize lang_tag");
567 return def;
569 normalize_for_match(normalized);
571 cur_arr.push_back(std::make_pair(val.toString(),normalized));
574 if (canonicalize) {
575 locname = get_icu_value(locname, LOC_CANONICALIZE);
576 if (locname.isNull()) {
577 s_intl_error->setError(U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: "
578 "unable to canonicalize loc_range");
579 return def;
583 normalize_for_match(locname);
584 while (locname.size() > 0) {
585 for (auto &p : cur_arr) {
586 if (locname.same(p.second)) {
587 return canonicalize ? p.second : p.first;
590 locname = locale_suffix_strip(locname);
592 return def;
595 static Variant get_private_subtags(const String& locname) {
596 if (locname.empty()) return uninit_null();
597 String locale(locname);
598 int pos;
599 while ((pos = singleton_pos(locale)) >= 0) {
600 if ((locale[pos] == 'x') || (locale[pos] == 'X')) {
601 if ((pos + 2) == locale.size()) {
602 /* loc_name ends with '-x-' */
603 return uninit_null();
605 return f_substr(locale, pos);
607 if ((pos + 1) >= locale.size()) {
608 return uninit_null();
610 locale = f_substr(locale, pos + 1);
612 return uninit_null();
615 static void add_array_entry(Array& ret,
616 const String& locname,
617 LocaleTag tag) {
618 Variant val;
619 if (tag == LOC_PRIVATE) {
620 val = get_private_subtags(locname);
621 } else {
622 val = get_icu_value(locname, tag, true);
624 if (val.isNull()) return;
625 String strval = val.toString();
626 if (strval.empty()) {
627 return;
630 auto name = LocaleName(tag);
631 if ((tag != LOC_PRIVATE) && (tag != LOC_VARIANT)) {
632 ret.set(name, strval);
633 return;
636 const char *s = strval.c_str(), *e = s + strval.size(), *p;
637 int cnt;
638 for (cnt = 0, p = s; p < e; ++p) {
639 if (!isIDSeparator(*p)) continue;
640 if ((p - s) > 1) {
641 ret.set(name + String(cnt++), String(s, p - s, CopyString));
643 s = p + 1;
645 if ((e - s) > 1) {
646 ret.set(name + String(cnt++), String(s, e - s, CopyString));
650 static Array HHVM_STATIC_METHOD(Locale, parseLocale, const String& locale) {
651 String locname = localeOrDefault(locale);
652 Array ret = Array::Create();
653 if (std::find(g_grandfathered.begin(),
654 g_grandfathered.end(), locale.data()) !=
655 g_grandfathered.end()) {
656 ret.set(s_GRANDFATHERED, locname);
657 return ret;
659 add_array_entry(ret, locname, LOC_LANG);
660 add_array_entry(ret, locname, LOC_SCRIPT);
661 add_array_entry(ret, locname, LOC_REGION);
662 add_array_entry(ret, locname, LOC_VARIANT);
663 add_array_entry(ret, locname, LOC_PRIVATE);
664 return ret;
667 static bool HHVM_STATIC_METHOD(Locale, setDefault, const String& locale) {
668 return SetDefaultLocale(locale);
671 //////////////////////////////////////////////////////////////////////////////
673 const StaticString s_Locale("Locale");
675 void IntlExtension::initLocale() {
676 HHVM_STATIC_ME(Locale, acceptFromHttp);
677 HHVM_STATIC_ME(Locale, canonicalize);
678 HHVM_STATIC_ME(Locale, composeLocale);
679 HHVM_STATIC_ME(Locale, getAllVariants);
680 HHVM_STATIC_ME(Locale, getDefault);
681 HHVM_STATIC_ME(Locale, getDisplayLanguage);
682 HHVM_STATIC_ME(Locale, getDisplayName);
683 HHVM_STATIC_ME(Locale, getDisplayRegion);
684 HHVM_STATIC_ME(Locale, getDisplayScript);
685 HHVM_STATIC_ME(Locale, getDisplayVariant);
686 HHVM_STATIC_ME(Locale, getKeywords);
687 HHVM_STATIC_ME(Locale, getPrimaryLanguage);
688 HHVM_STATIC_ME(Locale, getRegion);
689 HHVM_STATIC_ME(Locale, getScript);
690 HHVM_STATIC_ME(Locale, lookup);
691 HHVM_STATIC_ME(Locale, parseLocale);
692 HHVM_STATIC_ME(Locale, setDefault);
694 #define ULOC_CONST(nm,val) Native::registerClassConstant<KindOfStaticString>\
695 (s_Locale.get(), s_##nm.get(), s_##val.get())
697 Native::registerClassConstant<KindOfNull>(s_Locale.get(),
698 s_DEFAULT_LOCALE.get());
699 ULOC_CONST(LANG_TAG, LOC_LANG);
700 ULOC_CONST(EXTLANG_TAG, LOC_EXTLANG);
701 ULOC_CONST(SCRIPT_TAG, LOC_SCRIPT);
702 ULOC_CONST(REGION_TAG, LOC_REGION);
703 ULOC_CONST(VARIANT_TAG, LOC_VARIANT);
704 ULOC_CONST(GRANDFATHERED_LANG_TAG, GRANDFATHERED);
705 ULOC_CONST(PRIVATE_TAG, LOC_PRIVATE);
707 #undef ULOC_CONST
709 #define ULOC_LOCALE_CONST(cns) \
710 Native::registerConstant<KindOfInt64>\
711 (makeStaticString("ULOC_" #cns), ULOC_##cns); \
712 Native::registerClassConstant<KindOfInt64>\
713 (s_Locale.get(), makeStaticString(#cns), ULOC_##cns);
715 ULOC_LOCALE_CONST(ACTUAL_LOCALE);
716 ULOC_LOCALE_CONST(VALID_LOCALE);
717 #undef ULOC_LOCALE_CONST
719 loadSystemlib("icu_locale");
722 //////////////////////////////////////////////////////////////////////////////
723 }} // namespace HPHP::Intl