runtime-intl.cc 28.9 KB
Newer Older
1 2 3 4
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6 7
#ifndef V8_INTL_SUPPORT
#error Internationalization is expected to be enabled.
#endif  // V8_INTL_SUPPORT
8

9
#include "src/runtime/runtime-utils.h"
10

11 12
#include <memory>

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.h"
21
#include "src/utils.h"
22 23 24 25 26 27 28 29 30 31

#include "unicode/brkiter.h"
#include "unicode/calendar.h"
#include "unicode/coll.h"
#include "unicode/curramt.h"
#include "unicode/datefmt.h"
#include "unicode/dcfmtsym.h"
#include "unicode/decimfmt.h"
#include "unicode/dtfmtsym.h"
#include "unicode/dtptngen.h"
32 33
#include "unicode/fieldpos.h"
#include "unicode/fpositer.h"
34 35 36 37 38 39
#include "unicode/locid.h"
#include "unicode/numfmt.h"
#include "unicode/numsys.h"
#include "unicode/rbbi.h"
#include "unicode/smpdtfmt.h"
#include "unicode/timezone.h"
40
#include "unicode/translit.h"
41 42 43 44
#include "unicode/uchar.h"
#include "unicode/ucol.h"
#include "unicode/ucurr.h"
#include "unicode/uloc.h"
45
#include "unicode/unistr.h"
46
#include "unicode/unum.h"
47
#include "unicode/uvernum.h"
48 49
#include "unicode/uversion.h"

50 51 52 53
#if U_ICU_VERSION_MAJOR_NUM >= 59
#include "unicode/char16ptr.h"
#endif

54 55 56
namespace v8 {
namespace internal {

57
// ECMA 402 6.2.3
58 59 60 61
RUNTIME_FUNCTION(Runtime_CanonicalizeLanguageTag) {
  HandleScope scope(isolate);
  Factory* factory = isolate->factory();

62
  DCHECK_EQ(1, args.length());
63 64 65 66 67
  CONVERT_ARG_HANDLE_CHECKED(String, locale_id_str, 0);

  v8::String::Utf8Value locale_id(v8::Utils::ToLocal(locale_id_str));

  // Return value which denotes invalid language tag.
68 69
  // TODO(jshin): Can uloc_{for,to}TanguageTag fail even for structually valid
  // language tags? If not, just add CHECK instead of returning 'invalid-tag'.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
  const char* const kInvalidTag = "invalid-tag";

  UErrorCode error = U_ZERO_ERROR;
  char icu_result[ULOC_FULLNAME_CAPACITY];
  int icu_length = 0;

  uloc_forLanguageTag(*locale_id, icu_result, ULOC_FULLNAME_CAPACITY,
                      &icu_length, &error);
  if (U_FAILURE(error) || icu_length == 0) {
    return *factory->NewStringFromAsciiChecked(kInvalidTag);
  }

  char result[ULOC_FULLNAME_CAPACITY];

  // Force strict BCP47 rules.
  uloc_toLanguageTag(icu_result, result, ULOC_FULLNAME_CAPACITY, TRUE, &error);

  if (U_FAILURE(error)) {
    return *factory->NewStringFromAsciiChecked(kInvalidTag);
  }

  return *factory->NewStringFromAsciiChecked(result);
}

RUNTIME_FUNCTION(Runtime_AvailableLocalesOf) {
  HandleScope scope(isolate);
  Factory* factory = isolate->factory();

98
  DCHECK_EQ(1, args.length());
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
  CONVERT_ARG_HANDLE_CHECKED(String, service, 0);

  const icu::Locale* available_locales = NULL;
  int32_t count = 0;

  if (service->IsUtf8EqualTo(CStrVector("collator"))) {
    available_locales = icu::Collator::getAvailableLocales(count);
  } else if (service->IsUtf8EqualTo(CStrVector("numberformat"))) {
    available_locales = icu::NumberFormat::getAvailableLocales(count);
  } else if (service->IsUtf8EqualTo(CStrVector("dateformat"))) {
    available_locales = icu::DateFormat::getAvailableLocales(count);
  } else if (service->IsUtf8EqualTo(CStrVector("breakiterator"))) {
    available_locales = icu::BreakIterator::getAvailableLocales(count);
  }

  UErrorCode error = U_ZERO_ERROR;
  char result[ULOC_FULLNAME_CAPACITY];
  Handle<JSObject> locales = factory->NewJSObject(isolate->object_function());

  for (int32_t i = 0; i < count; ++i) {
    const char* icu_name = available_locales[i].getName();

    error = U_ZERO_ERROR;
    // No need to force strict BCP47 rules.
    uloc_toLanguageTag(icu_name, result, ULOC_FULLNAME_CAPACITY, FALSE, &error);
    if (U_FAILURE(error)) {
      // This shouldn't happen, but lets not break the user.
      continue;
    }

    RETURN_FAILURE_ON_EXCEPTION(
        isolate, JSObject::SetOwnPropertyIgnoreAttributes(
                     locales, factory->NewStringFromAsciiChecked(result),
                     factory->NewNumber(i), NONE));
  }

  return *locales;
}

RUNTIME_FUNCTION(Runtime_GetDefaultICULocale) {
  HandleScope scope(isolate);
  Factory* factory = isolate->factory();

142
  DCHECK_EQ(0, args.length());
143 144 145

  icu::Locale default_locale;

146 147 148 149 150
  // Translate ICU's fallback locale to a well-known locale.
  if (strcmp(default_locale.getName(), "en_US_POSIX") == 0) {
    return *factory->NewStringFromStaticChars("en-US");
  }

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
  // Set the locale
  char result[ULOC_FULLNAME_CAPACITY];
  UErrorCode status = U_ZERO_ERROR;
  uloc_toLanguageTag(default_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
                     FALSE, &status);
  if (U_SUCCESS(status)) {
    return *factory->NewStringFromAsciiChecked(result);
  }

  return *factory->NewStringFromStaticChars("und");
}

RUNTIME_FUNCTION(Runtime_GetLanguageTagVariants) {
  HandleScope scope(isolate);
  Factory* factory = isolate->factory();

167
  DCHECK_EQ(1, args.length());
168 169 170 171 172 173

  CONVERT_ARG_HANDLE_CHECKED(JSArray, input, 0);

  uint32_t length = static_cast<uint32_t>(input->length()->Number());
  // Set some limit to prevent fuzz tests from going OOM.
  // Can be bumped when callers' requirements change.
174
  if (length >= 100) return isolate->ThrowIllegalOperation();
175 176 177 178 179
  Handle<FixedArray> output = factory->NewFixedArray(length);
  Handle<Name> maximized = factory->NewStringFromStaticChars("maximized");
  Handle<Name> base = factory->NewStringFromStaticChars("base");
  for (unsigned int i = 0; i < length; ++i) {
    Handle<Object> locale_id;
180 181
    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
        isolate, locale_id, JSReceiver::GetElement(isolate, input, i));
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
    if (!locale_id->IsString()) {
      return isolate->Throw(*factory->illegal_argument_string());
    }

    v8::String::Utf8Value utf8_locale_id(
        v8::Utils::ToLocal(Handle<String>::cast(locale_id)));

    UErrorCode error = U_ZERO_ERROR;

    // Convert from BCP47 to ICU format.
    // de-DE-u-co-phonebk -> de_DE@collation=phonebook
    char icu_locale[ULOC_FULLNAME_CAPACITY];
    int icu_locale_length = 0;
    uloc_forLanguageTag(*utf8_locale_id, icu_locale, ULOC_FULLNAME_CAPACITY,
                        &icu_locale_length, &error);
    if (U_FAILURE(error) || icu_locale_length == 0) {
      return isolate->Throw(*factory->illegal_argument_string());
    }

    // Maximize the locale.
    // de_DE@collation=phonebook -> de_Latn_DE@collation=phonebook
    char icu_max_locale[ULOC_FULLNAME_CAPACITY];
    uloc_addLikelySubtags(icu_locale, icu_max_locale, ULOC_FULLNAME_CAPACITY,
                          &error);

    // Remove extensions from maximized locale.
    // de_Latn_DE@collation=phonebook -> de_Latn_DE
    char icu_base_max_locale[ULOC_FULLNAME_CAPACITY];
    uloc_getBaseName(icu_max_locale, icu_base_max_locale,
                     ULOC_FULLNAME_CAPACITY, &error);

    // Get original name without extensions.
    // de_DE@collation=phonebook -> de_DE
    char icu_base_locale[ULOC_FULLNAME_CAPACITY];
    uloc_getBaseName(icu_locale, icu_base_locale, ULOC_FULLNAME_CAPACITY,
                     &error);

    // Convert from ICU locale format to BCP47 format.
    // de_Latn_DE -> de-Latn-DE
    char base_max_locale[ULOC_FULLNAME_CAPACITY];
    uloc_toLanguageTag(icu_base_max_locale, base_max_locale,
                       ULOC_FULLNAME_CAPACITY, FALSE, &error);

    // de_DE -> de-DE
    char base_locale[ULOC_FULLNAME_CAPACITY];
    uloc_toLanguageTag(icu_base_locale, base_locale, ULOC_FULLNAME_CAPACITY,
                       FALSE, &error);

    if (U_FAILURE(error)) {
      return isolate->Throw(*factory->illegal_argument_string());
    }

    Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
    Handle<String> value = factory->NewStringFromAsciiChecked(base_max_locale);
    JSObject::AddProperty(result, maximized, value, NONE);
    value = factory->NewStringFromAsciiChecked(base_locale);
    JSObject::AddProperty(result, base, value, NONE);
    output->set(i, *result);
  }

  Handle<JSArray> result = factory->NewJSArrayWithElements(output);
  result->set_length(Smi::FromInt(length));
  return *result;
}

RUNTIME_FUNCTION(Runtime_IsInitializedIntlObject) {
  HandleScope scope(isolate);

250
  DCHECK_EQ(1, args.length());
251 252 253 254 255 256

  CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);

  if (!input->IsJSObject()) return isolate->heap()->false_value();
  Handle<JSObject> obj = Handle<JSObject>::cast(input);

257
  Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
258
  Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
259
  return isolate->heap()->ToBoolean(!tag->IsUndefined(isolate));
260 261 262 263 264
}

RUNTIME_FUNCTION(Runtime_IsInitializedIntlObjectOfType) {
  HandleScope scope(isolate);

265
  DCHECK_EQ(2, args.length());
266 267 268 269 270 271 272

  CONVERT_ARG_HANDLE_CHECKED(Object, input, 0);
  CONVERT_ARG_HANDLE_CHECKED(String, expected_type, 1);

  if (!input->IsJSObject()) return isolate->heap()->false_value();
  Handle<JSObject> obj = Handle<JSObject>::cast(input);

273
  Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
274
  Handle<Object> tag = JSReceiver::GetDataProperty(obj, marker);
275 276 277 278 279 280 281
  return isolate->heap()->ToBoolean(tag->IsString() &&
                                    String::cast(*tag)->Equals(*expected_type));
}

RUNTIME_FUNCTION(Runtime_MarkAsInitializedIntlObjectOfType) {
  HandleScope scope(isolate);

282
  DCHECK_EQ(2, args.length());
283 284 285 286

  CONVERT_ARG_HANDLE_CHECKED(JSObject, input, 0);
  CONVERT_ARG_HANDLE_CHECKED(String, type, 1);

287 288
  Handle<Symbol> marker = isolate->factory()->intl_initialized_marker_symbol();
  JSObject::SetProperty(input, marker, type, STRICT).Assert();
289 290 291 292 293 294 295

  return isolate->heap()->undefined_value();
}

RUNTIME_FUNCTION(Runtime_CreateDateTimeFormat) {
  HandleScope scope(isolate);

296
  DCHECK_EQ(3, args.length());
297 298 299 300 301

  CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);

302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
  Handle<JSFunction> constructor(
      isolate->native_context()->intl_date_time_format_function());

  Handle<JSObject> local_object;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
                                     JSObject::New(constructor, constructor));

  // Set date time formatter as embedder field of the resulting JS object.
  icu::SimpleDateFormat* date_format =
      DateFormat::InitializeDateTimeFormat(isolate, locale, options, resolved);

  if (!date_format) return isolate->ThrowIllegalOperation();

  local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(date_format));

  // Make object handle weak so we can delete the data format once GC kicks in.
  Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
  GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
                          DateFormat::DeleteDateFormat,
                          WeakCallbackType::kInternalFields);
  return *local_object;
323 324 325 326 327
}

RUNTIME_FUNCTION(Runtime_InternalDateFormat) {
  HandleScope scope(isolate);

328
  DCHECK_EQ(2, args.length());
329 330 331 332 333

  CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 1);

  Handle<Object> value;
334
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(date));
335

336 337
  icu::SimpleDateFormat* date_format =
      DateFormat::UnpackDateFormat(isolate, date_format_holder);
338
  CHECK_NOT_NULL(date_format);
339 340 341 342

  icu::UnicodeString result;
  date_format->format(value->Number(), result);

343 344 345 346
  RETURN_RESULT_OR_FAILURE(
      isolate, isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
                   reinterpret_cast<const uint16_t*>(result.getBuffer()),
                   result.length())));
347 348
}

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
namespace {
// The list comes from third_party/icu/source/i18n/unicode/udat.h.
// They're mapped to DateTimeFormat components listed at
// https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts .

Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
  switch (field_id) {
    case -1:
      return isolate->factory()->literal_string();
    case UDAT_YEAR_FIELD:
    case UDAT_EXTENDED_YEAR_FIELD:
    case UDAT_YEAR_NAME_FIELD:
      return isolate->factory()->year_string();
    case UDAT_MONTH_FIELD:
    case UDAT_STANDALONE_MONTH_FIELD:
      return isolate->factory()->month_string();
    case UDAT_DATE_FIELD:
      return isolate->factory()->day_string();
    case UDAT_HOUR_OF_DAY1_FIELD:
    case UDAT_HOUR_OF_DAY0_FIELD:
    case UDAT_HOUR1_FIELD:
    case UDAT_HOUR0_FIELD:
      return isolate->factory()->hour_string();
    case UDAT_MINUTE_FIELD:
      return isolate->factory()->minute_string();
    case UDAT_SECOND_FIELD:
      return isolate->factory()->second_string();
    case UDAT_DAY_OF_WEEK_FIELD:
    case UDAT_DOW_LOCAL_FIELD:
    case UDAT_STANDALONE_DAY_FIELD:
      return isolate->factory()->weekday_string();
    case UDAT_AM_PM_FIELD:
      return isolate->factory()->dayperiod_string();
    case UDAT_TIMEZONE_FIELD:
    case UDAT_TIMEZONE_RFC_FIELD:
    case UDAT_TIMEZONE_GENERIC_FIELD:
    case UDAT_TIMEZONE_SPECIAL_FIELD:
    case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
    case UDAT_TIMEZONE_ISO_FIELD:
    case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
      return isolate->factory()->timeZoneName_string();
    case UDAT_ERA_FIELD:
      return isolate->factory()->era_string();
    default:
      // Other UDAT_*_FIELD's cannot show up because there is no way to specify
      // them via options of Intl.DateTimeFormat.
      UNREACHABLE();
      // To prevent MSVC from issuing C4715 warning.
      return Handle<String>();
  }
}

bool AddElement(Handle<JSArray> array, int index, int32_t field_id,
                const icu::UnicodeString& formatted, int32_t begin, int32_t end,
                Isolate* isolate) {
  HandleScope scope(isolate);
  Factory* factory = isolate->factory();
  Handle<JSObject> element = factory->NewJSObject(isolate->object_function());
  Handle<String> value = IcuDateFieldIdToDateType(field_id, isolate);
  JSObject::AddProperty(element, factory->type_string(), value, NONE);

  icu::UnicodeString field(formatted.tempSubStringBetween(begin, end));
  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
412 413 414 415
      isolate, value,
      factory->NewStringFromTwoByte(Vector<const uint16_t>(
          reinterpret_cast<const uint16_t*>(field.getBuffer()),
          field.length())),
416 417 418 419 420 421 422 423 424 425 426 427 428 429
      false);

  JSObject::AddProperty(element, factory->value_string(), value, NONE);
  RETURN_ON_EXCEPTION_VALUE(
      isolate, JSObject::AddDataElement(array, index, element, NONE), false);
  return true;
}

}  // namespace

RUNTIME_FUNCTION(Runtime_InternalDateFormatToParts) {
  HandleScope scope(isolate);
  Factory* factory = isolate->factory();

430
  DCHECK_EQ(2, args.length());
431 432 433 434 435 436 437 438

  CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 1);

  Handle<Object> value;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(date));

  icu::SimpleDateFormat* date_format =
439
      DateFormat::UnpackDateFormat(isolate, date_format_holder);
440
  CHECK_NOT_NULL(date_format);
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478

  icu::UnicodeString formatted;
  icu::FieldPositionIterator fp_iter;
  icu::FieldPosition fp;
  UErrorCode status = U_ZERO_ERROR;
  date_format->format(value->Number(), formatted, &fp_iter, status);
  if (U_FAILURE(status)) return isolate->heap()->undefined_value();

  Handle<JSArray> result = factory->NewJSArray(0);
  int32_t length = formatted.length();
  if (length == 0) return *result;

  int index = 0;
  int32_t previous_end_pos = 0;
  while (fp_iter.next(fp)) {
    int32_t begin_pos = fp.getBeginIndex();
    int32_t end_pos = fp.getEndIndex();

    if (previous_end_pos < begin_pos) {
      if (!AddElement(result, index, -1, formatted, previous_end_pos, begin_pos,
                      isolate)) {
        return isolate->heap()->undefined_value();
      }
      ++index;
    }
    if (!AddElement(result, index, fp.getField(), formatted, begin_pos, end_pos,
                    isolate)) {
      return isolate->heap()->undefined_value();
    }
    previous_end_pos = end_pos;
    ++index;
  }
  if (previous_end_pos < length) {
    if (!AddElement(result, index, -1, formatted, previous_end_pos, length,
                    isolate)) {
      return isolate->heap()->undefined_value();
    }
  }
479
  JSObject::ValidateElements(*result);
480 481
  return *result;
}
482 483 484 485

RUNTIME_FUNCTION(Runtime_CreateNumberFormat) {
  HandleScope scope(isolate);

486
  DCHECK_EQ(3, args.length());
487 488 489 490 491

  CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
  Handle<JSFunction> constructor(
      isolate->native_context()->intl_number_format_function());

  Handle<JSObject> local_object;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
                                     JSObject::New(constructor, constructor));

  // Set number formatter as embedder field of the resulting JS object.
  icu::DecimalFormat* number_format =
      NumberFormat::InitializeNumberFormat(isolate, locale, options, resolved);

  if (!number_format) return isolate->ThrowIllegalOperation();

  local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(number_format));

  Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
  GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
                          NumberFormat::DeleteNumberFormat,
                          WeakCallbackType::kInternalFields);
  return *local_object;
512 513 514 515 516
}

RUNTIME_FUNCTION(Runtime_InternalNumberFormat) {
  HandleScope scope(isolate);

517
  DCHECK_EQ(2, args.length());
518 519 520 521 522

  CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0);
  CONVERT_ARG_HANDLE_CHECKED(Object, number, 1);

  Handle<Object> value;
523
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(number));
524 525

  icu::DecimalFormat* number_format =
526
      NumberFormat::UnpackNumberFormat(isolate, number_format_holder);
527
  CHECK_NOT_NULL(number_format);
528 529 530 531

  icu::UnicodeString result;
  number_format->format(value->Number(), result);

532 533 534 535
  RETURN_RESULT_OR_FAILURE(
      isolate, isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
                   reinterpret_cast<const uint16_t*>(result.getBuffer()),
                   result.length())));
536 537
}

538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
RUNTIME_FUNCTION(Runtime_CurrencyDigits) {
  DCHECK_EQ(1, args.length());

  CONVERT_ARG_HANDLE_CHECKED(String, currency, 0);

  // TODO(littledan): Avoid transcoding the string twice
  v8::String::Utf8Value currency_string(v8::Utils::ToLocal(currency));
  icu::UnicodeString currency_icu =
      icu::UnicodeString::fromUTF8(*currency_string);

  DisallowHeapAllocation no_gc;
  UErrorCode status = U_ZERO_ERROR;
#if U_ICU_VERSION_MAJOR_NUM >= 59
  uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
      icu::toUCharPtr(currency_icu.getTerminatedBuffer()), &status);
#else
  uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
      currency_icu.getTerminatedBuffer(), &status);
#endif
  // For missing currency codes, default to the most common, 2
  if (!U_SUCCESS(status)) fraction_digits = 2;
  return Smi::FromInt(fraction_digits);
}
561 562 563 564

RUNTIME_FUNCTION(Runtime_CreateCollator) {
  HandleScope scope(isolate);

565
  DCHECK_EQ(3, args.length());
566 567 568 569 570

  CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);

571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
  Handle<JSFunction> constructor(
      isolate->native_context()->intl_collator_function());

  Handle<JSObject> local_object;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
                                     JSObject::New(constructor, constructor));

  // Set collator as embedder field of the resulting JS object.
  icu::Collator* collator =
      Collator::InitializeCollator(isolate, locale, options, resolved);

  if (!collator) return isolate->ThrowIllegalOperation();

  local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(collator));

  Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
  GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
                          Collator::DeleteCollator,
                          WeakCallbackType::kInternalFields);
  return *local_object;
591 592 593 594 595
}

RUNTIME_FUNCTION(Runtime_InternalCompare) {
  HandleScope scope(isolate);

596
  DCHECK_EQ(3, args.length());
597 598 599 600 601

  CONVERT_ARG_HANDLE_CHECKED(JSObject, collator_holder, 0);
  CONVERT_ARG_HANDLE_CHECKED(String, string1, 1);
  CONVERT_ARG_HANDLE_CHECKED(String, string2, 2);

602
  icu::Collator* collator = Collator::UnpackCollator(isolate, collator_holder);
603
  CHECK_NOT_NULL(collator);
604

605 606
  string1 = String::Flatten(string1);
  string2 = String::Flatten(string2);
607 608

  UCollationResult result;
609
  UErrorCode status = U_ZERO_ERROR;
610 611 612 613 614 615
  {
    DisallowHeapAllocation no_gc;
    int32_t length1 = string1->length();
    int32_t length2 = string2->length();
    String::FlatContent flat1 = string1->GetFlatContent();
    String::FlatContent flat2 = string2->GetFlatContent();
616 617
    std::unique_ptr<uc16[]> sap1;
    std::unique_ptr<uc16[]> sap2;
618 619 620 621 622
    icu::UnicodeString string_val1(
        FALSE, GetUCharBufferFromFlat(flat1, &sap1, length1), length1);
    icu::UnicodeString string_val2(
        FALSE, GetUCharBufferFromFlat(flat2, &sap2, length2), length2);
    result = collator->compare(string_val1, string_val2, status);
623
  }
624 625 626 627 628 629 630 631
  if (U_FAILURE(status)) return isolate->ThrowIllegalOperation();

  return *isolate->factory()->NewNumberFromInt(result);
}

RUNTIME_FUNCTION(Runtime_CreateBreakIterator) {
  HandleScope scope(isolate);

632
  DCHECK_EQ(3, args.length());
633 634 635 636 637

  CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
  CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);

638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
  Handle<JSFunction> constructor(
      isolate->native_context()->intl_v8_break_iterator_function());

  Handle<JSObject> local_object;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
                                     JSObject::New(constructor, constructor));

  // Set break iterator as embedder field of the resulting JS object.
  icu::BreakIterator* break_iterator = V8BreakIterator::InitializeBreakIterator(
      isolate, locale, options, resolved);

  if (!break_iterator) return isolate->ThrowIllegalOperation();

  local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(break_iterator));
  // Make sure that the pointer to adopted text is NULL.
  local_object->SetEmbedderField(1, static_cast<Smi*>(nullptr));

  // Make object handle weak so we can delete the break iterator once GC kicks
  // in.
  Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
  GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
                          V8BreakIterator::DeleteBreakIterator,
                          WeakCallbackType::kInternalFields);
  return *local_object;
662 663 664 665 666
}

RUNTIME_FUNCTION(Runtime_BreakIteratorAdoptText) {
  HandleScope scope(isolate);

667
  DCHECK_EQ(2, args.length());
668 669 670 671

  CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);
  CONVERT_ARG_HANDLE_CHECKED(String, text, 1);

672 673
  icu::BreakIterator* break_iterator =
      V8BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
674
  CHECK_NOT_NULL(break_iterator);
675

676 677
  icu::UnicodeString* u_text = reinterpret_cast<icu::UnicodeString*>(
      break_iterator_holder->GetEmbedderField(1));
678 679
  delete u_text;

680 681 682 683
  int length = text->length();
  text = String::Flatten(text);
  DisallowHeapAllocation no_gc;
  String::FlatContent flat = text->GetFlatContent();
684
  std::unique_ptr<uc16[]> sap;
685 686
  const UChar* text_value = GetUCharBufferFromFlat(flat, &sap, length);
  u_text = new icu::UnicodeString(text_value, length);
687
  break_iterator_holder->SetEmbedderField(1, reinterpret_cast<Smi*>(u_text));
688 689 690 691 692 693 694 695 696

  break_iterator->setText(*u_text);

  return isolate->heap()->undefined_value();
}

RUNTIME_FUNCTION(Runtime_BreakIteratorFirst) {
  HandleScope scope(isolate);

697
  DCHECK_EQ(1, args.length());
698 699 700

  CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);

701 702
  icu::BreakIterator* break_iterator =
      V8BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
703
  CHECK_NOT_NULL(break_iterator);
704 705 706 707 708 709 710

  return *isolate->factory()->NewNumberFromInt(break_iterator->first());
}

RUNTIME_FUNCTION(Runtime_BreakIteratorNext) {
  HandleScope scope(isolate);

711
  DCHECK_EQ(1, args.length());
712 713 714

  CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);

715 716
  icu::BreakIterator* break_iterator =
      V8BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
717
  CHECK_NOT_NULL(break_iterator);
718 719 720 721 722 723 724

  return *isolate->factory()->NewNumberFromInt(break_iterator->next());
}

RUNTIME_FUNCTION(Runtime_BreakIteratorCurrent) {
  HandleScope scope(isolate);

725
  DCHECK_EQ(1, args.length());
726 727 728

  CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);

729 730
  icu::BreakIterator* break_iterator =
      V8BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
731
  CHECK_NOT_NULL(break_iterator);
732 733 734 735 736 737 738

  return *isolate->factory()->NewNumberFromInt(break_iterator->current());
}

RUNTIME_FUNCTION(Runtime_BreakIteratorBreakType) {
  HandleScope scope(isolate);

739
  DCHECK_EQ(1, args.length());
740 741 742

  CONVERT_ARG_HANDLE_CHECKED(JSObject, break_iterator_holder, 0);

743 744
  icu::BreakIterator* break_iterator =
      V8BreakIterator::UnpackBreakIterator(isolate, break_iterator_holder);
745
  CHECK_NOT_NULL(break_iterator);
746 747 748 749 750 751 752 753 754

  // TODO(cira): Remove cast once ICU fixes base BreakIterator class.
  icu::RuleBasedBreakIterator* rule_based_iterator =
      static_cast<icu::RuleBasedBreakIterator*>(break_iterator);
  int32_t status = rule_based_iterator->getRuleStatus();
  // Keep return values in sync with JavaScript BreakType enum.
  if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) {
    return *isolate->factory()->NewStringFromStaticChars("none");
  } else if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) {
755
    return isolate->heap()->number_string();
756 757 758 759 760 761 762 763 764 765
  } else if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) {
    return *isolate->factory()->NewStringFromStaticChars("letter");
  } else if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) {
    return *isolate->factory()->NewStringFromStaticChars("kana");
  } else if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) {
    return *isolate->factory()->NewStringFromStaticChars("ideo");
  } else {
    return *isolate->factory()->NewStringFromStaticChars("unknown");
  }
}
766

767
RUNTIME_FUNCTION(Runtime_StringToLowerCaseIntl) {
768 769 770 771 772 773 774
  HandleScope scope(isolate);
  DCHECK_EQ(args.length(), 1);
  CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
  s = String::Flatten(s);
  return ConvertToLower(s, isolate);
}

775
RUNTIME_FUNCTION(Runtime_StringToUpperCaseIntl) {
776 777 778 779 780 781 782
  HandleScope scope(isolate);
  DCHECK_EQ(args.length(), 1);
  CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
  s = String::Flatten(s);
  return ConvertToUpper(s, isolate);
}

783 784 785 786 787
RUNTIME_FUNCTION(Runtime_StringLocaleConvertCase) {
  HandleScope scope(isolate);
  DCHECK_EQ(args.length(), 3);
  CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
  CONVERT_BOOLEAN_ARG_CHECKED(is_upper, 1);
788 789
  CONVERT_ARG_HANDLE_CHECKED(String, lang_arg, 2);

790 791 792
  // Primary language tag can be up to 8 characters long in theory.
  // https://tools.ietf.org/html/bcp47#section-2.2.1
  DCHECK(lang_arg->length() <= 8);
793
  lang_arg = String::Flatten(lang_arg);
794
  s = String::Flatten(s);
795 796

  // All the languages requiring special-handling have two-letter codes.
797 798 799 800
  // Note that we have to check for '!= 2' here because private-use language
  // tags (x-foo) or grandfathered irregular tags (e.g. i-enochian) would have
  // only 'x' or 'i' when they get here.
  if (V8_UNLIKELY(lang_arg->length() != 2))
801 802 803 804 805 806 807 808 809
    return ConvertCase(s, is_upper, isolate);

  char c1, c2;
  {
    DisallowHeapAllocation no_gc;
    String::FlatContent lang = lang_arg->GetFlatContent();
    c1 = lang.Get(0);
    c2 = lang.Get(1);
  }
810 811 812
  // TODO(jshin): Consider adding a fast path for ASCII or Latin-1. The fastpath
  // in the root locale needs to be adjusted for az, lt and tr because even case
  // mapping of ASCII range characters are different in those locales.
813 814 815 816 817 818 819 820 821 822 823
  // Greek (el) does not require any adjustment.
  if (V8_UNLIKELY(c1 == 't' && c2 == 'r'))
    return LocaleConvertCase(s, isolate, is_upper, "tr");
  if (V8_UNLIKELY(c1 == 'e' && c2 == 'l'))
    return LocaleConvertCase(s, isolate, is_upper, "el");
  if (V8_UNLIKELY(c1 == 'l' && c2 == 't'))
    return LocaleConvertCase(s, isolate, is_upper, "lt");
  if (V8_UNLIKELY(c1 == 'a' && c2 == 'z'))
    return LocaleConvertCase(s, isolate, is_upper, "az");

  return ConvertCase(s, is_upper, isolate);
824 825
}

826 827 828 829 830 831 832
RUNTIME_FUNCTION(Runtime_DateCacheVersion) {
  HandleScope scope(isolate);
  DCHECK_EQ(0, args.length());
  if (isolate->serializer_enabled()) return isolate->heap()->undefined_value();
  if (!isolate->eternal_handles()->Exists(EternalHandles::DATE_CACHE_VERSION)) {
    Handle<FixedArray> date_cache_version =
        isolate->factory()->NewFixedArray(1, TENURED);
833
    date_cache_version->set(0, Smi::kZero);
834 835 836 837 838 839 840 841 842
    isolate->eternal_handles()->CreateSingleton(
        isolate, *date_cache_version, EternalHandles::DATE_CACHE_VERSION);
  }
  Handle<FixedArray> date_cache_version =
      Handle<FixedArray>::cast(isolate->eternal_handles()->GetSingleton(
          EternalHandles::DATE_CACHE_VERSION));
  return date_cache_version->get(0);
}

843 844
}  // namespace internal
}  // namespace v8