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
3 // found in the LICENSE file.
4 // limitations under the License.
5 
6 #include "src/i18n.h"
7 
8 #include <memory>
9 
10 #include "src/api.h"
11 #include "src/factory.h"
12 #include "src/isolate.h"
13 #include "unicode/brkiter.h"
14 #include "unicode/calendar.h"
15 #include "unicode/coll.h"
16 #include "unicode/curramt.h"
17 #include "unicode/dcfmtsym.h"
18 #include "unicode/decimfmt.h"
19 #include "unicode/dtfmtsym.h"
20 #include "unicode/dtptngen.h"
21 #include "unicode/gregocal.h"
22 #include "unicode/locid.h"
23 #include "unicode/numfmt.h"
24 #include "unicode/numsys.h"
25 #include "unicode/rbbi.h"
26 #include "unicode/smpdtfmt.h"
27 #include "unicode/timezone.h"
28 #include "unicode/uchar.h"
29 #include "unicode/ucol.h"
30 #include "unicode/ucurr.h"
31 #include "unicode/unum.h"
32 #include "unicode/uversion.h"
33 
34 namespace v8 {
35 namespace internal {
36 
37 namespace {
38 
ExtractStringSetting(Isolate * isolate,Handle<JSObject> options,const char * key,icu::UnicodeString * setting)39 bool ExtractStringSetting(Isolate* isolate,
40                           Handle<JSObject> options,
41                           const char* key,
42                           icu::UnicodeString* setting) {
43   Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
44   Handle<Object> object =
45       JSReceiver::GetProperty(options, str).ToHandleChecked();
46   if (object->IsString()) {
47     v8::String::Utf8Value utf8_string(
48         v8::Utils::ToLocal(Handle<String>::cast(object)));
49     *setting = icu::UnicodeString::fromUTF8(*utf8_string);
50     return true;
51   }
52   return false;
53 }
54 
55 
ExtractIntegerSetting(Isolate * isolate,Handle<JSObject> options,const char * key,int32_t * value)56 bool ExtractIntegerSetting(Isolate* isolate,
57                            Handle<JSObject> options,
58                            const char* key,
59                            int32_t* value) {
60   Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
61   Handle<Object> object =
62       JSReceiver::GetProperty(options, str).ToHandleChecked();
63   if (object->IsNumber()) {
64     object->ToInt32(value);
65     return true;
66   }
67   return false;
68 }
69 
70 
ExtractBooleanSetting(Isolate * isolate,Handle<JSObject> options,const char * key,bool * value)71 bool ExtractBooleanSetting(Isolate* isolate,
72                            Handle<JSObject> options,
73                            const char* key,
74                            bool* value) {
75   Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
76   Handle<Object> object =
77       JSReceiver::GetProperty(options, str).ToHandleChecked();
78   if (object->IsBoolean()) {
79     *value = object->BooleanValue();
80     return true;
81   }
82   return false;
83 }
84 
85 
CreateICUDateFormat(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)86 icu::SimpleDateFormat* CreateICUDateFormat(
87     Isolate* isolate,
88     const icu::Locale& icu_locale,
89     Handle<JSObject> options) {
90   // Create time zone as specified by the user. We have to re-create time zone
91   // since calendar takes ownership.
92   icu::TimeZone* tz = NULL;
93   icu::UnicodeString timezone;
94   if (ExtractStringSetting(isolate, options, "timeZone", &timezone)) {
95     tz = icu::TimeZone::createTimeZone(timezone);
96   } else {
97     tz = icu::TimeZone::createDefault();
98   }
99 
100   // Create a calendar using locale, and apply time zone to it.
101   UErrorCode status = U_ZERO_ERROR;
102   icu::Calendar* calendar =
103       icu::Calendar::createInstance(tz, icu_locale, status);
104 
105   if (calendar->getDynamicClassID() ==
106       icu::GregorianCalendar::getStaticClassID()) {
107     icu::GregorianCalendar* gc = (icu::GregorianCalendar*)calendar;
108     UErrorCode status = U_ZERO_ERROR;
109     // The beginning of ECMAScript time, namely -(2**53)
110     const double start_of_time = -9007199254740992;
111     gc->setGregorianChange(start_of_time, status);
112     DCHECK(U_SUCCESS(status));
113   }
114 
115   // Make formatter from skeleton. Calendar and numbering system are added
116   // to the locale as Unicode extension (if they were specified at all).
117   icu::SimpleDateFormat* date_format = NULL;
118   icu::UnicodeString skeleton;
119   if (ExtractStringSetting(isolate, options, "skeleton", &skeleton)) {
120     std::unique_ptr<icu::DateTimePatternGenerator> generator(
121         icu::DateTimePatternGenerator::createInstance(icu_locale, status));
122     icu::UnicodeString pattern;
123     if (U_SUCCESS(status))
124       pattern = generator->getBestPattern(skeleton, status);
125 
126     date_format = new icu::SimpleDateFormat(pattern, icu_locale, status);
127     if (U_SUCCESS(status)) {
128       date_format->adoptCalendar(calendar);
129     }
130   }
131 
132   if (U_FAILURE(status)) {
133     delete calendar;
134     delete date_format;
135     date_format = nullptr;
136   }
137 
138   return date_format;
139 }
140 
141 
SetResolvedDateSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::SimpleDateFormat * date_format,Handle<JSObject> resolved)142 void SetResolvedDateSettings(Isolate* isolate,
143                              const icu::Locale& icu_locale,
144                              icu::SimpleDateFormat* date_format,
145                              Handle<JSObject> resolved) {
146   Factory* factory = isolate->factory();
147   UErrorCode status = U_ZERO_ERROR;
148   icu::UnicodeString pattern;
149   date_format->toPattern(pattern);
150   JSObject::SetProperty(
151       resolved, factory->intl_pattern_symbol(),
152       factory->NewStringFromTwoByte(
153                    Vector<const uint16_t>(
154                        reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
155                        pattern.length())).ToHandleChecked(),
156       SLOPPY).Assert();
157 
158   // Set time zone and calendar.
159   const icu::Calendar* calendar = date_format->getCalendar();
160   // getType() returns legacy calendar type name instead of LDML/BCP47 calendar
161   // key values. i18n.js maps them to BCP47 values for key "ca".
162   // TODO(jshin): Consider doing it here, instead.
163   const char* calendar_name = calendar->getType();
164   JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("calendar"),
165                         factory->NewStringFromAsciiChecked(calendar_name),
166                         SLOPPY).Assert();
167 
168   const icu::TimeZone& tz = calendar->getTimeZone();
169   icu::UnicodeString time_zone;
170   tz.getID(time_zone);
171 
172   icu::UnicodeString canonical_time_zone;
173   icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
174   if (U_SUCCESS(status)) {
175     if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
176       JSObject::SetProperty(
177           resolved, factory->NewStringFromStaticChars("timeZone"),
178           factory->NewStringFromStaticChars("UTC"), SLOPPY).Assert();
179     } else {
180       JSObject::SetProperty(
181           resolved, factory->NewStringFromStaticChars("timeZone"),
182           factory->NewStringFromTwoByte(
183                        Vector<const uint16_t>(
184                            reinterpret_cast<const uint16_t*>(
185                                canonical_time_zone.getBuffer()),
186                            canonical_time_zone.length())).ToHandleChecked(),
187           SLOPPY).Assert();
188     }
189   }
190 
191   // Ugly hack. ICU doesn't expose numbering system in any way, so we have
192   // to assume that for given locale NumberingSystem constructor produces the
193   // same digits as NumberFormat/Calendar would.
194   status = U_ZERO_ERROR;
195   icu::NumberingSystem* numbering_system =
196       icu::NumberingSystem::createInstance(icu_locale, status);
197   if (U_SUCCESS(status)) {
198     const char* ns = numbering_system->getName();
199     JSObject::SetProperty(
200         resolved, factory->NewStringFromStaticChars("numberingSystem"),
201         factory->NewStringFromAsciiChecked(ns), SLOPPY).Assert();
202   } else {
203     JSObject::SetProperty(resolved,
204                           factory->NewStringFromStaticChars("numberingSystem"),
205                           factory->undefined_value(), SLOPPY).Assert();
206   }
207   delete numbering_system;
208 
209   // Set the locale
210   char result[ULOC_FULLNAME_CAPACITY];
211   status = U_ZERO_ERROR;
212   uloc_toLanguageTag(
213       icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
214   if (U_SUCCESS(status)) {
215     JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
216                           factory->NewStringFromAsciiChecked(result),
217                           SLOPPY).Assert();
218   } else {
219     // This would never happen, since we got the locale from ICU.
220     JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
221                           factory->NewStringFromStaticChars("und"),
222                           SLOPPY).Assert();
223   }
224 }
225 
226 
227 template<int internal_fields, EternalHandles::SingletonHandle field>
GetEternal(Isolate * isolate)228 Handle<ObjectTemplateInfo> GetEternal(Isolate* isolate) {
229   if (isolate->eternal_handles()->Exists(field)) {
230     return Handle<ObjectTemplateInfo>::cast(
231         isolate->eternal_handles()->GetSingleton(field));
232   }
233   v8::Local<v8::ObjectTemplate> raw_template =
234       v8::ObjectTemplate::New(reinterpret_cast<v8::Isolate*>(isolate));
235   raw_template->SetInternalFieldCount(internal_fields);
236   return Handle<ObjectTemplateInfo>::cast(
237       isolate->eternal_handles()->CreateSingleton(
238         isolate,
239         *v8::Utils::OpenHandle(*raw_template),
240         field));
241 }
242 
243 
CreateICUNumberFormat(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)244 icu::DecimalFormat* CreateICUNumberFormat(
245     Isolate* isolate,
246     const icu::Locale& icu_locale,
247     Handle<JSObject> options) {
248   // Make formatter from options. Numbering system is added
249   // to the locale as Unicode extension (if it was specified at all).
250   UErrorCode status = U_ZERO_ERROR;
251   icu::DecimalFormat* number_format = NULL;
252   icu::UnicodeString style;
253   icu::UnicodeString currency;
254   if (ExtractStringSetting(isolate, options, "style", &style)) {
255     if (style == UNICODE_STRING_SIMPLE("currency")) {
256       icu::UnicodeString display;
257       ExtractStringSetting(isolate, options, "currency", &currency);
258       ExtractStringSetting(isolate, options, "currencyDisplay", &display);
259 
260 #if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
261       icu::NumberFormat::EStyles format_style;
262       if (display == UNICODE_STRING_SIMPLE("code")) {
263         format_style = icu::NumberFormat::kIsoCurrencyStyle;
264       } else if (display == UNICODE_STRING_SIMPLE("name")) {
265         format_style = icu::NumberFormat::kPluralCurrencyStyle;
266       } else {
267         format_style = icu::NumberFormat::kCurrencyStyle;
268       }
269 #else  // ICU version is 4.8 or above (we ignore versions below 4.0).
270       UNumberFormatStyle format_style;
271       if (display == UNICODE_STRING_SIMPLE("code")) {
272         format_style = UNUM_CURRENCY_ISO;
273       } else if (display == UNICODE_STRING_SIMPLE("name")) {
274         format_style = UNUM_CURRENCY_PLURAL;
275       } else {
276         format_style = UNUM_CURRENCY;
277       }
278 #endif
279 
280       number_format = static_cast<icu::DecimalFormat*>(
281           icu::NumberFormat::createInstance(icu_locale, format_style, status));
282 
283       if (U_FAILURE(status)) {
284         delete number_format;
285         return NULL;
286       }
287 
288       UErrorCode status_digits = U_ZERO_ERROR;
289       uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
290         currency.getTerminatedBuffer(), &status_digits);
291       if (U_SUCCESS(status_digits)) {
292         number_format->setMinimumFractionDigits(fraction_digits);
293         number_format->setMaximumFractionDigits(fraction_digits);
294       } else {
295         // Set min & max to default values (previously in i18n.js)
296         number_format->setMinimumFractionDigits(0);
297         number_format->setMaximumFractionDigits(3);
298       }
299     } else if (style == UNICODE_STRING_SIMPLE("percent")) {
300       number_format = static_cast<icu::DecimalFormat*>(
301           icu::NumberFormat::createPercentInstance(icu_locale, status));
302       if (U_FAILURE(status)) {
303         delete number_format;
304         return NULL;
305       }
306       // Make sure 1.1% doesn't go into 2%.
307       number_format->setMinimumFractionDigits(1);
308     } else {
309       // Make a decimal instance by default.
310       number_format = static_cast<icu::DecimalFormat*>(
311           icu::NumberFormat::createInstance(icu_locale, status));
312     }
313   }
314 
315   if (U_FAILURE(status)) {
316     delete number_format;
317     return NULL;
318   }
319 
320   // Set all options.
321   if (!currency.isEmpty()) {
322     number_format->setCurrency(currency.getBuffer(), status);
323   }
324 
325   int32_t digits;
326   if (ExtractIntegerSetting(
327           isolate, options, "minimumIntegerDigits", &digits)) {
328     number_format->setMinimumIntegerDigits(digits);
329   }
330 
331   if (ExtractIntegerSetting(
332           isolate, options, "minimumFractionDigits", &digits)) {
333     number_format->setMinimumFractionDigits(digits);
334   }
335 
336   if (ExtractIntegerSetting(
337           isolate, options, "maximumFractionDigits", &digits)) {
338     number_format->setMaximumFractionDigits(digits);
339   }
340 
341   bool significant_digits_used = false;
342   if (ExtractIntegerSetting(
343           isolate, options, "minimumSignificantDigits", &digits)) {
344     number_format->setMinimumSignificantDigits(digits);
345     significant_digits_used = true;
346   }
347 
348   if (ExtractIntegerSetting(
349           isolate, options, "maximumSignificantDigits", &digits)) {
350     number_format->setMaximumSignificantDigits(digits);
351     significant_digits_used = true;
352   }
353 
354   number_format->setSignificantDigitsUsed(significant_digits_used);
355 
356   bool grouping;
357   if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) {
358     number_format->setGroupingUsed(grouping);
359   }
360 
361   // Set rounding mode.
362   number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
363 
364   return number_format;
365 }
366 
367 
SetResolvedNumberSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::DecimalFormat * number_format,Handle<JSObject> resolved)368 void SetResolvedNumberSettings(Isolate* isolate,
369                                const icu::Locale& icu_locale,
370                                icu::DecimalFormat* number_format,
371                                Handle<JSObject> resolved) {
372   Factory* factory = isolate->factory();
373   icu::UnicodeString pattern;
374   number_format->toPattern(pattern);
375   JSObject::SetProperty(
376       resolved, factory->intl_pattern_symbol(),
377       factory->NewStringFromTwoByte(
378                    Vector<const uint16_t>(
379                        reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
380                        pattern.length())).ToHandleChecked(),
381       SLOPPY).Assert();
382 
383   // Set resolved currency code in options.currency if not empty.
384   icu::UnicodeString currency(number_format->getCurrency());
385   if (!currency.isEmpty()) {
386     JSObject::SetProperty(
387         resolved, factory->NewStringFromStaticChars("currency"),
388         factory->NewStringFromTwoByte(Vector<const uint16_t>(
389                                           reinterpret_cast<const uint16_t*>(
390                                               currency.getBuffer()),
391                                           currency.length())).ToHandleChecked(),
392         SLOPPY).Assert();
393   }
394 
395   // Ugly hack. ICU doesn't expose numbering system in any way, so we have
396   // to assume that for given locale NumberingSystem constructor produces the
397   // same digits as NumberFormat/Calendar would.
398   UErrorCode status = U_ZERO_ERROR;
399   icu::NumberingSystem* numbering_system =
400       icu::NumberingSystem::createInstance(icu_locale, status);
401   if (U_SUCCESS(status)) {
402     const char* ns = numbering_system->getName();
403     JSObject::SetProperty(
404         resolved, factory->NewStringFromStaticChars("numberingSystem"),
405         factory->NewStringFromAsciiChecked(ns), SLOPPY).Assert();
406   } else {
407     JSObject::SetProperty(resolved,
408                           factory->NewStringFromStaticChars("numberingSystem"),
409                           factory->undefined_value(), SLOPPY).Assert();
410   }
411   delete numbering_system;
412 
413   JSObject::SetProperty(
414       resolved, factory->NewStringFromStaticChars("useGrouping"),
415       factory->ToBoolean(number_format->isGroupingUsed()), SLOPPY).Assert();
416 
417   JSObject::SetProperty(
418       resolved, factory->NewStringFromStaticChars("minimumIntegerDigits"),
419       factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()),
420       SLOPPY).Assert();
421 
422   JSObject::SetProperty(
423       resolved, factory->NewStringFromStaticChars("minimumFractionDigits"),
424       factory->NewNumberFromInt(number_format->getMinimumFractionDigits()),
425       SLOPPY).Assert();
426 
427   JSObject::SetProperty(
428       resolved, factory->NewStringFromStaticChars("maximumFractionDigits"),
429       factory->NewNumberFromInt(number_format->getMaximumFractionDigits()),
430       SLOPPY).Assert();
431 
432   Handle<String> key =
433       factory->NewStringFromStaticChars("minimumSignificantDigits");
434   Maybe<bool> maybe = JSReceiver::HasOwnProperty(resolved, key);
435   CHECK(maybe.IsJust());
436   if (maybe.FromJust()) {
437     JSObject::SetProperty(
438         resolved, factory->NewStringFromStaticChars("minimumSignificantDigits"),
439         factory->NewNumberFromInt(number_format->getMinimumSignificantDigits()),
440         SLOPPY).Assert();
441   }
442 
443   key = factory->NewStringFromStaticChars("maximumSignificantDigits");
444   maybe = JSReceiver::HasOwnProperty(resolved, key);
445   CHECK(maybe.IsJust());
446   if (maybe.FromJust()) {
447     JSObject::SetProperty(
448         resolved, factory->NewStringFromStaticChars("maximumSignificantDigits"),
449         factory->NewNumberFromInt(number_format->getMaximumSignificantDigits()),
450         SLOPPY).Assert();
451   }
452 
453   // Set the locale
454   char result[ULOC_FULLNAME_CAPACITY];
455   status = U_ZERO_ERROR;
456   uloc_toLanguageTag(
457       icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
458   if (U_SUCCESS(status)) {
459     JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
460                           factory->NewStringFromAsciiChecked(result),
461                           SLOPPY).Assert();
462   } else {
463     // This would never happen, since we got the locale from ICU.
464     JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
465                           factory->NewStringFromStaticChars("und"),
466                           SLOPPY).Assert();
467   }
468 }
469 
470 
CreateICUCollator(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)471 icu::Collator* CreateICUCollator(
472     Isolate* isolate,
473     const icu::Locale& icu_locale,
474     Handle<JSObject> options) {
475   // Make collator from options.
476   icu::Collator* collator = NULL;
477   UErrorCode status = U_ZERO_ERROR;
478   collator = icu::Collator::createInstance(icu_locale, status);
479 
480   if (U_FAILURE(status)) {
481     delete collator;
482     return NULL;
483   }
484 
485   // Set flags first, and then override them with sensitivity if necessary.
486   bool numeric;
487   if (ExtractBooleanSetting(isolate, options, "numeric", &numeric)) {
488     collator->setAttribute(
489         UCOL_NUMERIC_COLLATION, numeric ? UCOL_ON : UCOL_OFF, status);
490   }
491 
492   // Normalization is always on, by the spec. We are free to optimize
493   // if the strings are already normalized (but we don't have a way to tell
494   // that right now).
495   collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
496 
497   icu::UnicodeString case_first;
498   if (ExtractStringSetting(isolate, options, "caseFirst", &case_first)) {
499     if (case_first == UNICODE_STRING_SIMPLE("upper")) {
500       collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status);
501     } else if (case_first == UNICODE_STRING_SIMPLE("lower")) {
502       collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status);
503     } else {
504       // Default (false/off).
505       collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
506     }
507   }
508 
509   icu::UnicodeString sensitivity;
510   if (ExtractStringSetting(isolate, options, "sensitivity", &sensitivity)) {
511     if (sensitivity == UNICODE_STRING_SIMPLE("base")) {
512       collator->setStrength(icu::Collator::PRIMARY);
513     } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) {
514       collator->setStrength(icu::Collator::SECONDARY);
515     } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) {
516       collator->setStrength(icu::Collator::PRIMARY);
517       collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
518     } else {
519       // variant (default)
520       collator->setStrength(icu::Collator::TERTIARY);
521     }
522   }
523 
524   bool ignore;
525   if (ExtractBooleanSetting(isolate, options, "ignorePunctuation", &ignore)) {
526     if (ignore) {
527       collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
528     }
529   }
530 
531   return collator;
532 }
533 
534 
SetResolvedCollatorSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::Collator * collator,Handle<JSObject> resolved)535 void SetResolvedCollatorSettings(Isolate* isolate,
536                                  const icu::Locale& icu_locale,
537                                  icu::Collator* collator,
538                                  Handle<JSObject> resolved) {
539   Factory* factory = isolate->factory();
540   UErrorCode status = U_ZERO_ERROR;
541 
542   JSObject::SetProperty(
543       resolved, factory->NewStringFromStaticChars("numeric"),
544       factory->ToBoolean(
545           collator->getAttribute(UCOL_NUMERIC_COLLATION, status) == UCOL_ON),
546       SLOPPY).Assert();
547 
548   switch (collator->getAttribute(UCOL_CASE_FIRST, status)) {
549     case UCOL_LOWER_FIRST:
550       JSObject::SetProperty(
551           resolved, factory->NewStringFromStaticChars("caseFirst"),
552           factory->NewStringFromStaticChars("lower"), SLOPPY).Assert();
553       break;
554     case UCOL_UPPER_FIRST:
555       JSObject::SetProperty(
556           resolved, factory->NewStringFromStaticChars("caseFirst"),
557           factory->NewStringFromStaticChars("upper"), SLOPPY).Assert();
558       break;
559     default:
560       JSObject::SetProperty(
561           resolved, factory->NewStringFromStaticChars("caseFirst"),
562           factory->NewStringFromStaticChars("false"), SLOPPY).Assert();
563   }
564 
565   switch (collator->getAttribute(UCOL_STRENGTH, status)) {
566     case UCOL_PRIMARY: {
567       JSObject::SetProperty(
568           resolved, factory->NewStringFromStaticChars("strength"),
569           factory->NewStringFromStaticChars("primary"), SLOPPY).Assert();
570 
571       // case level: true + s1 -> case, s1 -> base.
572       if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) {
573         JSObject::SetProperty(
574             resolved, factory->NewStringFromStaticChars("sensitivity"),
575             factory->NewStringFromStaticChars("case"), SLOPPY).Assert();
576       } else {
577         JSObject::SetProperty(
578             resolved, factory->NewStringFromStaticChars("sensitivity"),
579             factory->NewStringFromStaticChars("base"), SLOPPY).Assert();
580       }
581       break;
582     }
583     case UCOL_SECONDARY:
584       JSObject::SetProperty(
585           resolved, factory->NewStringFromStaticChars("strength"),
586           factory->NewStringFromStaticChars("secondary"), SLOPPY).Assert();
587       JSObject::SetProperty(
588           resolved, factory->NewStringFromStaticChars("sensitivity"),
589           factory->NewStringFromStaticChars("accent"), SLOPPY).Assert();
590       break;
591     case UCOL_TERTIARY:
592       JSObject::SetProperty(
593           resolved, factory->NewStringFromStaticChars("strength"),
594           factory->NewStringFromStaticChars("tertiary"), SLOPPY).Assert();
595       JSObject::SetProperty(
596           resolved, factory->NewStringFromStaticChars("sensitivity"),
597           factory->NewStringFromStaticChars("variant"), SLOPPY).Assert();
598       break;
599     case UCOL_QUATERNARY:
600       // We shouldn't get quaternary and identical from ICU, but if we do
601       // put them into variant.
602       JSObject::SetProperty(
603           resolved, factory->NewStringFromStaticChars("strength"),
604           factory->NewStringFromStaticChars("quaternary"), SLOPPY).Assert();
605       JSObject::SetProperty(
606           resolved, factory->NewStringFromStaticChars("sensitivity"),
607           factory->NewStringFromStaticChars("variant"), SLOPPY).Assert();
608       break;
609     default:
610       JSObject::SetProperty(
611           resolved, factory->NewStringFromStaticChars("strength"),
612           factory->NewStringFromStaticChars("identical"), SLOPPY).Assert();
613       JSObject::SetProperty(
614           resolved, factory->NewStringFromStaticChars("sensitivity"),
615           factory->NewStringFromStaticChars("variant"), SLOPPY).Assert();
616   }
617 
618   JSObject::SetProperty(
619       resolved, factory->NewStringFromStaticChars("ignorePunctuation"),
620       factory->ToBoolean(collator->getAttribute(UCOL_ALTERNATE_HANDLING,
621                                                 status) == UCOL_SHIFTED),
622       SLOPPY).Assert();
623 
624   // Set the locale
625   char result[ULOC_FULLNAME_CAPACITY];
626   status = U_ZERO_ERROR;
627   uloc_toLanguageTag(
628       icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
629   if (U_SUCCESS(status)) {
630     JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
631                           factory->NewStringFromAsciiChecked(result),
632                           SLOPPY).Assert();
633   } else {
634     // This would never happen, since we got the locale from ICU.
635     JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
636                           factory->NewStringFromStaticChars("und"),
637                           SLOPPY).Assert();
638   }
639 }
640 
641 
CreateICUBreakIterator(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)642 icu::BreakIterator* CreateICUBreakIterator(
643     Isolate* isolate,
644     const icu::Locale& icu_locale,
645     Handle<JSObject> options) {
646   UErrorCode status = U_ZERO_ERROR;
647   icu::BreakIterator* break_iterator = NULL;
648   icu::UnicodeString type;
649   if (!ExtractStringSetting(isolate, options, "type", &type)) return NULL;
650 
651   if (type == UNICODE_STRING_SIMPLE("character")) {
652     break_iterator =
653       icu::BreakIterator::createCharacterInstance(icu_locale, status);
654   } else if (type == UNICODE_STRING_SIMPLE("sentence")) {
655     break_iterator =
656       icu::BreakIterator::createSentenceInstance(icu_locale, status);
657   } else if (type == UNICODE_STRING_SIMPLE("line")) {
658     break_iterator =
659       icu::BreakIterator::createLineInstance(icu_locale, status);
660   } else {
661     // Defualt is word iterator.
662     break_iterator =
663       icu::BreakIterator::createWordInstance(icu_locale, status);
664   }
665 
666   if (U_FAILURE(status)) {
667     delete break_iterator;
668     return NULL;
669   }
670 
671   isolate->CountUsage(v8::Isolate::UseCounterFeature::kBreakIterator);
672 
673   return break_iterator;
674 }
675 
676 
SetResolvedBreakIteratorSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::BreakIterator * break_iterator,Handle<JSObject> resolved)677 void SetResolvedBreakIteratorSettings(Isolate* isolate,
678                                       const icu::Locale& icu_locale,
679                                       icu::BreakIterator* break_iterator,
680                                       Handle<JSObject> resolved) {
681   Factory* factory = isolate->factory();
682   UErrorCode status = U_ZERO_ERROR;
683 
684   // Set the locale
685   char result[ULOC_FULLNAME_CAPACITY];
686   status = U_ZERO_ERROR;
687   uloc_toLanguageTag(
688       icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
689   if (U_SUCCESS(status)) {
690     JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
691                           factory->NewStringFromAsciiChecked(result),
692                           SLOPPY).Assert();
693   } else {
694     // This would never happen, since we got the locale from ICU.
695     JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
696                           factory->NewStringFromStaticChars("und"),
697                           SLOPPY).Assert();
698   }
699 }
700 
701 }  // namespace
702 
703 
704 // static
GetTemplate(Isolate * isolate)705 Handle<ObjectTemplateInfo> I18N::GetTemplate(Isolate* isolate) {
706   return GetEternal<1, i::EternalHandles::I18N_TEMPLATE_ONE>(isolate);
707 }
708 
709 
710 // static
GetTemplate2(Isolate * isolate)711 Handle<ObjectTemplateInfo> I18N::GetTemplate2(Isolate* isolate) {
712   return GetEternal<2, i::EternalHandles::I18N_TEMPLATE_TWO>(isolate);
713 }
714 
715 
716 // static
InitializeDateTimeFormat(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)717 icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat(
718     Isolate* isolate,
719     Handle<String> locale,
720     Handle<JSObject> options,
721     Handle<JSObject> resolved) {
722   // Convert BCP47 into ICU locale format.
723   UErrorCode status = U_ZERO_ERROR;
724   icu::Locale icu_locale;
725   char icu_result[ULOC_FULLNAME_CAPACITY];
726   int icu_length = 0;
727   v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
728   if (bcp47_locale.length() != 0) {
729     uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
730                         &icu_length, &status);
731     if (U_FAILURE(status) || icu_length == 0) {
732       return NULL;
733     }
734     icu_locale = icu::Locale(icu_result);
735   }
736 
737   icu::SimpleDateFormat* date_format = CreateICUDateFormat(
738       isolate, icu_locale, options);
739   if (!date_format) {
740     // Remove extensions and try again.
741     icu::Locale no_extension_locale(icu_locale.getBaseName());
742     date_format = CreateICUDateFormat(isolate, no_extension_locale, options);
743 
744     if (!date_format) {
745       FATAL("Failed to create ICU date format, are ICU data files missing?");
746     }
747 
748     // Set resolved settings (pattern, numbering system, calendar).
749     SetResolvedDateSettings(
750         isolate, no_extension_locale, date_format, resolved);
751   } else {
752     SetResolvedDateSettings(isolate, icu_locale, date_format, resolved);
753   }
754 
755   return date_format;
756 }
757 
758 
UnpackDateFormat(Isolate * isolate,Handle<JSObject> obj)759 icu::SimpleDateFormat* DateFormat::UnpackDateFormat(
760     Isolate* isolate,
761     Handle<JSObject> obj) {
762   Handle<String> key =
763       isolate->factory()->NewStringFromStaticChars("dateFormat");
764   Maybe<bool> maybe = JSReceiver::HasOwnProperty(obj, key);
765   CHECK(maybe.IsJust());
766   if (maybe.FromJust()) {
767     return reinterpret_cast<icu::SimpleDateFormat*>(
768         obj->GetInternalField(0));
769   }
770 
771   return NULL;
772 }
773 
DeleteDateFormat(const v8::WeakCallbackInfo<void> & data)774 void DateFormat::DeleteDateFormat(const v8::WeakCallbackInfo<void>& data) {
775   delete reinterpret_cast<icu::SimpleDateFormat*>(data.GetInternalField(0));
776   GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
777 }
778 
779 
InitializeNumberFormat(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)780 icu::DecimalFormat* NumberFormat::InitializeNumberFormat(
781     Isolate* isolate,
782     Handle<String> locale,
783     Handle<JSObject> options,
784     Handle<JSObject> resolved) {
785   // Convert BCP47 into ICU locale format.
786   UErrorCode status = U_ZERO_ERROR;
787   icu::Locale icu_locale;
788   char icu_result[ULOC_FULLNAME_CAPACITY];
789   int icu_length = 0;
790   v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
791   if (bcp47_locale.length() != 0) {
792     uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
793                         &icu_length, &status);
794     if (U_FAILURE(status) || icu_length == 0) {
795       return NULL;
796     }
797     icu_locale = icu::Locale(icu_result);
798   }
799 
800   icu::DecimalFormat* number_format =
801       CreateICUNumberFormat(isolate, icu_locale, options);
802   if (!number_format) {
803     // Remove extensions and try again.
804     icu::Locale no_extension_locale(icu_locale.getBaseName());
805     number_format = CreateICUNumberFormat(
806         isolate, no_extension_locale, options);
807 
808     if (!number_format) {
809       FATAL("Failed to create ICU number format, are ICU data files missing?");
810     }
811 
812     // Set resolved settings (pattern, numbering system).
813     SetResolvedNumberSettings(
814         isolate, no_extension_locale, number_format, resolved);
815   } else {
816     SetResolvedNumberSettings(isolate, icu_locale, number_format, resolved);
817   }
818 
819   return number_format;
820 }
821 
822 
UnpackNumberFormat(Isolate * isolate,Handle<JSObject> obj)823 icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
824     Isolate* isolate,
825     Handle<JSObject> obj) {
826   Handle<String> key =
827       isolate->factory()->NewStringFromStaticChars("numberFormat");
828   Maybe<bool> maybe = JSReceiver::HasOwnProperty(obj, key);
829   CHECK(maybe.IsJust());
830   if (maybe.FromJust()) {
831     return reinterpret_cast<icu::DecimalFormat*>(obj->GetInternalField(0));
832   }
833 
834   return NULL;
835 }
836 
DeleteNumberFormat(const v8::WeakCallbackInfo<void> & data)837 void NumberFormat::DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data) {
838   delete reinterpret_cast<icu::DecimalFormat*>(data.GetInternalField(0));
839   GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
840 }
841 
842 
InitializeCollator(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)843 icu::Collator* Collator::InitializeCollator(
844     Isolate* isolate,
845     Handle<String> locale,
846     Handle<JSObject> options,
847     Handle<JSObject> resolved) {
848   // Convert BCP47 into ICU locale format.
849   UErrorCode status = U_ZERO_ERROR;
850   icu::Locale icu_locale;
851   char icu_result[ULOC_FULLNAME_CAPACITY];
852   int icu_length = 0;
853   v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
854   if (bcp47_locale.length() != 0) {
855     uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
856                         &icu_length, &status);
857     if (U_FAILURE(status) || icu_length == 0) {
858       return NULL;
859     }
860     icu_locale = icu::Locale(icu_result);
861   }
862 
863   icu::Collator* collator = CreateICUCollator(isolate, icu_locale, options);
864   if (!collator) {
865     // Remove extensions and try again.
866     icu::Locale no_extension_locale(icu_locale.getBaseName());
867     collator = CreateICUCollator(isolate, no_extension_locale, options);
868 
869     if (!collator) {
870       FATAL("Failed to create ICU collator, are ICU data files missing?");
871     }
872 
873     // Set resolved settings (pattern, numbering system).
874     SetResolvedCollatorSettings(
875         isolate, no_extension_locale, collator, resolved);
876   } else {
877     SetResolvedCollatorSettings(isolate, icu_locale, collator, resolved);
878   }
879 
880   return collator;
881 }
882 
883 
UnpackCollator(Isolate * isolate,Handle<JSObject> obj)884 icu::Collator* Collator::UnpackCollator(Isolate* isolate,
885                                         Handle<JSObject> obj) {
886   Handle<String> key = isolate->factory()->NewStringFromStaticChars("collator");
887   Maybe<bool> maybe = JSReceiver::HasOwnProperty(obj, key);
888   CHECK(maybe.IsJust());
889   if (maybe.FromJust()) {
890     return reinterpret_cast<icu::Collator*>(obj->GetInternalField(0));
891   }
892 
893   return NULL;
894 }
895 
DeleteCollator(const v8::WeakCallbackInfo<void> & data)896 void Collator::DeleteCollator(const v8::WeakCallbackInfo<void>& data) {
897   delete reinterpret_cast<icu::Collator*>(data.GetInternalField(0));
898   GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
899 }
900 
901 
InitializeBreakIterator(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)902 icu::BreakIterator* BreakIterator::InitializeBreakIterator(
903     Isolate* isolate,
904     Handle<String> locale,
905     Handle<JSObject> options,
906     Handle<JSObject> resolved) {
907   // Convert BCP47 into ICU locale format.
908   UErrorCode status = U_ZERO_ERROR;
909   icu::Locale icu_locale;
910   char icu_result[ULOC_FULLNAME_CAPACITY];
911   int icu_length = 0;
912   v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
913   if (bcp47_locale.length() != 0) {
914     uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
915                         &icu_length, &status);
916     if (U_FAILURE(status) || icu_length == 0) {
917       return NULL;
918     }
919     icu_locale = icu::Locale(icu_result);
920   }
921 
922   icu::BreakIterator* break_iterator = CreateICUBreakIterator(
923       isolate, icu_locale, options);
924   if (!break_iterator) {
925     // Remove extensions and try again.
926     icu::Locale no_extension_locale(icu_locale.getBaseName());
927     break_iterator = CreateICUBreakIterator(
928         isolate, no_extension_locale, options);
929 
930     if (!break_iterator) {
931       FATAL("Failed to create ICU break iterator, are ICU data files missing?");
932     }
933 
934     // Set resolved settings (locale).
935     SetResolvedBreakIteratorSettings(
936         isolate, no_extension_locale, break_iterator, resolved);
937   } else {
938     SetResolvedBreakIteratorSettings(
939         isolate, icu_locale, break_iterator, resolved);
940   }
941 
942   return break_iterator;
943 }
944 
945 
UnpackBreakIterator(Isolate * isolate,Handle<JSObject> obj)946 icu::BreakIterator* BreakIterator::UnpackBreakIterator(Isolate* isolate,
947                                                        Handle<JSObject> obj) {
948   Handle<String> key =
949       isolate->factory()->NewStringFromStaticChars("breakIterator");
950   Maybe<bool> maybe = JSReceiver::HasOwnProperty(obj, key);
951   CHECK(maybe.IsJust());
952   if (maybe.FromJust()) {
953     return reinterpret_cast<icu::BreakIterator*>(obj->GetInternalField(0));
954   }
955 
956   return NULL;
957 }
958 
DeleteBreakIterator(const v8::WeakCallbackInfo<void> & data)959 void BreakIterator::DeleteBreakIterator(
960     const v8::WeakCallbackInfo<void>& data) {
961   delete reinterpret_cast<icu::BreakIterator*>(data.GetInternalField(0));
962   delete reinterpret_cast<icu::UnicodeString*>(data.GetInternalField(1));
963   GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
964 }
965 
966 }  // namespace internal
967 }  // namespace v8
968