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