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