1 // Copyright 2014 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 
5 
6 #ifdef V8_I18N_SUPPORT
7 #include "src/runtime/runtime-utils.h"
8 
9 #include "src/api.h"
10 #include "src/api-natives.h"
11 #include "src/arguments.h"
12 #include "src/factory.h"
13 #include "src/i18n.h"
14 #include "src/isolate-inl.h"
15 #include "src/messages.h"
16 
17 #include "unicode/brkiter.h"
18 #include "unicode/calendar.h"
19 #include "unicode/coll.h"
20 #include "unicode/curramt.h"
21 #include "unicode/datefmt.h"
22 #include "unicode/dcfmtsym.h"
23 #include "unicode/decimfmt.h"
24 #include "unicode/dtfmtsym.h"
25 #include "unicode/dtptngen.h"
26 #include "unicode/locid.h"
27 #include "unicode/numfmt.h"
28 #include "unicode/numsys.h"
29 #include "unicode/rbbi.h"
30 #include "unicode/smpdtfmt.h"
31 #include "unicode/timezone.h"
32 #include "unicode/uchar.h"
33 #include "unicode/ucol.h"
34 #include "unicode/ucurr.h"
35 #include "unicode/uloc.h"
36 #include "unicode/unum.h"
37 #include "unicode/uversion.h"
38 
39 
40 namespace v8 {
41 namespace internal {
42 
RUNTIME_FUNCTION(Runtime_CanonicalizeLanguageTag)43 RUNTIME_FUNCTION(Runtime_CanonicalizeLanguageTag) {
44   HandleScope scope(isolate);
45   Factory* factory = isolate->factory();
46 
47   DCHECK(args.length() == 1);
48   CONVERT_ARG_HANDLE_CHECKED(String, locale_id_str, 0);
49 
50   v8::String::Utf8Value locale_id(v8::Utils::ToLocal(locale_id_str));
51 
52   // Return value which denotes invalid language tag.
53   const char* const kInvalidTag = "invalid-tag";
54 
55   UErrorCode error = U_ZERO_ERROR;
56   char icu_result[ULOC_FULLNAME_CAPACITY];
57   int icu_length = 0;
58 
59   uloc_forLanguageTag(*locale_id, icu_result, ULOC_FULLNAME_CAPACITY,
60                       &icu_length, &error);
61   if (U_FAILURE(error) || icu_length == 0) {
62     return *factory->NewStringFromAsciiChecked(kInvalidTag);
63   }
64 
65   char result[ULOC_FULLNAME_CAPACITY];
66 
67   // Force strict BCP47 rules.
68   uloc_toLanguageTag(icu_result, result, ULOC_FULLNAME_CAPACITY, TRUE, &error);
69 
70   if (U_FAILURE(error)) {
71     return *factory->NewStringFromAsciiChecked(kInvalidTag);
72   }
73 
74   return *factory->NewStringFromAsciiChecked(result);
75 }
76 
77 
RUNTIME_FUNCTION(Runtime_AvailableLocalesOf)78 RUNTIME_FUNCTION(Runtime_AvailableLocalesOf) {
79   HandleScope scope(isolate);
80   Factory* factory = isolate->factory();
81 
82   DCHECK(args.length() == 1);
83   CONVERT_ARG_HANDLE_CHECKED(String, service, 0);
84 
85   const icu::Locale* available_locales = NULL;
86   int32_t count = 0;
87 
88   if (service->IsUtf8EqualTo(CStrVector("collator"))) {
89     available_locales = icu::Collator::getAvailableLocales(count);
90   } else if (service->IsUtf8EqualTo(CStrVector("numberformat"))) {
91     available_locales = icu::NumberFormat::getAvailableLocales(count);
92   } else if (service->IsUtf8EqualTo(CStrVector("dateformat"))) {
93     available_locales = icu::DateFormat::getAvailableLocales(count);
94   } else if (service->IsUtf8EqualTo(CStrVector("breakiterator"))) {
95     available_locales = icu::BreakIterator::getAvailableLocales(count);
96   }
97 
98   UErrorCode error = U_ZERO_ERROR;
99   char result[ULOC_FULLNAME_CAPACITY];
100   Handle<JSObject> locales = factory->NewJSObject(isolate->object_function());
101 
102   for (int32_t i = 0; i < count; ++i) {
103     const char* icu_name = available_locales[i].getName();
104 
105     error = U_ZERO_ERROR;
106     // No need to force strict BCP47 rules.
107     uloc_toLanguageTag(icu_name, result, ULOC_FULLNAME_CAPACITY, FALSE, &error);
108     if (U_FAILURE(error)) {
109       // This shouldn't happen, but lets not break the user.
110       continue;
111     }
112 
113     RETURN_FAILURE_ON_EXCEPTION(
114         isolate, JSObject::SetOwnPropertyIgnoreAttributes(
115                      locales, factory->NewStringFromAsciiChecked(result),
116                      factory->NewNumber(i), NONE));
117   }
118 
119   return *locales;
120 }
121 
122 
RUNTIME_FUNCTION(Runtime_GetDefaultICULocale)123 RUNTIME_FUNCTION(Runtime_GetDefaultICULocale) {
124   HandleScope scope(isolate);
125   Factory* factory = isolate->factory();
126 
127   DCHECK(args.length() == 0);
128 
129   icu::Locale default_locale;
130 
131   // Set the locale
132   char result[ULOC_FULLNAME_CAPACITY];
133   UErrorCode status = U_ZERO_ERROR;
134   uloc_toLanguageTag(default_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
135                      FALSE, &status);
136   if (U_SUCCESS(status)) {
137     return *factory->NewStringFromAsciiChecked(result);
138   }
139 
140   return *factory->NewStringFromStaticChars("und");
141 }
142 
143 
RUNTIME_FUNCTION(Runtime_GetLanguageTagVariants)144 RUNTIME_FUNCTION(Runtime_GetLanguageTagVariants) {
145   HandleScope scope(isolate);
146   Factory* factory = isolate->factory();
147 
148   DCHECK(args.length() == 1);
149 
150   CONVERT_ARG_HANDLE_CHECKED(JSArray, input, 0);
151 
152   uint32_t length = static_cast<uint32_t>(input->length()->Number());
153   // Set some limit to prevent fuzz tests from going OOM.
154   // Can be bumped when callers' requirements change.
155   RUNTIME_ASSERT(length < 100);
156   Handle<FixedArray> output = factory->NewFixedArray(length);
157   Handle<Name> maximized = factory->NewStringFromStaticChars("maximized");
158   Handle<Name> base = factory->NewStringFromStaticChars("base");
159   for (unsigned int i = 0; i < length; ++i) {
160     Handle<Object> locale_id;
161     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, locale_id,
162                                        Object::GetElement(isolate, input, i));
163     if (!locale_id->IsString()) {
164       return isolate->Throw(*factory->illegal_argument_string());
165     }
166 
167     v8::String::Utf8Value utf8_locale_id(
168         v8::Utils::ToLocal(Handle<String>::cast(locale_id)));
169 
170     UErrorCode error = U_ZERO_ERROR;
171 
172     // Convert from BCP47 to ICU format.
173     // de-DE-u-co-phonebk -> de_DE@collation=phonebook
174     char icu_locale[ULOC_FULLNAME_CAPACITY];
175     int icu_locale_length = 0;
176     uloc_forLanguageTag(*utf8_locale_id, icu_locale, ULOC_FULLNAME_CAPACITY,
177                         &icu_locale_length, &error);
178     if (U_FAILURE(error) || icu_locale_length == 0) {
179       return isolate->Throw(*factory->illegal_argument_string());
180     }
181 
182     // Maximize the locale.
183     // de_DE@collation=phonebook -> de_Latn_DE@collation=phonebook
184     char icu_max_locale[ULOC_FULLNAME_CAPACITY];
185     uloc_addLikelySubtags(icu_locale, icu_max_locale, ULOC_FULLNAME_CAPACITY,
186                           &error);
187 
188     // Remove extensions from maximized locale.
189     // de_Latn_DE@collation=phonebook -> de_Latn_DE
190     char icu_base_max_locale[ULOC_FULLNAME_CAPACITY];
191     uloc_getBaseName(icu_max_locale, icu_base_max_locale,
192                      ULOC_FULLNAME_CAPACITY, &error);
193 
194     // Get original name without extensions.
195     // de_DE@collation=phonebook -> de_DE
196     char icu_base_locale[ULOC_FULLNAME_CAPACITY];
197     uloc_getBaseName(icu_locale, icu_base_locale, ULOC_FULLNAME_CAPACITY,
198                      &error);
199 
200     // Convert from ICU locale format to BCP47 format.
201     // de_Latn_DE -> de-Latn-DE
202     char base_max_locale[ULOC_FULLNAME_CAPACITY];
203     uloc_toLanguageTag(icu_base_max_locale, base_max_locale,
204                        ULOC_FULLNAME_CAPACITY, FALSE, &error);
205 
206     // de_DE -> de-DE
207     char base_locale[ULOC_FULLNAME_CAPACITY];
208     uloc_toLanguageTag(icu_base_locale, base_locale, ULOC_FULLNAME_CAPACITY,
209                        FALSE, &error);
210 
211     if (U_FAILURE(error)) {
212       return isolate->Throw(*factory->illegal_argument_string());
213     }
214 
215     Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
216     Handle<String> value = factory->NewStringFromAsciiChecked(base_max_locale);
217     JSObject::AddProperty(result, maximized, value, NONE);
218     value = factory->NewStringFromAsciiChecked(base_locale);
219     JSObject::AddProperty(result, base, value, NONE);
220     output->set(i, *result);
221   }
222 
223   Handle<JSArray> result = factory->NewJSArrayWithElements(output);
224   result->set_length(Smi::FromInt(length));
225   return *result;
226 }
227 
228 
RUNTIME_FUNCTION(Runtime_IsInitializedIntlObject)229 RUNTIME_FUNCTION(Runtime_IsInitializedIntlObject) {
230   HandleScope scope(isolate);
231 
232   DCHECK(args.length() == 1);
233 
234   CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
235 
236   if (!input->IsJSObject()) return isolate->heap()->false_value();
237   Handle<JSObject> obj = Handle<JSObject>::cast(input);
238 
239   Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
240   Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
241   return isolate->heap()->ToBoolean(!tag->IsUndefined());
242 }
243 
244 
RUNTIME_FUNCTION(Runtime_IsInitializedIntlObjectOfType)245 RUNTIME_FUNCTION(Runtime_IsInitializedIntlObjectOfType) {
246   HandleScope scope(isolate);
247 
248   DCHECK(args.length() == 2);
249 
250   CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
251   CONVERT_ARG_HANDLE_CHECKED(String, expected_type, 1);
252 
253   if (!input->IsJSObject()) return isolate->heap()->false_value();
254   Handle<JSObject> obj = Handle<JSObject>::cast(input);
255 
256   Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
257   Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
258   return isolate->heap()->ToBoolean(tag->IsString() &&
259                                     String::cast(*tag)->Equals(*expected_type));
260 }
261 
262 
RUNTIME_FUNCTION(Runtime_MarkAsInitializedIntlObjectOfType)263 RUNTIME_FUNCTION(Runtime_MarkAsInitializedIntlObjectOfType) {
264   HandleScope scope(isolate);
265 
266   DCHECK(args.length() == 3);
267 
268   CONVERT_ARG_HANDLE_CHECKED(JSObject, input, 0);
269   CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
270   CONVERT_ARG_HANDLE_CHECKED(JSObject, impl, 2);
271 
272   Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
273   JSObject::SetProperty(input, marker, type, STRICT).Assert();
274 
275   marker = isolate->factory()->intl_impl_object_symbol();
276   JSObject::SetProperty(input, marker, impl, STRICT).Assert();
277 
278   return isolate->heap()->undefined_value();
279 }
280 
281 
RUNTIME_FUNCTION(Runtime_GetImplFromInitializedIntlObject)282 RUNTIME_FUNCTION(Runtime_GetImplFromInitializedIntlObject) {
283   HandleScope scope(isolate);
284 
285   DCHECK(args.length() == 1);
286 
287   CONVERT_ARG_HANDLE_CHECKED(JSObject, input, 0);
288 
289   if (!input->IsJSObject()) {
290     THROW_NEW_ERROR_RETURN_FAILURE(
291         isolate, NewTypeError(MessageTemplate::kNotIntlObject, input));
292   }
293 
294   Handle<JSObject> obj = Handle<JSObject>::cast(input);
295 
296   Handle<Symbol> marker = isolate->factory()->intl_impl_object_symbol();
297 
298   Handle<Object> impl = JSReceiver::GetDataProperty(obj, marker);
299   if (impl->IsTheHole()) {
300     THROW_NEW_ERROR_RETURN_FAILURE(
301         isolate, NewTypeError(MessageTemplate::kNotIntlObject, obj));
302   }
303   return *impl;
304 }
305 
306 
RUNTIME_FUNCTION(Runtime_CreateDateTimeFormat)307 RUNTIME_FUNCTION(Runtime_CreateDateTimeFormat) {
308   HandleScope scope(isolate);
309 
310   DCHECK(args.length() == 3);
311 
312   CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
313   CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
314   CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
315 
316   Handle<ObjectTemplateInfo> date_format_template = I18N::GetTemplate(isolate);
317 
318   // Create an empty object wrapper.
319   Handle<JSObject> local_object;
320   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
321       isolate, local_object,
322       ApiNatives::InstantiateObject(date_format_template));
323 
324   // Set date time formatter as internal field of the resulting JS object.
325   icu::SimpleDateFormat* date_format =
326       DateFormat::InitializeDateTimeFormat(isolate, locale, options, resolved);
327 
328   if (!date_format) return isolate->ThrowIllegalOperation();
329 
330   local_object->SetInternalField(0, reinterpret_cast<Smi*>(date_format));
331 
332   Factory* factory = isolate->factory();
333   Handle<String> key = factory->NewStringFromStaticChars("dateFormat");
334   Handle<String> value = factory->NewStringFromStaticChars("valid");
335   JSObject::AddProperty(local_object, key, value, NONE);
336 
337   // Make object handle weak so we can delete the data format once GC kicks in.
338   Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
339   GlobalHandles::MakeWeak(wrapper.location(),
340                           reinterpret_cast<void*>(wrapper.location()),
341                           DateFormat::DeleteDateFormat);
342   return *local_object;
343 }
344 
345 
RUNTIME_FUNCTION(Runtime_InternalDateFormat)346 RUNTIME_FUNCTION(Runtime_InternalDateFormat) {
347   HandleScope scope(isolate);
348 
349   DCHECK(args.length() == 2);
350 
351   CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
352   CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 1);
353 
354   Handle<Object> value;
355   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(date));
356 
357   icu::SimpleDateFormat* date_format =
358       DateFormat::UnpackDateFormat(isolate, date_format_holder);
359   if (!date_format) return isolate->ThrowIllegalOperation();
360 
361   icu::UnicodeString result;
362   date_format->format(value->Number(), result);
363 
364   Handle<String> result_str;
365   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
366       isolate, result_str,
367       isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
368           reinterpret_cast<const uint16_t*>(result.getBuffer()),
369           result.length())));
370   return *result_str;
371 }
372 
373 
RUNTIME_FUNCTION(Runtime_InternalDateParse)374 RUNTIME_FUNCTION(Runtime_InternalDateParse) {
375   HandleScope scope(isolate);
376 
377   DCHECK(args.length() == 2);
378 
379   CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
380   CONVERT_ARG_HANDLE_CHECKED(String, date_string, 1);
381 
382   v8::String::Utf8Value utf8_date(v8::Utils::ToLocal(date_string));
383   icu::UnicodeString u_date(icu::UnicodeString::fromUTF8(*utf8_date));
384   icu::SimpleDateFormat* date_format =
385       DateFormat::UnpackDateFormat(isolate, date_format_holder);
386   if (!date_format) return isolate->ThrowIllegalOperation();
387 
388   UErrorCode status = U_ZERO_ERROR;
389   UDate date = date_format->parse(u_date, status);
390   if (U_FAILURE(status)) return isolate->heap()->undefined_value();
391 
392   Handle<JSDate> result;
393   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
394       isolate, result,
395       JSDate::New(isolate->date_function(), isolate->date_function(),
396                   static_cast<double>(date)));
397   return *result;
398 }
399 
400 
RUNTIME_FUNCTION(Runtime_CreateNumberFormat)401 RUNTIME_FUNCTION(Runtime_CreateNumberFormat) {
402   HandleScope scope(isolate);
403 
404   DCHECK(args.length() == 3);
405 
406   CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
407   CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
408   CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
409 
410   Handle<ObjectTemplateInfo> number_format_template =
411       I18N::GetTemplate(isolate);
412 
413   // Create an empty object wrapper.
414   Handle<JSObject> local_object;
415   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
416       isolate, local_object,
417       ApiNatives::InstantiateObject(number_format_template));
418 
419   // Set number formatter as internal field of the resulting JS object.
420   icu::DecimalFormat* number_format =
421       NumberFormat::InitializeNumberFormat(isolate, locale, options, resolved);
422 
423   if (!number_format) return isolate->ThrowIllegalOperation();
424 
425   local_object->SetInternalField(0, reinterpret_cast<Smi*>(number_format));
426 
427   Factory* factory = isolate->factory();
428   Handle<String> key = factory->NewStringFromStaticChars("numberFormat");
429   Handle<String> value = factory->NewStringFromStaticChars("valid");
430   JSObject::AddProperty(local_object, key, value, NONE);
431 
432   Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
433   GlobalHandles::MakeWeak(wrapper.location(),
434                           reinterpret_cast<void*>(wrapper.location()),
435                           NumberFormat::DeleteNumberFormat);
436   return *local_object;
437 }
438 
439 
RUNTIME_FUNCTION(Runtime_InternalNumberFormat)440 RUNTIME_FUNCTION(Runtime_InternalNumberFormat) {
441   HandleScope scope(isolate);
442 
443   DCHECK(args.length() == 2);
444 
445   CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0);
446   CONVERT_ARG_HANDLE_CHECKED(Object, number, 1);
447 
448   Handle<Object> value;
449   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(number));
450 
451   icu::DecimalFormat* number_format =
452       NumberFormat::UnpackNumberFormat(isolate, number_format_holder);
453   if (!number_format) return isolate->ThrowIllegalOperation();
454 
455   icu::UnicodeString result;
456   number_format->format(value->Number(), result);
457 
458   Handle<String> result_str;
459   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
460       isolate, result_str,
461       isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
462           reinterpret_cast<const uint16_t*>(result.getBuffer()),
463           result.length())));
464   return *result_str;
465 }
466 
467 
RUNTIME_FUNCTION(Runtime_InternalNumberParse)468 RUNTIME_FUNCTION(Runtime_InternalNumberParse) {
469   HandleScope scope(isolate);
470 
471   DCHECK(args.length() == 2);
472 
473   CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0);
474   CONVERT_ARG_HANDLE_CHECKED(String, number_string, 1);
475 
476   isolate->CountUsage(v8::Isolate::UseCounterFeature::kIntlV8Parse);
477 
478   v8::String::Utf8Value utf8_number(v8::Utils::ToLocal(number_string));
479   icu::UnicodeString u_number(icu::UnicodeString::fromUTF8(*utf8_number));
480   icu::DecimalFormat* number_format =
481       NumberFormat::UnpackNumberFormat(isolate, number_format_holder);
482   if (!number_format) return isolate->ThrowIllegalOperation();
483 
484   UErrorCode status = U_ZERO_ERROR;
485   icu::Formattable result;
486   // ICU 4.6 doesn't support parseCurrency call. We need to wait for ICU49
487   // to be part of Chrome.
488   // TODO(cira): Include currency parsing code using parseCurrency call.
489   // We need to check if the formatter parses all currencies or only the
490   // one it was constructed with (it will impact the API - how to return ISO
491   // code and the value).
492   number_format->parse(u_number, result, status);
493   if (U_FAILURE(status)) return isolate->heap()->undefined_value();
494 
495   switch (result.getType()) {
496     case icu::Formattable::kDouble:
497       return *isolate->factory()->NewNumber(result.getDouble());
498     case icu::Formattable::kLong:
499       return *isolate->factory()->NewNumberFromInt(result.getLong());
500     case icu::Formattable::kInt64:
501       return *isolate->factory()->NewNumber(
502           static_cast<double>(result.getInt64()));
503     default:
504       return isolate->heap()->undefined_value();
505   }
506 }
507 
508 
RUNTIME_FUNCTION(Runtime_CreateCollator)509 RUNTIME_FUNCTION(Runtime_CreateCollator) {
510   HandleScope scope(isolate);
511 
512   DCHECK(args.length() == 3);
513 
514   CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
515   CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
516   CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
517 
518   Handle<ObjectTemplateInfo> collator_template = I18N::GetTemplate(isolate);
519 
520   // Create an empty object wrapper.
521   Handle<JSObject> local_object;
522   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
523       isolate, local_object, ApiNatives::InstantiateObject(collator_template));
524 
525   // Set collator as internal field of the resulting JS object.
526   icu::Collator* collator =
527       Collator::InitializeCollator(isolate, locale, options, resolved);
528 
529   if (!collator) return isolate->ThrowIllegalOperation();
530 
531   local_object->SetInternalField(0, reinterpret_cast<Smi*>(collator));
532 
533   Factory* factory = isolate->factory();
534   Handle<String> key = factory->NewStringFromStaticChars("collator");
535   Handle<String> value = factory->NewStringFromStaticChars("valid");
536   JSObject::AddProperty(local_object, key, value, NONE);
537 
538   Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
539   GlobalHandles::MakeWeak(wrapper.location(),
540                           reinterpret_cast<void*>(wrapper.location()),
541                           Collator::DeleteCollator);
542   return *local_object;
543 }
544 
545 
RUNTIME_FUNCTION(Runtime_InternalCompare)546 RUNTIME_FUNCTION(Runtime_InternalCompare) {
547   HandleScope scope(isolate);
548 
549   DCHECK(args.length() == 3);
550 
551   CONVERT_ARG_HANDLE_CHECKED(JSObject, collator_holder, 0);
552   CONVERT_ARG_HANDLE_CHECKED(String, string1, 1);
553   CONVERT_ARG_HANDLE_CHECKED(String, string2, 2);
554 
555   icu::Collator* collator = Collator::UnpackCollator(isolate, collator_holder);
556   if (!collator) return isolate->ThrowIllegalOperation();
557 
558   v8::String::Value string_value1(v8::Utils::ToLocal(string1));
559   v8::String::Value string_value2(v8::Utils::ToLocal(string2));
560   const UChar* u_string1 = reinterpret_cast<const UChar*>(*string_value1);
561   const UChar* u_string2 = reinterpret_cast<const UChar*>(*string_value2);
562   UErrorCode status = U_ZERO_ERROR;
563   UCollationResult result =
564       collator->compare(u_string1, string_value1.length(), u_string2,
565                         string_value2.length(), status);
566   if (U_FAILURE(status)) return isolate->ThrowIllegalOperation();
567 
568   return *isolate->factory()->NewNumberFromInt(result);
569 }
570 
571 
RUNTIME_FUNCTION(Runtime_StringNormalize)572 RUNTIME_FUNCTION(Runtime_StringNormalize) {
573   HandleScope scope(isolate);
574   static const UNormalizationMode normalizationForms[] = {
575       UNORM_NFC, UNORM_NFD, UNORM_NFKC, UNORM_NFKD};
576 
577   DCHECK(args.length() == 2);
578 
579   CONVERT_ARG_HANDLE_CHECKED(String, stringValue, 0);
580   CONVERT_NUMBER_CHECKED(int, form_id, Int32, args[1]);
581   RUNTIME_ASSERT(form_id >= 0 &&
582                  static_cast<size_t>(form_id) < arraysize(normalizationForms));
583 
584   v8::String::Value string_value(v8::Utils::ToLocal(stringValue));
585   const UChar* u_value = reinterpret_cast<const UChar*>(*string_value);
586 
587   // TODO(mnita): check Normalizer2 (not available in ICU 46)
588   UErrorCode status = U_ZERO_ERROR;
589   icu::UnicodeString result;
590   icu::Normalizer::normalize(u_value, normalizationForms[form_id], 0, result,
591                              status);
592   if (U_FAILURE(status)) {
593     return isolate->heap()->undefined_value();
594   }
595 
596   Handle<String> result_str;
597   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
598       isolate, result_str,
599       isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
600           reinterpret_cast<const uint16_t*>(result.getBuffer()),
601           result.length())));
602   return *result_str;
603 }
604 
605 
RUNTIME_FUNCTION(Runtime_CreateBreakIterator)606 RUNTIME_FUNCTION(Runtime_CreateBreakIterator) {
607   HandleScope scope(isolate);
608 
609   DCHECK(args.length() == 3);
610 
611   CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
612   CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
613   CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
614 
615   Handle<ObjectTemplateInfo> break_iterator_template =
616       I18N::GetTemplate2(isolate);
617 
618   // Create an empty object wrapper.
619   Handle<JSObject> local_object;
620   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
621       isolate, local_object,
622       ApiNatives::InstantiateObject(break_iterator_template));
623 
624   // Set break iterator as internal field of the resulting JS object.
625   icu::BreakIterator* break_iterator = BreakIterator::InitializeBreakIterator(
626       isolate, locale, options, resolved);
627 
628   if (!break_iterator) return isolate->ThrowIllegalOperation();
629 
630   local_object->SetInternalField(0, reinterpret_cast<Smi*>(break_iterator));
631   // Make sure that the pointer to adopted text is NULL.
632   local_object->SetInternalField(1, static_cast<Smi*>(nullptr));
633 
634   Factory* factory = isolate->factory();
635   Handle<String> key = factory->NewStringFromStaticChars("breakIterator");
636   Handle<String> value = factory->NewStringFromStaticChars("valid");
637   JSObject::AddProperty(local_object, key, value, NONE);
638 
639   // Make object handle weak so we can delete the break iterator once GC kicks
640   // in.
641   Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
642   GlobalHandles::MakeWeak(wrapper.location(),
643                           reinterpret_cast<void*>(wrapper.location()),
644                           BreakIterator::DeleteBreakIterator);
645   return *local_object;
646 }
647 
648 
RUNTIME_FUNCTION(Runtime_BreakIteratorAdoptText)649 RUNTIME_FUNCTION(Runtime_BreakIteratorAdoptText) {
650   HandleScope scope(isolate);
651 
652   DCHECK(args.length() == 2);
653 
654   CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
655   CONVERT_ARG_HANDLE_CHECKED(String, text, 1);
656 
657   icu::BreakIterator* break_iterator =
658       BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
659   if (!break_iterator) return isolate->ThrowIllegalOperation();
660 
661   icu::UnicodeString* u_text = reinterpret_cast<icu::UnicodeString*>(
662       break_iterator_holder->GetInternalField(1));
663   delete u_text;
664 
665   v8::String::Value text_value(v8::Utils::ToLocal(text));
666   u_text = new icu::UnicodeString(reinterpret_cast<const UChar*>(*text_value),
667                                   text_value.length());
668   break_iterator_holder->SetInternalField(1, reinterpret_cast<Smi*>(u_text));
669 
670   break_iterator->setText(*u_text);
671 
672   return isolate->heap()->undefined_value();
673 }
674 
675 
RUNTIME_FUNCTION(Runtime_BreakIteratorFirst)676 RUNTIME_FUNCTION(Runtime_BreakIteratorFirst) {
677   HandleScope scope(isolate);
678 
679   DCHECK(args.length() == 1);
680 
681   CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
682 
683   icu::BreakIterator* break_iterator =
684       BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
685   if (!break_iterator) return isolate->ThrowIllegalOperation();
686 
687   return *isolate->factory()->NewNumberFromInt(break_iterator->first());
688 }
689 
690 
RUNTIME_FUNCTION(Runtime_BreakIteratorNext)691 RUNTIME_FUNCTION(Runtime_BreakIteratorNext) {
692   HandleScope scope(isolate);
693 
694   DCHECK(args.length() == 1);
695 
696   CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
697 
698   icu::BreakIterator* break_iterator =
699       BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
700   if (!break_iterator) return isolate->ThrowIllegalOperation();
701 
702   return *isolate->factory()->NewNumberFromInt(break_iterator->next());
703 }
704 
705 
RUNTIME_FUNCTION(Runtime_BreakIteratorCurrent)706 RUNTIME_FUNCTION(Runtime_BreakIteratorCurrent) {
707   HandleScope scope(isolate);
708 
709   DCHECK(args.length() == 1);
710 
711   CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
712 
713   icu::BreakIterator* break_iterator =
714       BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
715   if (!break_iterator) return isolate->ThrowIllegalOperation();
716 
717   return *isolate->factory()->NewNumberFromInt(break_iterator->current());
718 }
719 
720 
RUNTIME_FUNCTION(Runtime_BreakIteratorBreakType)721 RUNTIME_FUNCTION(Runtime_BreakIteratorBreakType) {
722   HandleScope scope(isolate);
723 
724   DCHECK(args.length() == 1);
725 
726   CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
727 
728   icu::BreakIterator* break_iterator =
729       BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
730   if (!break_iterator) return isolate->ThrowIllegalOperation();
731 
732   // TODO(cira): Remove cast once ICU fixes base BreakIterator class.
733   icu::RuleBasedBreakIterator* rule_based_iterator =
734       static_cast<icu::RuleBasedBreakIterator*>(break_iterator);
735   int32_t status = rule_based_iterator->getRuleStatus();
736   // Keep return values in sync with JavaScript BreakType enum.
737   if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) {
738     return *isolate->factory()->NewStringFromStaticChars("none");
739   } else if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) {
740     return *isolate->factory()->number_string();
741   } else if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) {
742     return *isolate->factory()->NewStringFromStaticChars("letter");
743   } else if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) {
744     return *isolate->factory()->NewStringFromStaticChars("kana");
745   } else if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) {
746     return *isolate->factory()->NewStringFromStaticChars("ideo");
747   } else {
748     return *isolate->factory()->NewStringFromStaticChars("unknown");
749   }
750 }
751 }  // namespace internal
752 }  // namespace v8
753 
754 #endif  // V8_I18N_SUPPORT
755