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 <cmath>
12 : #include <memory>
13 :
14 : #include "src/api-natives.h"
15 : #include "src/api.h"
16 : #include "src/arguments.h"
17 : #include "src/factory.h"
18 : #include "src/intl.h"
19 : #include "src/isolate-inl.h"
20 : #include "src/messages.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/plurrule.h"
39 : #include "unicode/rbbi.h"
40 : #include "unicode/smpdtfmt.h"
41 : #include "unicode/timezone.h"
42 : #include "unicode/translit.h"
43 : #include "unicode/uchar.h"
44 : #include "unicode/ucol.h"
45 : #include "unicode/ucurr.h"
46 : #include "unicode/uloc.h"
47 : #include "unicode/unistr.h"
48 : #include "unicode/unum.h"
49 : #include "unicode/uvernum.h"
50 : #include "unicode/uversion.h"
51 :
52 : #if U_ICU_VERSION_MAJOR_NUM >= 59
53 : #include "unicode/char16ptr.h"
54 : #endif
55 :
56 : namespace v8 {
57 : namespace internal {
58 :
59 : // ECMA 402 6.2.3
60 1688 : RUNTIME_FUNCTION(Runtime_CanonicalizeLanguageTag) {
61 844 : HandleScope scope(isolate);
62 : v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
63 :
64 844 : Factory* factory = isolate->factory();
65 :
66 : DCHECK_EQ(1, args.length());
67 1688 : CONVERT_ARG_HANDLE_CHECKED(String, locale_id_str, 0);
68 :
69 : v8::String::Utf8Value locale_id(v8_isolate,
70 2532 : v8::Utils::ToLocal(locale_id_str));
71 :
72 : // TODO(jshin): uloc_{for,to}TanguageTag can fail even for a structually valid
73 : // language tag if it's too long (much longer than 100 chars). Even if we
74 : // allocate a longer buffer, ICU will still fail if it's too long. Either
75 : // propose to Ecma 402 to put a limit on the locale length or change ICU to
76 : // handle long locale names better. See
77 : // https://ssl.icu-project.org/trac/ticket/13417 .
78 :
79 : // Return value which denotes invalid language tag.
80 : const char* const kInvalidTag = "invalid-tag";
81 :
82 844 : UErrorCode error = U_ZERO_ERROR;
83 : char icu_result[ULOC_FULLNAME_CAPACITY];
84 844 : uloc_forLanguageTag(*locale_id, icu_result, ULOC_FULLNAME_CAPACITY, nullptr,
85 844 : &error);
86 844 : if (U_FAILURE(error) || error == U_STRING_NOT_TERMINATED_WARNING) {
87 36 : return *factory->NewStringFromAsciiChecked(kInvalidTag);
88 : }
89 :
90 : char result[ULOC_FULLNAME_CAPACITY];
91 :
92 : // Force strict BCP47 rules.
93 826 : uloc_toLanguageTag(icu_result, result, ULOC_FULLNAME_CAPACITY, TRUE, &error);
94 :
95 826 : if (U_FAILURE(error) || error == U_STRING_NOT_TERMINATED_WARNING) {
96 18 : return *factory->NewStringFromAsciiChecked(kInvalidTag);
97 : }
98 :
99 2478 : return *factory->NewStringFromAsciiChecked(result);
100 : }
101 :
102 1568 : RUNTIME_FUNCTION(Runtime_AvailableLocalesOf) {
103 784 : HandleScope scope(isolate);
104 784 : Factory* factory = isolate->factory();
105 :
106 : DCHECK_EQ(1, args.length());
107 1568 : CONVERT_ARG_HANDLE_CHECKED(String, service, 0);
108 :
109 : const icu::Locale* available_locales = nullptr;
110 784 : int32_t count = 0;
111 :
112 1568 : if (service->IsUtf8EqualTo(CStrVector("collator"))) {
113 195 : available_locales = icu::Collator::getAvailableLocales(count);
114 1178 : } else if (service->IsUtf8EqualTo(CStrVector("numberformat"))) {
115 213 : available_locales = icu::NumberFormat::getAvailableLocales(count);
116 752 : } else if (service->IsUtf8EqualTo(CStrVector("dateformat"))) {
117 289 : available_locales = icu::DateFormat::getAvailableLocales(count);
118 174 : } else if (service->IsUtf8EqualTo(CStrVector("breakiterator"))) {
119 78 : available_locales = icu::BreakIterator::getAvailableLocales(count);
120 18 : } else if (service->IsUtf8EqualTo(CStrVector("pluralrules"))) {
121 : // TODO(littledan): For PluralRules, filter out locales that
122 : // don't support PluralRules.
123 : // PluralRules is missing an appropriate getAvailableLocales method,
124 : // so we should filter from all locales, but it's not clear how; see
125 : // https://ssl.icu-project.org/trac/ticket/12756
126 9 : available_locales = icu::Locale::getAvailableLocales(count);
127 : } else {
128 0 : UNREACHABLE();
129 : }
130 :
131 784 : UErrorCode error = U_ZERO_ERROR;
132 : char result[ULOC_FULLNAME_CAPACITY];
133 784 : Handle<JSObject> locales = factory->NewJSObject(isolate->object_function());
134 :
135 110664 : for (int32_t i = 0; i < count; ++i) {
136 110664 : const char* icu_name = available_locales[i].getName();
137 :
138 110664 : error = U_ZERO_ERROR;
139 : // No need to force strict BCP47 rules.
140 110664 : uloc_toLanguageTag(icu_name, result, ULOC_FULLNAME_CAPACITY, FALSE, &error);
141 110664 : if (U_FAILURE(error) || error == U_STRING_NOT_TERMINATED_WARNING) {
142 : // This shouldn't happen, but lets not break the user.
143 : continue;
144 : }
145 :
146 221328 : RETURN_FAILURE_ON_EXCEPTION(
147 : isolate, JSObject::SetOwnPropertyIgnoreAttributes(
148 : locales, factory->NewStringFromAsciiChecked(result),
149 : factory->NewNumber(i), NONE));
150 : }
151 :
152 784 : return *locales;
153 : }
154 :
155 1112 : RUNTIME_FUNCTION(Runtime_GetDefaultICULocale) {
156 556 : HandleScope scope(isolate);
157 556 : Factory* factory = isolate->factory();
158 :
159 : DCHECK_EQ(0, args.length());
160 :
161 1112 : icu::Locale default_locale;
162 :
163 : // Translate ICU's fallback locale to a well-known locale.
164 556 : if (strcmp(default_locale.getName(), "en_US_POSIX") == 0) {
165 0 : return *factory->NewStringFromStaticChars("en-US");
166 : }
167 :
168 : // Set the locale
169 : char result[ULOC_FULLNAME_CAPACITY];
170 556 : UErrorCode status = U_ZERO_ERROR;
171 : uloc_toLanguageTag(default_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
172 556 : FALSE, &status);
173 556 : if (U_SUCCESS(status)) {
174 1112 : return *factory->NewStringFromAsciiChecked(result);
175 : }
176 :
177 556 : return *factory->NewStringFromStaticChars("und");
178 : }
179 :
180 0 : RUNTIME_FUNCTION(Runtime_IsInitializedIntlObject) {
181 0 : HandleScope scope(isolate);
182 :
183 : DCHECK_EQ(1, args.length());
184 :
185 0 : CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
186 :
187 0 : if (!input->IsJSObject()) return isolate->heap()->false_value();
188 0 : Handle<JSObject> obj = Handle<JSObject>::cast(input);
189 :
190 0 : Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
191 0 : Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
192 0 : return isolate->heap()->ToBoolean(!tag->IsUndefined(isolate));
193 : }
194 :
195 24288 : RUNTIME_FUNCTION(Runtime_IsInitializedIntlObjectOfType) {
196 12144 : HandleScope scope(isolate);
197 :
198 : DCHECK_EQ(2, args.length());
199 :
200 12144 : CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
201 24288 : CONVERT_ARG_HANDLE_CHECKED(String, expected_type, 1);
202 :
203 12144 : if (!input->IsJSObject()) return isolate->heap()->false_value();
204 12098 : Handle<JSObject> obj = Handle<JSObject>::cast(input);
205 :
206 12098 : Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
207 12098 : Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
208 23836 : return isolate->heap()->ToBoolean(tag->IsString() &&
209 23836 : String::cast(*tag)->Equals(*expected_type));
210 : }
211 :
212 22572 : RUNTIME_FUNCTION(Runtime_MarkAsInitializedIntlObjectOfType) {
213 11286 : HandleScope scope(isolate);
214 :
215 : DCHECK_EQ(2, args.length());
216 :
217 22572 : CONVERT_ARG_HANDLE_CHECKED(JSObject, input, 0);
218 22572 : CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
219 :
220 11286 : Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
221 11286 : JSObject::SetProperty(input, marker, type, LanguageMode::kStrict).Assert();
222 :
223 11286 : return isolate->heap()->undefined_value();
224 : }
225 :
226 2541 : RUNTIME_FUNCTION(Runtime_CreateDateTimeFormat) {
227 847 : HandleScope scope(isolate);
228 :
229 : DCHECK_EQ(3, args.length());
230 :
231 1694 : CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
232 1694 : CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
233 1694 : CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
234 :
235 : Handle<JSFunction> constructor(
236 2541 : isolate->native_context()->intl_date_time_format_function());
237 :
238 : Handle<JSObject> local_object;
239 2541 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
240 : JSObject::New(constructor, constructor));
241 :
242 : // Set date time formatter as embedder field of the resulting JS object.
243 : icu::SimpleDateFormat* date_format =
244 847 : DateFormat::InitializeDateTimeFormat(isolate, locale, options, resolved);
245 :
246 847 : if (!date_format) return isolate->ThrowIllegalOperation();
247 :
248 847 : local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(date_format));
249 :
250 : // Make object handle weak so we can delete the data format once GC kicks in.
251 847 : Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
252 : GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
253 : DateFormat::DeleteDateFormat,
254 847 : WeakCallbackType::kInternalFields);
255 847 : return *local_object;
256 : }
257 :
258 890 : RUNTIME_FUNCTION(Runtime_InternalDateFormat) {
259 445 : HandleScope scope(isolate);
260 :
261 : DCHECK_EQ(2, args.length());
262 :
263 890 : CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
264 890 : CONVERT_NUMBER_ARG_HANDLE_CHECKED(date, 1);
265 :
266 445 : double date_value = date->Number();
267 : // Check for +-Infinity and Nan
268 445 : if (!std::isfinite(date_value)) {
269 108 : THROW_NEW_ERROR_RETURN_FAILURE(
270 : isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
271 : }
272 :
273 : icu::SimpleDateFormat* date_format =
274 391 : DateFormat::UnpackDateFormat(isolate, date_format_holder);
275 391 : CHECK_NOT_NULL(date_format);
276 :
277 782 : icu::UnicodeString result;
278 391 : date_format->format(date_value, result);
279 :
280 782 : RETURN_RESULT_OR_FAILURE(
281 : isolate, isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
282 : reinterpret_cast<const uint16_t*>(result.getBuffer()),
283 445 : result.length())));
284 : }
285 :
286 : namespace {
287 : // The list comes from third_party/icu/source/i18n/unicode/udat.h.
288 : // They're mapped to DateTimeFormat components listed at
289 : // https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts .
290 :
291 270 : Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
292 270 : switch (field_id) {
293 : case -1:
294 : return isolate->factory()->literal_string();
295 : case UDAT_YEAR_FIELD:
296 : case UDAT_EXTENDED_YEAR_FIELD:
297 : case UDAT_YEAR_NAME_FIELD:
298 : return isolate->factory()->year_string();
299 : case UDAT_MONTH_FIELD:
300 : case UDAT_STANDALONE_MONTH_FIELD:
301 : return isolate->factory()->month_string();
302 : case UDAT_DATE_FIELD:
303 : return isolate->factory()->day_string();
304 : case UDAT_HOUR_OF_DAY1_FIELD:
305 : case UDAT_HOUR_OF_DAY0_FIELD:
306 : case UDAT_HOUR1_FIELD:
307 : case UDAT_HOUR0_FIELD:
308 : return isolate->factory()->hour_string();
309 : case UDAT_MINUTE_FIELD:
310 : return isolate->factory()->minute_string();
311 : case UDAT_SECOND_FIELD:
312 : return isolate->factory()->second_string();
313 : case UDAT_DAY_OF_WEEK_FIELD:
314 : case UDAT_DOW_LOCAL_FIELD:
315 : case UDAT_STANDALONE_DAY_FIELD:
316 : return isolate->factory()->weekday_string();
317 : case UDAT_AM_PM_FIELD:
318 : return isolate->factory()->dayperiod_string();
319 : case UDAT_TIMEZONE_FIELD:
320 : case UDAT_TIMEZONE_RFC_FIELD:
321 : case UDAT_TIMEZONE_GENERIC_FIELD:
322 : case UDAT_TIMEZONE_SPECIAL_FIELD:
323 : case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
324 : case UDAT_TIMEZONE_ISO_FIELD:
325 : case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
326 : return isolate->factory()->timeZoneName_string();
327 : case UDAT_ERA_FIELD:
328 : return isolate->factory()->era_string();
329 : default:
330 : // Other UDAT_*_FIELD's cannot show up because there is no way to specify
331 : // them via options of Intl.DateTimeFormat.
332 0 : UNREACHABLE();
333 : // To prevent MSVC from issuing C4715 warning.
334 : return Handle<String>();
335 : }
336 : }
337 :
338 270 : bool AddElement(Handle<JSArray> array, int index, int32_t field_id,
339 : const icu::UnicodeString& formatted, int32_t begin, int32_t end,
340 : Isolate* isolate) {
341 : HandleScope scope(isolate);
342 : Factory* factory = isolate->factory();
343 270 : Handle<JSObject> element = factory->NewJSObject(isolate->object_function());
344 270 : Handle<String> value = IcuDateFieldIdToDateType(field_id, isolate);
345 270 : JSObject::AddProperty(element, factory->type_string(), value, NONE);
346 :
347 270 : icu::UnicodeString field(formatted.tempSubStringBetween(begin, end));
348 810 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
349 : isolate, value,
350 : factory->NewStringFromTwoByte(Vector<const uint16_t>(
351 : reinterpret_cast<const uint16_t*>(field.getBuffer()),
352 : field.length())),
353 : false);
354 :
355 270 : JSObject::AddProperty(element, factory->value_string(), value, NONE);
356 810 : RETURN_ON_EXCEPTION_VALUE(
357 : isolate, JSObject::AddDataElement(array, index, element, NONE), false);
358 270 : return true;
359 : }
360 :
361 : } // namespace
362 :
363 144 : RUNTIME_FUNCTION(Runtime_InternalDateFormatToParts) {
364 72 : HandleScope scope(isolate);
365 72 : Factory* factory = isolate->factory();
366 :
367 : DCHECK_EQ(2, args.length());
368 :
369 144 : CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
370 144 : CONVERT_NUMBER_ARG_HANDLE_CHECKED(date, 1);
371 :
372 72 : double date_value = date->Number();
373 72 : if (!std::isfinite(date_value)) {
374 54 : THROW_NEW_ERROR_RETURN_FAILURE(
375 : isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
376 : }
377 :
378 : icu::SimpleDateFormat* date_format =
379 45 : DateFormat::UnpackDateFormat(isolate, date_format_holder);
380 45 : CHECK_NOT_NULL(date_format);
381 :
382 90 : icu::UnicodeString formatted;
383 90 : icu::FieldPositionIterator fp_iter;
384 90 : icu::FieldPosition fp;
385 45 : UErrorCode status = U_ZERO_ERROR;
386 45 : date_format->format(date_value, formatted, &fp_iter, status);
387 45 : if (U_FAILURE(status)) return isolate->heap()->undefined_value();
388 :
389 45 : Handle<JSArray> result = factory->NewJSArray(0);
390 45 : int32_t length = formatted.length();
391 45 : if (length == 0) return *result;
392 :
393 : int index = 0;
394 : int32_t previous_end_pos = 0;
395 207 : while (fp_iter.next(fp)) {
396 162 : int32_t begin_pos = fp.getBeginIndex();
397 162 : int32_t end_pos = fp.getEndIndex();
398 :
399 162 : if (previous_end_pos < begin_pos) {
400 108 : if (!AddElement(result, index, -1, formatted, previous_end_pos, begin_pos,
401 108 : isolate)) {
402 0 : return isolate->heap()->undefined_value();
403 : }
404 108 : ++index;
405 : }
406 162 : if (!AddElement(result, index, fp.getField(), formatted, begin_pos, end_pos,
407 162 : isolate)) {
408 0 : return isolate->heap()->undefined_value();
409 : }
410 : previous_end_pos = end_pos;
411 162 : ++index;
412 : }
413 45 : if (previous_end_pos < length) {
414 0 : if (!AddElement(result, index, -1, formatted, previous_end_pos, length,
415 0 : isolate)) {
416 0 : return isolate->heap()->undefined_value();
417 : }
418 : }
419 45 : JSObject::ValidateElements(*result);
420 72 : return *result;
421 : }
422 :
423 1827 : RUNTIME_FUNCTION(Runtime_CreateNumberFormat) {
424 609 : HandleScope scope(isolate);
425 :
426 : DCHECK_EQ(3, args.length());
427 :
428 1218 : CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
429 1218 : CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
430 1218 : CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
431 :
432 : Handle<JSFunction> constructor(
433 1827 : isolate->native_context()->intl_number_format_function());
434 :
435 : Handle<JSObject> local_object;
436 1827 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
437 : JSObject::New(constructor, constructor));
438 :
439 : // Set number formatter as embedder field of the resulting JS object.
440 : icu::DecimalFormat* number_format =
441 609 : NumberFormat::InitializeNumberFormat(isolate, locale, options, resolved);
442 :
443 609 : if (!number_format) return isolate->ThrowIllegalOperation();
444 :
445 609 : local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(number_format));
446 :
447 609 : Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
448 : GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
449 : NumberFormat::DeleteNumberFormat,
450 609 : WeakCallbackType::kInternalFields);
451 609 : return *local_object;
452 : }
453 :
454 3946 : RUNTIME_FUNCTION(Runtime_InternalNumberFormat) {
455 1973 : HandleScope scope(isolate);
456 :
457 : DCHECK_EQ(2, args.length());
458 :
459 3946 : CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0);
460 1973 : CONVERT_ARG_HANDLE_CHECKED(Object, number, 1);
461 :
462 : Handle<Object> value;
463 3946 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(number));
464 :
465 : icu::DecimalFormat* number_format =
466 1973 : NumberFormat::UnpackNumberFormat(isolate, number_format_holder);
467 1973 : CHECK_NOT_NULL(number_format);
468 :
469 3946 : icu::UnicodeString result;
470 1973 : number_format->format(value->Number(), result);
471 :
472 3946 : RETURN_RESULT_OR_FAILURE(
473 : isolate, isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
474 : reinterpret_cast<const uint16_t*>(result.getBuffer()),
475 1973 : result.length())));
476 : }
477 :
478 234 : RUNTIME_FUNCTION(Runtime_CurrencyDigits) {
479 : DCHECK_EQ(1, args.length());
480 : v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
481 :
482 234 : CONVERT_ARG_HANDLE_CHECKED(String, currency, 0);
483 :
484 : // TODO(littledan): Avoid transcoding the string twice
485 : v8::String::Utf8Value currency_string(v8_isolate,
486 234 : v8::Utils::ToLocal(currency));
487 : icu::UnicodeString currency_icu =
488 234 : icu::UnicodeString::fromUTF8(*currency_string);
489 :
490 : DisallowHeapAllocation no_gc;
491 117 : UErrorCode status = U_ZERO_ERROR;
492 : #if U_ICU_VERSION_MAJOR_NUM >= 59
493 : uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
494 117 : icu::toUCharPtr(currency_icu.getTerminatedBuffer()), &status);
495 : #else
496 : uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
497 : currency_icu.getTerminatedBuffer(), &status);
498 : #endif
499 : // For missing currency codes, default to the most common, 2
500 117 : if (!U_SUCCESS(status)) fraction_digits = 2;
501 234 : return Smi::FromInt(fraction_digits);
502 : }
503 :
504 29148 : RUNTIME_FUNCTION(Runtime_CreateCollator) {
505 9716 : HandleScope scope(isolate);
506 :
507 : DCHECK_EQ(3, args.length());
508 :
509 19432 : CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
510 19432 : CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
511 19432 : CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
512 :
513 : Handle<JSFunction> constructor(
514 29148 : isolate->native_context()->intl_collator_function());
515 :
516 : Handle<JSObject> local_object;
517 29148 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
518 : JSObject::New(constructor, constructor));
519 :
520 : // Set collator as embedder field of the resulting JS object.
521 : icu::Collator* collator =
522 9716 : Collator::InitializeCollator(isolate, locale, options, resolved);
523 :
524 9716 : if (!collator) return isolate->ThrowIllegalOperation();
525 :
526 9716 : local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(collator));
527 :
528 9716 : Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
529 : GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
530 : Collator::DeleteCollator,
531 9716 : WeakCallbackType::kInternalFields);
532 9716 : return *local_object;
533 : }
534 :
535 183400 : RUNTIME_FUNCTION(Runtime_InternalCompare) {
536 91700 : HandleScope scope(isolate);
537 :
538 : DCHECK_EQ(3, args.length());
539 :
540 183400 : CONVERT_ARG_HANDLE_CHECKED(JSObject, collator_holder, 0);
541 183400 : CONVERT_ARG_HANDLE_CHECKED(String, string1, 1);
542 183400 : CONVERT_ARG_HANDLE_CHECKED(String, string2, 2);
543 :
544 91700 : icu::Collator* collator = Collator::UnpackCollator(isolate, collator_holder);
545 91700 : CHECK_NOT_NULL(collator);
546 :
547 91700 : string1 = String::Flatten(string1);
548 91700 : string2 = String::Flatten(string2);
549 :
550 : UCollationResult result;
551 91700 : UErrorCode status = U_ZERO_ERROR;
552 : {
553 : DisallowHeapAllocation no_gc;
554 91700 : int32_t length1 = string1->length();
555 91700 : int32_t length2 = string2->length();
556 91700 : String::FlatContent flat1 = string1->GetFlatContent();
557 91700 : String::FlatContent flat2 = string2->GetFlatContent();
558 91700 : std::unique_ptr<uc16[]> sap1;
559 183400 : std::unique_ptr<uc16[]> sap2;
560 : icu::UnicodeString string_val1(
561 183400 : FALSE, GetUCharBufferFromFlat(flat1, &sap1, length1), length1);
562 : icu::UnicodeString string_val2(
563 183400 : FALSE, GetUCharBufferFromFlat(flat2, &sap2, length2), length2);
564 183400 : result = collator->compare(string_val1, string_val2, status);
565 : }
566 91700 : if (U_FAILURE(status)) return isolate->ThrowIllegalOperation();
567 :
568 183400 : return *isolate->factory()->NewNumberFromInt(result);
569 : }
570 :
571 81 : RUNTIME_FUNCTION(Runtime_CreatePluralRules) {
572 27 : HandleScope scope(isolate);
573 :
574 : DCHECK_EQ(3, args.length());
575 :
576 54 : CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
577 54 : CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
578 54 : CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
579 :
580 : Handle<JSFunction> constructor(
581 81 : isolate->native_context()->intl_plural_rules_function());
582 :
583 : Handle<JSObject> local_object;
584 81 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
585 : JSObject::New(constructor, constructor));
586 :
587 : // Set pluralRules as internal field of the resulting JS object.
588 : icu::PluralRules* plural_rules;
589 : icu::DecimalFormat* decimal_format;
590 : bool success = PluralRules::InitializePluralRules(
591 27 : isolate, locale, options, resolved, &plural_rules, &decimal_format);
592 :
593 27 : if (!success) return isolate->ThrowIllegalOperation();
594 :
595 54 : local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(plural_rules));
596 54 : local_object->SetEmbedderField(1, reinterpret_cast<Smi*>(decimal_format));
597 :
598 27 : Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
599 : GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
600 : PluralRules::DeletePluralRules,
601 27 : WeakCallbackType::kInternalFields);
602 27 : return *local_object;
603 : }
604 :
605 360 : RUNTIME_FUNCTION(Runtime_PluralRulesSelect) {
606 180 : HandleScope scope(isolate);
607 :
608 : DCHECK_EQ(2, args.length());
609 :
610 360 : CONVERT_ARG_HANDLE_CHECKED(JSObject, plural_rules_holder, 0);
611 180 : CONVERT_ARG_HANDLE_CHECKED(Object, number, 1);
612 :
613 : icu::PluralRules* plural_rules =
614 180 : PluralRules::UnpackPluralRules(isolate, plural_rules_holder);
615 180 : CHECK_NOT_NULL(plural_rules);
616 :
617 : icu::DecimalFormat* number_format =
618 180 : PluralRules::UnpackNumberFormat(isolate, plural_rules_holder);
619 180 : CHECK_NOT_NULL(number_format);
620 :
621 : // Currently, PluralRules doesn't implement all the options for rounding that
622 : // the Intl spec provides; format and parse the number to round to the
623 : // appropriate amount, then apply PluralRules.
624 : //
625 : // TODO(littledan): If a future ICU version supports an extended API to avoid
626 : // this step, then switch to that API. Bug thread:
627 : // http://bugs.icu-project.org/trac/ticket/12763
628 360 : icu::UnicodeString rounded_string;
629 180 : number_format->format(number->Number(), rounded_string);
630 :
631 360 : icu::Formattable formattable;
632 180 : UErrorCode status = U_ZERO_ERROR;
633 180 : number_format->parse(rounded_string, formattable, status);
634 180 : if (!U_SUCCESS(status)) return isolate->ThrowIllegalOperation();
635 :
636 180 : double rounded = formattable.getDouble(status);
637 180 : if (!U_SUCCESS(status)) return isolate->ThrowIllegalOperation();
638 :
639 360 : icu::UnicodeString result = plural_rules->select(rounded);
640 : return *isolate->factory()
641 : ->NewStringFromTwoByte(Vector<const uint16_t>(
642 : reinterpret_cast<const uint16_t*>(
643 180 : icu::toUCharPtr(result.getBuffer())),
644 540 : result.length()))
645 540 : .ToHandleChecked();
646 : }
647 :
648 345 : RUNTIME_FUNCTION(Runtime_CreateBreakIterator) {
649 115 : HandleScope scope(isolate);
650 :
651 : DCHECK_EQ(3, args.length());
652 :
653 230 : CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
654 230 : CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
655 230 : CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
656 :
657 : Handle<JSFunction> constructor(
658 345 : isolate->native_context()->intl_v8_break_iterator_function());
659 :
660 : Handle<JSObject> local_object;
661 345 : ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
662 : JSObject::New(constructor, constructor));
663 :
664 : // Set break iterator as embedder field of the resulting JS object.
665 : icu::BreakIterator* break_iterator = V8BreakIterator::InitializeBreakIterator(
666 115 : isolate, locale, options, resolved);
667 :
668 115 : if (!break_iterator) return isolate->ThrowIllegalOperation();
669 :
670 115 : local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(break_iterator));
671 : // Make sure that the pointer to adopted text is nullptr.
672 115 : local_object->SetEmbedderField(1, static_cast<Smi*>(nullptr));
673 :
674 : // Make object handle weak so we can delete the break iterator once GC kicks
675 : // in.
676 115 : Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
677 : GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
678 : V8BreakIterator::DeleteBreakIterator,
679 115 : WeakCallbackType::kInternalFields);
680 115 : return *local_object;
681 : }
682 :
683 46 : RUNTIME_FUNCTION(Runtime_BreakIteratorAdoptText) {
684 23 : HandleScope scope(isolate);
685 :
686 : DCHECK_EQ(2, args.length());
687 :
688 46 : CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
689 46 : CONVERT_ARG_HANDLE_CHECKED(String, text, 1);
690 :
691 : icu::BreakIterator* break_iterator =
692 23 : V8BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
693 23 : CHECK_NOT_NULL(break_iterator);
694 :
695 : icu::UnicodeString* u_text = reinterpret_cast<icu::UnicodeString*>(
696 23 : break_iterator_holder->GetEmbedderField(1));
697 23 : delete u_text;
698 :
699 23 : int length = text->length();
700 23 : text = String::Flatten(text);
701 : DisallowHeapAllocation no_gc;
702 23 : String::FlatContent flat = text->GetFlatContent();
703 46 : std::unique_ptr<uc16[]> sap;
704 23 : const UChar* text_value = GetUCharBufferFromFlat(flat, &sap, length);
705 23 : u_text = new icu::UnicodeString(text_value, length);
706 23 : break_iterator_holder->SetEmbedderField(1, reinterpret_cast<Smi*>(u_text));
707 :
708 23 : break_iterator->setText(*u_text);
709 :
710 46 : return isolate->heap()->undefined_value();
711 : }
712 :
713 36 : RUNTIME_FUNCTION(Runtime_BreakIteratorFirst) {
714 18 : HandleScope scope(isolate);
715 :
716 : DCHECK_EQ(1, args.length());
717 :
718 36 : CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
719 :
720 : icu::BreakIterator* break_iterator =
721 18 : V8BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
722 18 : CHECK_NOT_NULL(break_iterator);
723 :
724 36 : return *isolate->factory()->NewNumberFromInt(break_iterator->first());
725 : }
726 :
727 614 : RUNTIME_FUNCTION(Runtime_BreakIteratorNext) {
728 307 : HandleScope scope(isolate);
729 :
730 : DCHECK_EQ(1, args.length());
731 :
732 614 : CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
733 :
734 : icu::BreakIterator* break_iterator =
735 307 : V8BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
736 307 : CHECK_NOT_NULL(break_iterator);
737 :
738 614 : return *isolate->factory()->NewNumberFromInt(break_iterator->next());
739 : }
740 :
741 0 : RUNTIME_FUNCTION(Runtime_BreakIteratorCurrent) {
742 0 : HandleScope scope(isolate);
743 :
744 : DCHECK_EQ(1, args.length());
745 :
746 0 : CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
747 :
748 : icu::BreakIterator* break_iterator =
749 0 : V8BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
750 0 : CHECK_NOT_NULL(break_iterator);
751 :
752 0 : return *isolate->factory()->NewNumberFromInt(break_iterator->current());
753 : }
754 :
755 558 : RUNTIME_FUNCTION(Runtime_BreakIteratorBreakType) {
756 279 : HandleScope scope(isolate);
757 :
758 : DCHECK_EQ(1, args.length());
759 :
760 558 : CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
761 :
762 : icu::BreakIterator* break_iterator =
763 279 : V8BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
764 279 : CHECK_NOT_NULL(break_iterator);
765 :
766 : // TODO(cira): Remove cast once ICU fixes base BreakIterator class.
767 : icu::RuleBasedBreakIterator* rule_based_iterator =
768 : static_cast<icu::RuleBasedBreakIterator*>(break_iterator);
769 279 : int32_t status = rule_based_iterator->getRuleStatus();
770 : // Keep return values in sync with JavaScript BreakType enum.
771 279 : if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) {
772 270 : return *isolate->factory()->NewStringFromStaticChars("none");
773 144 : } else if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) {
774 0 : return isolate->heap()->number_string();
775 144 : } else if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) {
776 180 : return *isolate->factory()->NewStringFromStaticChars("letter");
777 54 : } else if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) {
778 0 : return *isolate->factory()->NewStringFromStaticChars("kana");
779 54 : } else if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) {
780 108 : return *isolate->factory()->NewStringFromStaticChars("ideo");
781 : } else {
782 0 : return *isolate->factory()->NewStringFromStaticChars("unknown");
783 279 : }
784 : }
785 :
786 9260 : RUNTIME_FUNCTION(Runtime_StringToLowerCaseIntl) {
787 4630 : HandleScope scope(isolate);
788 : DCHECK_EQ(args.length(), 1);
789 9260 : CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
790 4630 : s = String::Flatten(s);
791 4630 : return ConvertToLower(s, isolate);
792 : }
793 :
794 2594 : RUNTIME_FUNCTION(Runtime_StringToUpperCaseIntl) {
795 1297 : HandleScope scope(isolate);
796 : DCHECK_EQ(args.length(), 1);
797 2594 : CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
798 1297 : s = String::Flatten(s);
799 1297 : return ConvertToUpper(s, isolate);
800 : }
801 :
802 1530 : RUNTIME_FUNCTION(Runtime_StringLocaleConvertCase) {
803 765 : HandleScope scope(isolate);
804 : DCHECK_EQ(args.length(), 3);
805 1530 : CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
806 1530 : CONVERT_BOOLEAN_ARG_CHECKED(is_upper, 1);
807 1530 : CONVERT_ARG_HANDLE_CHECKED(String, lang_arg, 2);
808 :
809 : // Primary language tag can be up to 8 characters long in theory.
810 : // https://tools.ietf.org/html/bcp47#section-2.2.1
811 : DCHECK_LE(lang_arg->length(), 8);
812 765 : lang_arg = String::Flatten(lang_arg);
813 765 : s = String::Flatten(s);
814 :
815 : // All the languages requiring special-handling have two-letter codes.
816 : // Note that we have to check for '!= 2' here because private-use language
817 : // tags (x-foo) or grandfathered irregular tags (e.g. i-enochian) would have
818 : // only 'x' or 'i' when they get here.
819 765 : if (V8_UNLIKELY(lang_arg->length() != 2))
820 135 : return ConvertCase(s, is_upper, isolate);
821 :
822 : char c1, c2;
823 : {
824 : DisallowHeapAllocation no_gc;
825 630 : String::FlatContent lang = lang_arg->GetFlatContent();
826 630 : c1 = lang.Get(0);
827 630 : c2 = lang.Get(1);
828 : }
829 : // TODO(jshin): Consider adding a fast path for ASCII or Latin-1. The fastpath
830 : // in the root locale needs to be adjusted for az, lt and tr because even case
831 : // mapping of ASCII range characters are different in those locales.
832 : // Greek (el) does not require any adjustment.
833 630 : if (V8_UNLIKELY(c1 == 't' && c2 == 'r'))
834 108 : return LocaleConvertCase(s, isolate, is_upper, "tr");
835 522 : if (V8_UNLIKELY(c1 == 'e' && c2 == 'l'))
836 225 : return LocaleConvertCase(s, isolate, is_upper, "el");
837 297 : if (V8_UNLIKELY(c1 == 'l' && c2 == 't'))
838 18 : return LocaleConvertCase(s, isolate, is_upper, "lt");
839 279 : if (V8_UNLIKELY(c1 == 'a' && c2 == 'z'))
840 36 : return LocaleConvertCase(s, isolate, is_upper, "az");
841 :
842 243 : return ConvertCase(s, is_upper, isolate);
843 : }
844 :
845 366768 : RUNTIME_FUNCTION(Runtime_DateCacheVersion) {
846 73337 : HandleScope scope(isolate);
847 : DCHECK_EQ(0, args.length());
848 73337 : if (isolate->serializer_enabled()) return isolate->heap()->undefined_value();
849 73337 : if (!isolate->eternal_handles()->Exists(EternalHandles::DATE_CACHE_VERSION)) {
850 : Handle<FixedArray> date_cache_version =
851 83 : isolate->factory()->NewFixedArray(1, TENURED);
852 83 : date_cache_version->set(0, Smi::kZero);
853 : isolate->eternal_handles()->CreateSingleton(
854 83 : isolate, *date_cache_version, EternalHandles::DATE_CACHE_VERSION);
855 : }
856 : Handle<FixedArray> date_cache_version =
857 : Handle<FixedArray>::cast(isolate->eternal_handles()->GetSingleton(
858 73337 : EternalHandles::DATE_CACHE_VERSION));
859 73337 : return date_cache_version->get(0);
860 : }
861 :
862 : } // namespace internal
863 : } // namespace v8
|