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