Line data Source code
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 : #ifndef V8_INTL_SUPPORT
6 : #error Internationalization is expected to be enabled.
7 : #endif // V8_INTL_SUPPORT
8 :
9 : #include "src/runtime/runtime-utils.h"
10 :
11 : #include <memory>
12 :
13 : #include "src/api-natives.h"
14 : #include "src/api.h"
15 : #include "src/arguments.h"
16 : #include "src/factory.h"
17 : #include "src/intl.h"
18 : #include "src/isolate-inl.h"
19 : #include "src/messages.h"
20 : #include "src/objects/intl-objects-inl.h"
21 : #include "src/objects/intl-objects.h"
22 : #include "src/utils.h"
23 :
24 : #include "unicode/brkiter.h"
25 : #include "unicode/calendar.h"
26 : #include "unicode/coll.h"
27 : #include "unicode/curramt.h"
28 : #include "unicode/datefmt.h"
29 : #include "unicode/dcfmtsym.h"
30 : #include "unicode/decimfmt.h"
31 : #include "unicode/dtfmtsym.h"
32 : #include "unicode/dtptngen.h"
33 : #include "unicode/fieldpos.h"
34 : #include "unicode/fpositer.h"
35 : #include "unicode/locid.h"
36 : #include "unicode/numfmt.h"
37 : #include "unicode/numsys.h"
38 : #include "unicode/rbbi.h"
39 : #include "unicode/smpdtfmt.h"
40 : #include "unicode/timezone.h"
41 : #include "unicode/translit.h"
42 : #include "unicode/uchar.h"
43 : #include "unicode/ucol.h"
44 : #include "unicode/ucurr.h"
45 : #include "unicode/uloc.h"
46 : #include "unicode/unistr.h"
47 : #include "unicode/unum.h"
48 : #include "unicode/ustring.h"
49 : #include "unicode/uversion.h"
50 :
51 : namespace v8 {
52 : namespace internal {
53 :
54 : // ECMA 402 6.2.3
55 1982 : RUNTIME_FUNCTION(Runtime_CanonicalizeLanguageTag) {
56 991 : HandleScope scope(isolate);
57 991 : Factory* factory = isolate->factory();
58 :
59 : DCHECK_EQ(1, args.length());
60 1982 : CONVERT_ARG_HANDLE_CHECKED(String, locale_id_str, 0);
61 :
62 2973 : v8::String::Utf8Value locale_id(v8::Utils::ToLocal(locale_id_str));
63 :
64 : // Return value which denotes invalid language tag.
65 : // TODO(jshin): Can uloc_{for,to}TanguageTag fail even for structually valid
66 : // language tags? If not, just add CHECK instead of returning 'invalid-tag'.
67 : const char* const kInvalidTag = "invalid-tag";
68 :
69 991 : UErrorCode error = U_ZERO_ERROR;
70 : char icu_result[ULOC_FULLNAME_CAPACITY];
71 991 : int icu_length = 0;
72 :
73 991 : uloc_forLanguageTag(*locale_id, icu_result, ULOC_FULLNAME_CAPACITY,
74 991 : &icu_length, &error);
75 991 : if (U_FAILURE(error) || icu_length == 0) {
76 0 : return *factory->NewStringFromAsciiChecked(kInvalidTag);
77 : }
78 :
79 : char result[ULOC_FULLNAME_CAPACITY];
80 :
81 : // Force strict BCP47 rules.
82 991 : uloc_toLanguageTag(icu_result, result, ULOC_FULLNAME_CAPACITY, TRUE, &error);
83 :
84 991 : if (U_FAILURE(error)) {
85 0 : return *factory->NewStringFromAsciiChecked(kInvalidTag);
86 : }
87 :
88 2973 : return *factory->NewStringFromAsciiChecked(result);
89 : }
90 :
91 6132 : RUNTIME_FUNCTION(Runtime_AvailableLocalesOf) {
92 3066 : HandleScope scope(isolate);
93 3066 : Factory* factory = isolate->factory();
94 :
95 : DCHECK_EQ(1, args.length());
96 6132 : CONVERT_ARG_HANDLE_CHECKED(String, service, 0);
97 :
98 : const icu::Locale* available_locales = NULL;
99 3066 : int32_t count = 0;
100 :
101 6132 : if (service->IsUtf8EqualTo(CStrVector("collator"))) {
102 758 : available_locales = icu::Collator::getAvailableLocales(count);
103 4616 : } else if (service->IsUtf8EqualTo(CStrVector("numberformat"))) {
104 743 : available_locales = icu::NumberFormat::getAvailableLocales(count);
105 3130 : } else if (service->IsUtf8EqualTo(CStrVector("dateformat"))) {
106 844 : available_locales = icu::DateFormat::getAvailableLocales(count);
107 1442 : } else if (service->IsUtf8EqualTo(CStrVector("breakiterator"))) {
108 721 : available_locales = icu::BreakIterator::getAvailableLocales(count);
109 : }
110 :
111 3066 : UErrorCode error = U_ZERO_ERROR;
112 : char result[ULOC_FULLNAME_CAPACITY];
113 3066 : Handle<JSObject> locales = factory->NewJSObject(isolate->object_function());
114 :
115 354854 : for (int32_t i = 0; i < count; ++i) {
116 354854 : const char* icu_name = available_locales[i].getName();
117 :
118 354854 : error = U_ZERO_ERROR;
119 : // No need to force strict BCP47 rules.
120 354854 : uloc_toLanguageTag(icu_name, result, ULOC_FULLNAME_CAPACITY, FALSE, &error);
121 354854 : if (U_FAILURE(error)) {
122 : // This shouldn't happen, but lets not break the user.
123 : continue;
124 : }
125 :
126 709708 : RETURN_FAILURE_ON_EXCEPTION(
127 : isolate, JSObject::SetOwnPropertyIgnoreAttributes(
128 : locales, factory->NewStringFromAsciiChecked(result),
129 : factory->NewNumber(i), NONE));
130 : }
131 :
132 3066 : return *locales;
133 : }
134 :
135 1626 : RUNTIME_FUNCTION(Runtime_GetDefaultICULocale) {
136 813 : HandleScope scope(isolate);
137 813 : Factory* factory = isolate->factory();
138 :
139 : DCHECK_EQ(0, args.length());
140 :
141 1626 : icu::Locale default_locale;
142 :
143 : // Set the locale
144 : char result[ULOC_FULLNAME_CAPACITY];
145 813 : UErrorCode status = U_ZERO_ERROR;
146 : uloc_toLanguageTag(default_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
147 813 : FALSE, &status);
148 813 : if (U_SUCCESS(status)) {
149 1626 : return *factory->NewStringFromAsciiChecked(result);
150 : }
151 :
152 813 : return *factory->NewStringFromStaticChars("und");
153 : }
154 :
155 0 : RUNTIME_FUNCTION(Runtime_GetLanguageTagVariants) {
156 0 : HandleScope scope(isolate);
157 0 : Factory* factory = isolate->factory();
158 :
159 : DCHECK_EQ(1, args.length());
160 :
161 0 : CONVERT_ARG_HANDLE_CHECKED(JSArray, input, 0);
162 :
163 0 : uint32_t length = static_cast<uint32_t>(input->length()->Number());
164 : // Set some limit to prevent fuzz tests from going OOM.
165 : // Can be bumped when callers' requirements change.
166 0 : if (length >= 100) return isolate->ThrowIllegalOperation();
167 0 : Handle<FixedArray> output = factory->NewFixedArray(length);
168 0 : Handle<Name> maximized = factory->NewStringFromStaticChars("maximized");
169 0 : Handle<Name> base = factory->NewStringFromStaticChars("base");
170 0 : for (unsigned int i = 0; i < length; ++i) {
171 : Handle<Object> locale_id;
172 0 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
173 : isolate, locale_id, JSReceiver::GetElement(isolate, input, i));
174 0 : if (!locale_id->IsString()) {
175 0 : return isolate->Throw(*factory->illegal_argument_string());
176 : }
177 :
178 : v8::String::Utf8Value utf8_locale_id(
179 0 : v8::Utils::ToLocal(Handle<String>::cast(locale_id)));
180 :
181 0 : UErrorCode error = U_ZERO_ERROR;
182 :
183 : // Convert from BCP47 to ICU format.
184 : // de-DE-u-co-phonebk -> de_DE@collation=phonebook
185 : char icu_locale[ULOC_FULLNAME_CAPACITY];
186 0 : int icu_locale_length = 0;
187 0 : uloc_forLanguageTag(*utf8_locale_id, icu_locale, ULOC_FULLNAME_CAPACITY,
188 0 : &icu_locale_length, &error);
189 0 : if (U_FAILURE(error) || icu_locale_length == 0) {
190 0 : return isolate->Throw(*factory->illegal_argument_string());
191 : }
192 :
193 : // Maximize the locale.
194 : // de_DE@collation=phonebook -> de_Latn_DE@collation=phonebook
195 : char icu_max_locale[ULOC_FULLNAME_CAPACITY];
196 : uloc_addLikelySubtags(icu_locale, icu_max_locale, ULOC_FULLNAME_CAPACITY,
197 0 : &error);
198 :
199 : // Remove extensions from maximized locale.
200 : // de_Latn_DE@collation=phonebook -> de_Latn_DE
201 : char icu_base_max_locale[ULOC_FULLNAME_CAPACITY];
202 : uloc_getBaseName(icu_max_locale, icu_base_max_locale,
203 0 : ULOC_FULLNAME_CAPACITY, &error);
204 :
205 : // Get original name without extensions.
206 : // de_DE@collation=phonebook -> de_DE
207 : char icu_base_locale[ULOC_FULLNAME_CAPACITY];
208 : uloc_getBaseName(icu_locale, icu_base_locale, ULOC_FULLNAME_CAPACITY,
209 0 : &error);
210 :
211 : // Convert from ICU locale format to BCP47 format.
212 : // de_Latn_DE -> de-Latn-DE
213 : char base_max_locale[ULOC_FULLNAME_CAPACITY];
214 : uloc_toLanguageTag(icu_base_max_locale, base_max_locale,
215 0 : ULOC_FULLNAME_CAPACITY, FALSE, &error);
216 :
217 : // de_DE -> de-DE
218 : char base_locale[ULOC_FULLNAME_CAPACITY];
219 : uloc_toLanguageTag(icu_base_locale, base_locale, ULOC_FULLNAME_CAPACITY,
220 0 : FALSE, &error);
221 :
222 0 : if (U_FAILURE(error)) {
223 0 : return isolate->Throw(*factory->illegal_argument_string());
224 : }
225 :
226 0 : Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
227 0 : Handle<String> value = factory->NewStringFromAsciiChecked(base_max_locale);
228 0 : JSObject::AddProperty(result, maximized, value, NONE);
229 0 : value = factory->NewStringFromAsciiChecked(base_locale);
230 0 : JSObject::AddProperty(result, base, value, NONE);
231 0 : output->set(i, *result);
232 0 : }
233 :
234 0 : Handle<JSArray> result = factory->NewJSArrayWithElements(output);
235 0 : result->set_length(Smi::FromInt(length));
236 0 : return *result;
237 : }
238 :
239 0 : RUNTIME_FUNCTION(Runtime_IsInitializedIntlObject) {
240 0 : HandleScope scope(isolate);
241 :
242 : DCHECK_EQ(1, args.length());
243 :
244 0 : CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
245 :
246 0 : if (!input->IsJSObject()) return isolate->heap()->false_value();
247 0 : Handle<JSObject> obj = Handle<JSObject>::cast(input);
248 :
249 0 : Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
250 0 : Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
251 0 : return isolate->heap()->ToBoolean(!tag->IsUndefined(isolate));
252 : }
253 :
254 35612 : RUNTIME_FUNCTION(Runtime_IsInitializedIntlObjectOfType) {
255 17806 : HandleScope scope(isolate);
256 :
257 : DCHECK_EQ(2, args.length());
258 :
259 17806 : CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
260 35612 : CONVERT_ARG_HANDLE_CHECKED(String, expected_type, 1);
261 :
262 17806 : if (!input->IsJSObject()) return isolate->heap()->false_value();
263 17735 : Handle<JSObject> obj = Handle<JSObject>::cast(input);
264 :
265 17735 : Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
266 17735 : Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
267 34910 : return isolate->heap()->ToBoolean(tag->IsString() &&
268 34910 : String::cast(*tag)->Equals(*expected_type));
269 : }
270 :
271 34248 : RUNTIME_FUNCTION(Runtime_MarkAsInitializedIntlObjectOfType) {
272 17124 : HandleScope scope(isolate);
273 :
274 : DCHECK_EQ(2, args.length());
275 :
276 34248 : CONVERT_ARG_HANDLE_CHECKED(JSObject, input, 0);
277 34248 : CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
278 :
279 17124 : Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
280 17124 : JSObject::SetProperty(input, marker, type, STRICT).Assert();
281 :
282 17124 : return isolate->heap()->undefined_value();
283 : }
284 :
285 2266 : RUNTIME_FUNCTION(Runtime_CreateDateTimeFormat) {
286 1133 : HandleScope scope(isolate);
287 :
288 : DCHECK_EQ(3, args.length());
289 :
290 2266 : CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
291 2266 : CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
292 2266 : CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
293 :
294 2266 : RETURN_RESULT_OR_FAILURE(
295 1133 : isolate, JSIntlDateTimeFormat::New(isolate, locale, options, resolved));
296 : }
297 :
298 1100 : RUNTIME_FUNCTION(Runtime_InternalDateFormat) {
299 550 : HandleScope scope(isolate);
300 :
301 : DCHECK_EQ(2, args.length());
302 :
303 1100 : CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
304 1100 : CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 1);
305 :
306 : Handle<Object> value;
307 1100 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(date));
308 :
309 : Handle<JSIntlDateTimeFormat> dtf(
310 : reinterpret_cast<JSIntlDateTimeFormat*>(*date_format_holder));
311 :
312 550 : icu::SimpleDateFormat* date_format = dtf->simple_date_format();
313 550 : CHECK_NOT_NULL(date_format);
314 :
315 1100 : icu::UnicodeString result;
316 550 : date_format->format(value->Number(), result);
317 :
318 1100 : RETURN_RESULT_OR_FAILURE(
319 : isolate, isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
320 : reinterpret_cast<const uint16_t*>(result.getBuffer()),
321 550 : result.length())));
322 : }
323 :
324 : namespace {
325 : // The list comes from third_party/icu/source/i18n/unicode/udat.h.
326 : // They're mapped to DateTimeFormat components listed at
327 : // https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts .
328 :
329 196 : Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
330 196 : switch (field_id) {
331 : case -1:
332 : return isolate->factory()->literal_string();
333 : case UDAT_YEAR_FIELD:
334 : case UDAT_EXTENDED_YEAR_FIELD:
335 : case UDAT_YEAR_NAME_FIELD:
336 : return isolate->factory()->year_string();
337 : case UDAT_MONTH_FIELD:
338 : case UDAT_STANDALONE_MONTH_FIELD:
339 : return isolate->factory()->month_string();
340 : case UDAT_DATE_FIELD:
341 : return isolate->factory()->day_string();
342 : case UDAT_HOUR_OF_DAY1_FIELD:
343 : case UDAT_HOUR_OF_DAY0_FIELD:
344 : case UDAT_HOUR1_FIELD:
345 : case UDAT_HOUR0_FIELD:
346 : return isolate->factory()->hour_string();
347 : case UDAT_MINUTE_FIELD:
348 : return isolate->factory()->minute_string();
349 : case UDAT_SECOND_FIELD:
350 : return isolate->factory()->second_string();
351 : case UDAT_DAY_OF_WEEK_FIELD:
352 : case UDAT_DOW_LOCAL_FIELD:
353 : case UDAT_STANDALONE_DAY_FIELD:
354 : return isolate->factory()->weekday_string();
355 : case UDAT_AM_PM_FIELD:
356 : return isolate->factory()->dayperiod_string();
357 : case UDAT_TIMEZONE_FIELD:
358 : case UDAT_TIMEZONE_RFC_FIELD:
359 : case UDAT_TIMEZONE_GENERIC_FIELD:
360 : case UDAT_TIMEZONE_SPECIAL_FIELD:
361 : case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
362 : case UDAT_TIMEZONE_ISO_FIELD:
363 : case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
364 : return isolate->factory()->timeZoneName_string();
365 : case UDAT_ERA_FIELD:
366 : return isolate->factory()->era_string();
367 : default:
368 : // Other UDAT_*_FIELD's cannot show up because there is no way to specify
369 : // them via options of Intl.DateTimeFormat.
370 0 : UNREACHABLE();
371 : // To prevent MSVC from issuing C4715 warning.
372 : return Handle<String>();
373 : }
374 : }
375 :
376 196 : bool AddElement(Handle<JSArray> array, int index, int32_t field_id,
377 : const icu::UnicodeString& formatted, int32_t begin, int32_t end,
378 : Isolate* isolate) {
379 : HandleScope scope(isolate);
380 : Factory* factory = isolate->factory();
381 196 : Handle<JSObject> element = factory->NewJSObject(isolate->object_function());
382 196 : Handle<String> value = IcuDateFieldIdToDateType(field_id, isolate);
383 196 : JSObject::AddProperty(element, factory->type_string(), value, NONE);
384 :
385 196 : icu::UnicodeString field(formatted.tempSubStringBetween(begin, end));
386 392 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
387 : isolate, value,
388 : factory->NewStringFromTwoByte(Vector<const uint16_t>(
389 : reinterpret_cast<const uint16_t*>(field.getBuffer()),
390 : field.length())),
391 : false);
392 :
393 196 : JSObject::AddProperty(element, factory->value_string(), value, NONE);
394 588 : RETURN_ON_EXCEPTION_VALUE(
395 : isolate, JSObject::AddDataElement(array, index, element, NONE), false);
396 196 : return true;
397 : }
398 :
399 : } // namespace
400 :
401 28 : RUNTIME_FUNCTION(Runtime_InternalDateFormatToParts) {
402 14 : HandleScope scope(isolate);
403 14 : Factory* factory = isolate->factory();
404 :
405 : DCHECK_EQ(2, args.length());
406 :
407 28 : CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
408 28 : CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 1);
409 :
410 : Handle<Object> value;
411 28 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(date));
412 :
413 : icu::SimpleDateFormat* date_format =
414 : reinterpret_cast<JSIntlDateTimeFormat*>(*date_format_holder)
415 14 : ->simple_date_format();
416 14 : CHECK_NOT_NULL(date_format);
417 :
418 28 : icu::UnicodeString formatted;
419 28 : icu::FieldPositionIterator fp_iter;
420 28 : icu::FieldPosition fp;
421 14 : UErrorCode status = U_ZERO_ERROR;
422 14 : date_format->format(value->Number(), formatted, &fp_iter, status);
423 14 : if (U_FAILURE(status)) return isolate->heap()->undefined_value();
424 :
425 14 : Handle<JSArray> result = factory->NewJSArray(0);
426 14 : int32_t length = formatted.length();
427 14 : if (length == 0) return *result;
428 :
429 : int index = 0;
430 : int32_t previous_end_pos = 0;
431 126 : while (fp_iter.next(fp)) {
432 112 : int32_t begin_pos = fp.getBeginIndex();
433 112 : int32_t end_pos = fp.getEndIndex();
434 :
435 112 : if (previous_end_pos < begin_pos) {
436 84 : if (!AddElement(result, index, -1, formatted, previous_end_pos, begin_pos,
437 84 : isolate)) {
438 0 : return isolate->heap()->undefined_value();
439 : }
440 84 : ++index;
441 : }
442 112 : if (!AddElement(result, index, fp.getField(), formatted, begin_pos, end_pos,
443 112 : isolate)) {
444 0 : return isolate->heap()->undefined_value();
445 : }
446 : previous_end_pos = end_pos;
447 112 : ++index;
448 : }
449 14 : if (previous_end_pos < length) {
450 0 : if (!AddElement(result, index, -1, formatted, previous_end_pos, length,
451 0 : isolate)) {
452 0 : return isolate->heap()->undefined_value();
453 : }
454 : }
455 14 : JSObject::ValidateElements(result);
456 14 : return *result;
457 : }
458 :
459 1468 : RUNTIME_FUNCTION(Runtime_CreateNumberFormat) {
460 734 : HandleScope scope(isolate);
461 :
462 : DCHECK_EQ(3, args.length());
463 :
464 1468 : CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
465 1468 : CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
466 1468 : CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
467 :
468 1468 : RETURN_RESULT_OR_FAILURE(
469 734 : isolate, JSIntlNumberFormat::New(isolate, locale, options, resolved));
470 : }
471 :
472 5256 : RUNTIME_FUNCTION(Runtime_InternalNumberFormat) {
473 2628 : HandleScope scope(isolate);
474 :
475 : DCHECK_EQ(2, args.length());
476 :
477 5256 : CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0);
478 2628 : CONVERT_ARG_HANDLE_CHECKED(Object, number, 1);
479 :
480 : Handle<Object> value;
481 5256 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(number));
482 :
483 : icu::DecimalFormat* number_format =
484 : reinterpret_cast<JSIntlNumberFormat*>(*number_format_holder)
485 2628 : ->decimal_format();
486 2628 : CHECK_NOT_NULL(number_format);
487 :
488 5256 : icu::UnicodeString result;
489 2628 : number_format->format(value->Number(), result);
490 :
491 5256 : RETURN_RESULT_OR_FAILURE(
492 : isolate, isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
493 : reinterpret_cast<const uint16_t*>(result.getBuffer()),
494 2628 : result.length())));
495 : }
496 :
497 196 : RUNTIME_FUNCTION(Runtime_CurrencyDigits) {
498 : DCHECK_EQ(1, args.length());
499 :
500 196 : CONVERT_ARG_HANDLE_CHECKED(String, currency, 0);
501 :
502 : // TODO(littledan): Avoid transcoding the string twice
503 196 : v8::String::Utf8Value currency_string(v8::Utils::ToLocal(currency));
504 : icu::UnicodeString currency_icu =
505 196 : icu::UnicodeString::fromUTF8(*currency_string);
506 :
507 : DisallowHeapAllocation no_gc;
508 98 : UErrorCode status = U_ZERO_ERROR;
509 : #if U_ICU_VERSION_MAJOR_NUM >= 59
510 : uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
511 : icu::toUCharPtr(currency_icu.getTerminatedBuffer()), &status);
512 : #else
513 : uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
514 98 : currency_icu.getTerminatedBuffer(), &status);
515 : #endif
516 : // For missing currency codes, default to the most common, 2
517 98 : if (!U_SUCCESS(status)) fraction_digits = 2;
518 196 : return Smi::FromInt(fraction_digits);
519 : }
520 :
521 30192 : RUNTIME_FUNCTION(Runtime_CreateCollator) {
522 15096 : HandleScope scope(isolate);
523 :
524 : DCHECK_EQ(3, args.length());
525 :
526 30192 : CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
527 30192 : CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
528 30192 : CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
529 :
530 30192 : RETURN_RESULT_OR_FAILURE(
531 15096 : isolate, JSIntlCollator::New(isolate, locale, options, resolved));
532 : }
533 :
534 285202 : RUNTIME_FUNCTION(Runtime_InternalCompare) {
535 142601 : HandleScope scope(isolate);
536 :
537 : DCHECK_EQ(3, args.length());
538 :
539 285202 : CONVERT_ARG_HANDLE_CHECKED(JSObject, collator_holder, 0);
540 285202 : CONVERT_ARG_HANDLE_CHECKED(String, string1, 1);
541 285202 : CONVERT_ARG_HANDLE_CHECKED(String, string2, 2);
542 :
543 : icu::Collator* collator =
544 142601 : reinterpret_cast<JSIntlCollator*>(*collator_holder)->collator();
545 142601 : CHECK_NOT_NULL(collator);
546 :
547 142601 : string1 = String::Flatten(string1);
548 142601 : string2 = String::Flatten(string2);
549 :
550 : UCollationResult result;
551 142601 : UErrorCode status = U_ZERO_ERROR;
552 : {
553 : DisallowHeapAllocation no_gc;
554 142601 : int32_t length1 = string1->length();
555 142601 : int32_t length2 = string2->length();
556 142601 : String::FlatContent flat1 = string1->GetFlatContent();
557 142601 : String::FlatContent flat2 = string2->GetFlatContent();
558 142601 : std::unique_ptr<uc16[]> sap1;
559 285202 : std::unique_ptr<uc16[]> sap2;
560 : icu::UnicodeString string_val1(
561 285202 : FALSE, GetUCharBufferFromFlat(flat1, &sap1, length1), length1);
562 : icu::UnicodeString string_val2(
563 285202 : FALSE, GetUCharBufferFromFlat(flat2, &sap2, length2), length2);
564 285202 : result = collator->compare(string_val1, string_val2, status);
565 : }
566 142601 : if (U_FAILURE(status)) return isolate->ThrowIllegalOperation();
567 :
568 285202 : return *isolate->factory()->NewNumberFromInt(result);
569 : }
570 :
571 352 : RUNTIME_FUNCTION(Runtime_CreateBreakIterator) {
572 176 : HandleScope scope(isolate);
573 :
574 : DCHECK_EQ(3, args.length());
575 :
576 352 : CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
577 352 : CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
578 352 : CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
579 :
580 352 : RETURN_RESULT_OR_FAILURE(
581 176 : isolate, JSIntlV8BreakIterator::New(isolate, locale, options, resolved));
582 : }
583 :
584 68 : RUNTIME_FUNCTION(Runtime_BreakIteratorAdoptText) {
585 34 : HandleScope scope(isolate);
586 :
587 : DCHECK_EQ(2, args.length());
588 :
589 68 : CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
590 68 : CONVERT_ARG_HANDLE_CHECKED(String, text, 1);
591 :
592 : Handle<JSIntlV8BreakIterator> br(
593 : reinterpret_cast<JSIntlV8BreakIterator*>(*break_iterator_holder));
594 34 : icu::BreakIterator* break_iterator = br->break_iterator();
595 34 : CHECK_NOT_NULL(break_iterator);
596 :
597 34 : icu::UnicodeString* u_text = br->unicode_string();
598 34 : delete u_text;
599 :
600 34 : int length = text->length();
601 34 : text = String::Flatten(text);
602 : DisallowHeapAllocation no_gc;
603 34 : String::FlatContent flat = text->GetFlatContent();
604 68 : std::unique_ptr<uc16[]> sap;
605 34 : const UChar* text_value = GetUCharBufferFromFlat(flat, &sap, length);
606 34 : u_text = new icu::UnicodeString(text_value, length);
607 34 : br->set_unicode_string(u_text);
608 :
609 34 : break_iterator->setText(*u_text);
610 :
611 68 : return isolate->heap()->undefined_value();
612 : }
613 :
614 56 : RUNTIME_FUNCTION(Runtime_BreakIteratorFirst) {
615 28 : HandleScope scope(isolate);
616 :
617 : DCHECK_EQ(1, args.length());
618 :
619 56 : CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
620 :
621 : Handle<JSIntlV8BreakIterator> br(
622 : reinterpret_cast<JSIntlV8BreakIterator*>(*break_iterator_holder));
623 28 : icu::BreakIterator* break_iterator = br->break_iterator();
624 28 : CHECK_NOT_NULL(break_iterator);
625 :
626 56 : return *isolate->factory()->NewNumberFromInt(break_iterator->first());
627 : }
628 :
629 948 : RUNTIME_FUNCTION(Runtime_BreakIteratorNext) {
630 474 : HandleScope scope(isolate);
631 :
632 : DCHECK_EQ(1, args.length());
633 :
634 948 : CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
635 :
636 : Handle<JSIntlV8BreakIterator> br(
637 : reinterpret_cast<JSIntlV8BreakIterator*>(*break_iterator_holder));
638 474 : icu::BreakIterator* break_iterator = br->break_iterator();
639 474 : CHECK_NOT_NULL(break_iterator);
640 :
641 948 : return *isolate->factory()->NewNumberFromInt(break_iterator->next());
642 : }
643 :
644 0 : RUNTIME_FUNCTION(Runtime_BreakIteratorCurrent) {
645 0 : HandleScope scope(isolate);
646 :
647 : DCHECK_EQ(1, args.length());
648 :
649 0 : CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
650 :
651 : Handle<JSIntlV8BreakIterator> br(
652 : reinterpret_cast<JSIntlV8BreakIterator*>(*break_iterator_holder));
653 0 : icu::BreakIterator* break_iterator = br->break_iterator();
654 0 : CHECK_NOT_NULL(break_iterator);
655 :
656 0 : return *isolate->factory()->NewNumberFromInt(break_iterator->current());
657 : }
658 :
659 868 : RUNTIME_FUNCTION(Runtime_BreakIteratorBreakType) {
660 434 : HandleScope scope(isolate);
661 :
662 : DCHECK_EQ(1, args.length());
663 :
664 868 : CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
665 :
666 : Handle<JSIntlV8BreakIterator> br(
667 : reinterpret_cast<JSIntlV8BreakIterator*>(*break_iterator_holder));
668 434 : icu::BreakIterator* break_iterator = br->break_iterator();
669 434 : CHECK_NOT_NULL(break_iterator);
670 :
671 : // TODO(cira): Remove cast once ICU fixes base BreakIterator class.
672 : icu::RuleBasedBreakIterator* rule_based_iterator =
673 : static_cast<icu::RuleBasedBreakIterator*>(break_iterator);
674 434 : int32_t status = rule_based_iterator->getRuleStatus();
675 : // Keep return values in sync with JavaScript BreakType enum.
676 434 : if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) {
677 420 : return *isolate->factory()->NewStringFromStaticChars("none");
678 224 : } else if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) {
679 0 : return isolate->heap()->number_string();
680 224 : } else if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) {
681 280 : return *isolate->factory()->NewStringFromStaticChars("letter");
682 84 : } else if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) {
683 0 : return *isolate->factory()->NewStringFromStaticChars("kana");
684 84 : } else if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) {
685 168 : return *isolate->factory()->NewStringFromStaticChars("ideo");
686 : } else {
687 0 : return *isolate->factory()->NewStringFromStaticChars("unknown");
688 434 : }
689 : }
690 :
691 4448 : RUNTIME_FUNCTION(Runtime_StringToLowerCaseIntl) {
692 2224 : HandleScope scope(isolate);
693 : DCHECK_EQ(args.length(), 1);
694 4448 : CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
695 2224 : s = String::Flatten(s);
696 2224 : return ConvertToLower(s, isolate);
697 : }
698 :
699 3048 : RUNTIME_FUNCTION(Runtime_StringToUpperCaseIntl) {
700 1524 : HandleScope scope(isolate);
701 : DCHECK_EQ(args.length(), 1);
702 3048 : CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
703 1524 : s = String::Flatten(s);
704 1524 : return ConvertToUpper(s, isolate);
705 : }
706 :
707 2324 : RUNTIME_FUNCTION(Runtime_StringLocaleConvertCase) {
708 1162 : HandleScope scope(isolate);
709 : DCHECK_EQ(args.length(), 3);
710 2324 : CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
711 2324 : CONVERT_BOOLEAN_ARG_CHECKED(is_upper, 1);
712 2324 : CONVERT_ARG_HANDLE_CHECKED(String, lang_arg, 2);
713 :
714 : // Primary language tag can be up to 8 characters long in theory.
715 : // https://tools.ietf.org/html/bcp47#section-2.2.1
716 : DCHECK(lang_arg->length() <= 8);
717 1162 : lang_arg = String::Flatten(lang_arg);
718 1162 : s = String::Flatten(s);
719 :
720 : // All the languages requiring special-handling have two-letter codes.
721 1162 : if (V8_UNLIKELY(lang_arg->length() > 2))
722 182 : return ConvertCase(s, is_upper, isolate);
723 :
724 : char c1, c2;
725 : {
726 : DisallowHeapAllocation no_gc;
727 980 : String::FlatContent lang = lang_arg->GetFlatContent();
728 980 : c1 = lang.Get(0);
729 980 : c2 = lang.Get(1);
730 : }
731 : // TODO(jshin): Consider adding a fast path for ASCII or Latin-1. The fastpath
732 : // in the root locale needs to be adjusted for az, lt and tr because even case
733 : // mapping of ASCII range characters are different in those locales.
734 : // Greek (el) does not require any adjustment.
735 980 : if (V8_UNLIKELY(c1 == 't' && c2 == 'r'))
736 168 : return LocaleConvertCase(s, isolate, is_upper, "tr");
737 812 : if (V8_UNLIKELY(c1 == 'e' && c2 == 'l'))
738 350 : return LocaleConvertCase(s, isolate, is_upper, "el");
739 462 : if (V8_UNLIKELY(c1 == 'l' && c2 == 't'))
740 28 : return LocaleConvertCase(s, isolate, is_upper, "lt");
741 434 : if (V8_UNLIKELY(c1 == 'a' && c2 == 'z'))
742 56 : return LocaleConvertCase(s, isolate, is_upper, "az");
743 :
744 378 : return ConvertCase(s, is_upper, isolate);
745 : }
746 :
747 570138 : RUNTIME_FUNCTION(Runtime_DateCacheVersion) {
748 114008 : HandleScope scope(isolate);
749 : DCHECK_EQ(0, args.length());
750 114008 : if (isolate->serializer_enabled()) return isolate->heap()->undefined_value();
751 114008 : if (!isolate->eternal_handles()->Exists(EternalHandles::DATE_CACHE_VERSION)) {
752 : Handle<FixedArray> date_cache_version =
753 98 : isolate->factory()->NewFixedArray(1, TENURED);
754 98 : date_cache_version->set(0, Smi::kZero);
755 : isolate->eternal_handles()->CreateSingleton(
756 98 : isolate, *date_cache_version, EternalHandles::DATE_CACHE_VERSION);
757 : }
758 : Handle<FixedArray> date_cache_version =
759 : Handle<FixedArray>::cast(isolate->eternal_handles()->GetSingleton(
760 114008 : EternalHandles::DATE_CACHE_VERSION));
761 114008 : return date_cache_version->get(0);
762 : }
763 :
764 : } // namespace internal
765 : } // namespace v8
|