diff options
author | Michaël Zasso <targos@protonmail.com> | 2018-09-21 09:14:51 +0200 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2018-09-22 18:29:25 +0200 |
commit | 0e7ddbd3d7e9439c67573b854c49cf82c398ae82 (patch) | |
tree | 2afe372acde921cb57ddb3444ff00c5adef8848c /deps/v8/src/builtins/builtins-intl.cc | |
parent | 13245dc50da4cb7443c39ef6c68d419d5e6336d4 (diff) | |
download | node-new-0e7ddbd3d7e9439c67573b854c49cf82c398ae82.tar.gz |
deps: update V8 to 7.0.276.20
PR-URL: https://github.com/nodejs/node/pull/22754
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Diffstat (limited to 'deps/v8/src/builtins/builtins-intl.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-intl.cc | 799 |
1 files changed, 683 insertions, 116 deletions
diff --git a/deps/v8/src/builtins/builtins-intl.cc b/deps/v8/src/builtins/builtins-intl.cc index e6664950d0..1d54d0da80 100644 --- a/deps/v8/src/builtins/builtins-intl.cc +++ b/deps/v8/src/builtins/builtins-intl.cc @@ -6,26 +6,38 @@ #error Internationalization is expected to be enabled. #endif // V8_INTL_SUPPORT +#include <cmath> +#include <list> +#include <memory> + #include "src/builtins/builtins-intl.h" -#include "src/builtins/builtins-utils.h" +#include "src/builtins/builtins-utils-inl.h" #include "src/builtins/builtins.h" #include "src/date.h" +#include "src/elements.h" #include "src/intl.h" #include "src/objects-inl.h" #include "src/objects/intl-objects.h" +#include "src/objects/js-array-inl.h" +#include "src/objects/js-collator-inl.h" +#include "src/objects/js-list-format-inl.h" #include "src/objects/js-locale-inl.h" +#include "src/objects/js-plural-rules-inl.h" #include "src/objects/js-relative-time-format-inl.h" #include "unicode/datefmt.h" #include "unicode/decimfmt.h" #include "unicode/fieldpos.h" #include "unicode/fpositer.h" +#include "unicode/listformatter.h" #include "unicode/normalizer2.h" #include "unicode/numfmt.h" +#include "unicode/reldatefmt.h" #include "unicode/smpdtfmt.h" #include "unicode/udat.h" #include "unicode/ufieldpositer.h" #include "unicode/unistr.h" +#include "unicode/ureldatefmt.h" #include "unicode/ustring.h" namespace v8 { @@ -35,7 +47,7 @@ BUILTIN(StringPrototypeToUpperCaseIntl) { HandleScope scope(isolate); TO_THIS_STRING(string, "String.prototype.toUpperCase"); string = String::Flatten(isolate, string); - return ConvertCase(string, true, isolate); + RETURN_RESULT_OR_FAILURE(isolate, ConvertCase(string, true, isolate)); } BUILTIN(StringPrototypeNormalizeIntl) { @@ -106,7 +118,8 @@ BUILTIN(StringPrototypeNormalizeIntl) { } if (U_FAILURE(status)) { - return ReadOnlyRoots(isolate).undefined_value(); + THROW_NEW_ERROR_RETURN_FAILURE(isolate, + NewTypeError(MessageTemplate::kIcuError)); } RETURN_RESULT_OR_FAILURE( @@ -212,30 +225,6 @@ Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) { } } -bool AddElement(Handle<JSArray> array, int index, - Handle<String> field_type_string, - 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; - JSObject::AddProperty(isolate, element, factory->type_string(), - field_type_string, NONE); - - icu::UnicodeString field(formatted.tempSubStringBetween(begin, end)); - ASSIGN_RETURN_ON_EXCEPTION_VALUE( - isolate, value, - factory->NewStringFromTwoByte(Vector<const uint16_t>( - reinterpret_cast<const uint16_t*>(field.getBuffer()), - field.length())), - false); - - JSObject::AddProperty(isolate, element, factory->value_string(), value, NONE); - JSObject::AddDataElement(array, index, element, NONE); - return true; -} - bool cmp_NumberFormatSpan(const NumberFormatSpan& a, const NumberFormatSpan& b) { // Regions that start earlier should be encountered earlier. @@ -251,19 +240,21 @@ bool cmp_NumberFormatSpan(const NumberFormatSpan& a, return a.field_id < b.field_id; } -Object* FormatNumberToParts(Isolate* isolate, icu::NumberFormat* fmt, - double number) { +MaybeHandle<Object> FormatNumberToParts(Isolate* isolate, + icu::NumberFormat* fmt, double number) { Factory* factory = isolate->factory(); icu::UnicodeString formatted; icu::FieldPositionIterator fp_iter; UErrorCode status = U_ZERO_ERROR; fmt->format(number, formatted, &fp_iter, status); - if (U_FAILURE(status)) return ReadOnlyRoots(isolate).undefined_value(); + if (U_FAILURE(status)) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object); + } Handle<JSArray> result = factory->NewJSArray(0); int32_t length = formatted.length(); - if (length == 0) return *result; + if (length == 0) return result; std::vector<NumberFormatSpan> regions; // Add a "literal" backdrop for the entire string. This will be used if no @@ -289,19 +280,21 @@ Object* FormatNumberToParts(Isolate* isolate, icu::NumberFormat* fmt, part.field_id == -1 ? isolate->factory()->literal_string() : IcuNumberFieldIdToNumberType(part.field_id, number, isolate); - if (!AddElement(result, index, field_type_string, formatted, part.begin_pos, - part.end_pos, isolate)) { - return ReadOnlyRoots(isolate).undefined_value(); - } + Handle<String> substring; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, substring, + Intl::ToString(isolate, formatted, part.begin_pos, part.end_pos), + Object); + Intl::AddElement(isolate, result, index, field_type_string, substring); ++index; } JSObject::ValidateElements(*result); - return *result; + return result; } -Object* FormatDateToParts(Isolate* isolate, icu::DateFormat* format, - double date_value) { +MaybeHandle<Object> FormatDateToParts(Isolate* isolate, icu::DateFormat* format, + double date_value) { Factory* factory = isolate->factory(); icu::UnicodeString formatted; @@ -309,41 +302,48 @@ Object* FormatDateToParts(Isolate* isolate, icu::DateFormat* format, icu::FieldPosition fp; UErrorCode status = U_ZERO_ERROR; format->format(date_value, formatted, &fp_iter, status); - if (U_FAILURE(status)) return ReadOnlyRoots(isolate).undefined_value(); + if (U_FAILURE(status)) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object); + } Handle<JSArray> result = factory->NewJSArray(0); int32_t length = formatted.length(); - if (length == 0) return *result; + if (length == 0) return result; int index = 0; int32_t previous_end_pos = 0; + Handle<String> substring; 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, IcuDateFieldIdToDateType(-1, isolate), - formatted, previous_end_pos, begin_pos, isolate)) { - return ReadOnlyRoots(isolate).undefined_value(); - } + ASSIGN_RETURN_ON_EXCEPTION( + isolate, substring, + Intl::ToString(isolate, formatted, previous_end_pos, begin_pos), + Object); + Intl::AddElement(isolate, result, index, + IcuDateFieldIdToDateType(-1, isolate), substring); ++index; } - if (!AddElement(result, index, - IcuDateFieldIdToDateType(fp.getField(), isolate), formatted, - begin_pos, end_pos, isolate)) { - return ReadOnlyRoots(isolate).undefined_value(); - } + ASSIGN_RETURN_ON_EXCEPTION( + isolate, substring, + Intl::ToString(isolate, formatted, begin_pos, end_pos), Object); + Intl::AddElement(isolate, result, index, + IcuDateFieldIdToDateType(fp.getField(), isolate), + substring); previous_end_pos = end_pos; ++index; } if (previous_end_pos < length) { - if (!AddElement(result, index, IcuDateFieldIdToDateType(-1, isolate), - formatted, previous_end_pos, length, isolate)) { - return ReadOnlyRoots(isolate).undefined_value(); - } + ASSIGN_RETURN_ON_EXCEPTION( + isolate, substring, + Intl::ToString(isolate, formatted, previous_end_pos, length), Object); + Intl::AddElement(isolate, result, index, + IcuDateFieldIdToDateType(-1, isolate), substring); } JSObject::ValidateElements(*result); - return *result; + return result; } } // namespace @@ -461,11 +461,11 @@ BUILTIN(NumberFormatPrototypeFormatToParts) { } icu::DecimalFormat* number_format = - NumberFormat::UnpackNumberFormat(isolate, number_format_holder); + NumberFormat::UnpackNumberFormat(number_format_holder); CHECK_NOT_NULL(number_format); - Object* result = FormatNumberToParts(isolate, number_format, x->Number()); - return result; + RETURN_RESULT_OR_FAILURE( + isolate, FormatNumberToParts(isolate, number_format, x->Number())); } BUILTIN(DateTimeFormatPrototypeFormatToParts) { @@ -497,10 +497,11 @@ BUILTIN(DateTimeFormatPrototypeFormatToParts) { } icu::SimpleDateFormat* date_format = - DateFormat::UnpackDateFormat(isolate, date_format_holder); + DateFormat::UnpackDateFormat(date_format_holder); CHECK_NOT_NULL(date_format); - return FormatDateToParts(isolate, date_format, date_value); + RETURN_RESULT_OR_FAILURE(isolate, + FormatDateToParts(isolate, date_format, date_value)); } BUILTIN(NumberFormatPrototypeFormatNumber) { @@ -531,8 +532,8 @@ BUILTIN(NumberFormatPrototypeFormatNumber) { return *bound_format; } - Handle<Context> native_context = - Handle<Context>(isolate->context()->native_context(), isolate); + Handle<NativeContext> native_context(isolate->context()->native_context(), + isolate); Handle<Context> context = isolate->factory()->NewBuiltinContext( native_context, NumberFormat::ContextSlot::kLength); @@ -593,93 +594,402 @@ BUILTIN(NumberFormatInternalFormatNumber) { isolate, number_format_holder, number)); } -// Intl.Locale implementation -BUILTIN(LocaleConstructor) { +BUILTIN(DateTimeFormatPrototypeFormat) { + const char* const method = "get Intl.DateTimeFormat.prototype.format"; HandleScope scope(isolate); - if (args.new_target()->IsUndefined(isolate)) { // [[Call]] - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, - isolate->factory()->NewStringFromAsciiChecked( - "Intl.Locale"))); - } else { // [[Construct]] - Handle<JSFunction> target = args.target(); - Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target()); - Handle<JSObject> result; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, - JSObject::New(target, new_target)); + // 1. Let dtf be this value. + // 2. If Type(dtf) is not Object, throw a TypeError exception. + CHECK_RECEIVER(JSReceiver, receiver, method); - Handle<Object> tag = args.atOrUndefined(isolate, 1); - Handle<Object> options = args.atOrUndefined(isolate, 2); + // 3. Let dtf be ? UnwrapDateTimeFormat(dtf). + Handle<JSObject> date_format_holder; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, date_format_holder, + DateFormat::Unwrap(isolate, receiver, method)); + DCHECK(Intl::IsObjectOfType(isolate, date_format_holder, + Intl::Type::kDateTimeFormat)); - // First parameter is a locale, as a string/object. Can't be empty. - if (!tag->IsName() && !tag->IsJSReceiver()) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kLocaleNotEmpty)); - } + Handle<Object> bound_format = Handle<Object>( + date_format_holder->GetEmbedderField(DateFormat::kBoundFormatIndex), + isolate); - Handle<String> locale_string; - if (tag->IsJSLocale() && - Handle<JSLocale>::cast(tag)->locale()->IsString()) { - locale_string = - Handle<String>(Handle<JSLocale>::cast(tag)->locale(), isolate); - } else { - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, locale_string, - Object::ToString(isolate, tag)); - } + // 4. If dtf.[[BoundFormat]] is undefined, then + if (!bound_format->IsUndefined(isolate)) { + DCHECK(bound_format->IsJSFunction()); + // 5. Return dtf.[[BoundFormat]]. + return *bound_format; + } - Handle<JSReceiver> options_object; - if (options->IsNullOrUndefined(isolate)) { - // Make empty options bag. - options_object = isolate->factory()->NewJSObjectWithNullProto(); - } else { - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, options_object, - Object::ToObject(isolate, options)); - } + Handle<NativeContext> native_context(isolate->context()->native_context(), + isolate); + Handle<Context> context = isolate->factory()->NewBuiltinContext( + native_context, DateFormat::ContextSlot::kLength); - RETURN_RESULT_OR_FAILURE( - isolate, - JSLocale::InitializeLocale(isolate, Handle<JSLocale>::cast(result), - locale_string, options_object)); - } + // 4.b. Set F.[[DateTimeFormat]] to dtf. + context->set(DateFormat::ContextSlot::kDateFormat, *date_format_holder); + + Handle<SharedFunctionInfo> info = Handle<SharedFunctionInfo>( + native_context->date_format_internal_format_shared_fun(), isolate); + Handle<Map> map = isolate->strict_function_without_prototype_map(); + + // 4.a. Let F be a new built-in function object as defined in DateTime Format + // Functions (12.1.5). + Handle<JSFunction> new_bound_format_function = + isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context); + + // 4.c. Set dtf.[[BoundFormat]] to F. + date_format_holder->SetEmbedderField(DateFormat::kBoundFormatIndex, + *new_bound_format_function); + + // 5. Return dtf.[[BoundFormat]]. + return *new_bound_format_function; } -BUILTIN(RelativeTimeFormatConstructor) { +BUILTIN(DateTimeFormatInternalFormat) { + HandleScope scope(isolate); + Handle<Context> context = Handle<Context>(isolate->context(), isolate); + + // 1. Let dtf be F.[[DateTimeFormat]]. + Handle<JSObject> date_format_holder = Handle<JSObject>( + JSObject::cast(context->get(DateFormat::ContextSlot::kDateFormat)), + isolate); + + // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]] + // internal slot. + DCHECK(Intl::IsObjectOfType(isolate, date_format_holder, + Intl::Type::kDateTimeFormat)); + + Handle<Object> date = args.atOrUndefined(isolate, 1); + + RETURN_RESULT_OR_FAILURE( + isolate, DateFormat::DateTimeFormat(isolate, date_format_holder, date)); +} + +BUILTIN(ListFormatConstructor) { HandleScope scope(isolate); // 1. If NewTarget is undefined, throw a TypeError exception. if (args.new_target()->IsUndefined(isolate)) { // [[Call]] THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, isolate->factory()->NewStringFromStaticChars( - "Intl.RelativeTimeFormat"))); + "Intl.ListFormat"))); } // [[Construct]] Handle<JSFunction> target = args.target(); Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target()); Handle<JSObject> result; - // 2. Let relativeTimeFormat be - // ! OrdinaryCreateFromConstructor(NewTarget, - // "%RelativeTimeFormatPrototype%"). + // 2. Let listFormat be OrdinaryCreateFromConstructor(NewTarget, + // "%ListFormatPrototype%"). ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, JSObject::New(target, new_target)); + Handle<JSListFormat> format = Handle<JSListFormat>::cast(result); + format->set_flags(0); Handle<Object> locales = args.atOrUndefined(isolate, 1); Handle<Object> options = args.atOrUndefined(isolate, 2); - // 3. Return ? InitializeRelativeTimeFormat(relativeTimeFormat, locales, - // options). + // 3. Return InitializeListFormat(listFormat, locales, options). + RETURN_RESULT_OR_FAILURE(isolate, JSListFormat::InitializeListFormat( + isolate, format, locales, options)); +} + +BUILTIN(ListFormatPrototypeResolvedOptions) { + HandleScope scope(isolate); + CHECK_RECEIVER(JSListFormat, format_holder, + "Intl.ListFormat.prototype.resolvedOptions"); + return *JSListFormat::ResolvedOptions(isolate, format_holder); +} + +namespace { + +MaybeHandle<JSLocale> CreateLocale(Isolate* isolate, + Handle<JSFunction> constructor, + Handle<JSReceiver> new_target, + Handle<Object> tag, Handle<Object> options) { + Handle<JSObject> result; + ASSIGN_RETURN_ON_EXCEPTION(isolate, result, + JSObject::New(constructor, new_target), JSLocale); + + // First parameter is a locale, as a string/object. Can't be empty. + if (!tag->IsString() && !tag->IsJSReceiver()) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kLocaleNotEmpty), + JSLocale); + } + + Handle<String> locale_string; + if (tag->IsJSLocale() && Handle<JSLocale>::cast(tag)->locale()->IsString()) { + locale_string = + Handle<String>(Handle<JSLocale>::cast(tag)->locale(), isolate); + } else { + ASSIGN_RETURN_ON_EXCEPTION(isolate, locale_string, + Object::ToString(isolate, tag), JSLocale); + } + + Handle<JSReceiver> options_object; + if (options->IsNullOrUndefined(isolate)) { + // Make empty options bag. + options_object = isolate->factory()->NewJSObjectWithNullProto(); + } else { + ASSIGN_RETURN_ON_EXCEPTION(isolate, options_object, + Object::ToObject(isolate, options), JSLocale); + } + + return JSLocale::InitializeLocale(isolate, Handle<JSLocale>::cast(result), + locale_string, options_object); +} + +} // namespace + +// Intl.Locale implementation +BUILTIN(LocaleConstructor) { + HandleScope scope(isolate); + if (args.new_target()->IsUndefined(isolate)) { // [[Call]] + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, + isolate->factory()->NewStringFromAsciiChecked( + "Intl.Locale"))); + } + // [[Construct]] + Handle<JSFunction> target = args.target(); + Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target()); + + Handle<Object> tag = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); + RETURN_RESULT_OR_FAILURE( - isolate, JSRelativeTimeFormat::InitializeRelativeTimeFormat( - isolate, Handle<JSRelativeTimeFormat>::cast(result), locales, - options)); + isolate, CreateLocale(isolate, target, new_target, tag, options)); } -BUILTIN(RelativeTimeFormatPrototypeResolvedOptions) { +BUILTIN(LocalePrototypeMaximize) { + HandleScope scope(isolate); + CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.maximize"); + Handle<JSFunction> constructor( + isolate->native_context()->intl_locale_function(), isolate); + RETURN_RESULT_OR_FAILURE( + isolate, + CreateLocale(isolate, constructor, constructor, + JSLocale::Maximize(isolate, locale_holder->locale()), + isolate->factory()->NewJSObjectWithNullProto())); +} + +BUILTIN(LocalePrototypeMinimize) { + HandleScope scope(isolate); + CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.minimize"); + Handle<JSFunction> constructor( + isolate->native_context()->intl_locale_function(), isolate); + RETURN_RESULT_OR_FAILURE( + isolate, + CreateLocale(isolate, constructor, constructor, + JSLocale::Minimize(isolate, locale_holder->locale()), + isolate->factory()->NewJSObjectWithNullProto())); +} + +namespace { + +MaybeHandle<JSArray> GenerateRelativeTimeFormatParts( + Isolate* isolate, icu::UnicodeString formatted, + icu::UnicodeString integer_part, Handle<String> unit) { + Factory* factory = isolate->factory(); + Handle<JSArray> array = factory->NewJSArray(0); + int32_t found = formatted.indexOf(integer_part); + + Handle<String> substring; + if (found < 0) { + // Cannot find the integer_part in the formatted. + // Return [{'type': 'literal', 'value': formatted}] + ASSIGN_RETURN_ON_EXCEPTION(isolate, substring, + Intl::ToString(isolate, formatted), JSArray); + Intl::AddElement(isolate, array, + 0, // index + factory->literal_string(), // field_type_string + substring); + } else { + // Found the formatted integer in the result. + int index = 0; + + // array.push({ + // 'type': 'literal', + // 'value': formatted.substring(0, found)}) + if (found > 0) { + ASSIGN_RETURN_ON_EXCEPTION(isolate, substring, + Intl::ToString(isolate, formatted, 0, found), + JSArray); + Intl::AddElement(isolate, array, index++, + factory->literal_string(), // field_type_string + substring); + } + + // array.push({ + // 'type': 'integer', + // 'value': formatted.substring(found, found + integer_part.length), + // 'unit': unit}) + ASSIGN_RETURN_ON_EXCEPTION(isolate, substring, + Intl::ToString(isolate, formatted, found, + found + integer_part.length()), + JSArray); + Intl::AddElement(isolate, array, index++, + factory->integer_string(), // field_type_string + substring, factory->unit_string(), unit); + + // array.push({ + // 'type': 'literal', + // 'value': formatted.substring( + // found + integer_part.length, formatted.length)}) + if (found + integer_part.length() < formatted.length()) { + ASSIGN_RETURN_ON_EXCEPTION( + isolate, substring, + Intl::ToString(isolate, formatted, found + integer_part.length(), + formatted.length()), + JSArray); + Intl::AddElement(isolate, array, index, + factory->literal_string(), // field_type_string + substring); + } + } + return array; +} + +bool GetURelativeDateTimeUnit(Handle<String> unit, + URelativeDateTimeUnit* unit_enum) { + std::unique_ptr<char[]> unit_str = unit->ToCString(); + if ((strcmp("second", unit_str.get()) == 0) || + (strcmp("seconds", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_SECOND; + } else if ((strcmp("minute", unit_str.get()) == 0) || + (strcmp("minutes", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_MINUTE; + } else if ((strcmp("hour", unit_str.get()) == 0) || + (strcmp("hours", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_HOUR; + } else if ((strcmp("day", unit_str.get()) == 0) || + (strcmp("days", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_DAY; + } else if ((strcmp("week", unit_str.get()) == 0) || + (strcmp("weeks", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_WEEK; + } else if ((strcmp("month", unit_str.get()) == 0) || + (strcmp("months", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_MONTH; + } else if ((strcmp("quarter", unit_str.get()) == 0) || + (strcmp("quarters", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_QUARTER; + } else if ((strcmp("year", unit_str.get()) == 0) || + (strcmp("years", unit_str.get()) == 0)) { + *unit_enum = UDAT_REL_UNIT_YEAR; + } else { + return false; + } + return true; +} + +MaybeHandle<Object> RelativeTimeFormatPrototypeFormatCommon( + BuiltinArguments args, Isolate* isolate, + Handle<JSRelativeTimeFormat> format_holder, const char* func_name, + bool to_parts) { + Factory* factory = isolate->factory(); + Handle<Object> value_obj = args.atOrUndefined(isolate, 1); + Handle<Object> unit_obj = args.atOrUndefined(isolate, 2); + + // 3. Let value be ? ToNumber(value). + Handle<Object> value; + ASSIGN_RETURN_ON_EXCEPTION(isolate, value, + Object::ToNumber(isolate, value_obj), Object); + double number = value->Number(); + // 4. Let unit be ? ToString(unit). + Handle<String> unit; + ASSIGN_RETURN_ON_EXCEPTION(isolate, unit, Object::ToString(isolate, unit_obj), + Object); + + // 4. If isFinite(value) is false, then throw a RangeError exception. + if (!std::isfinite(number)) { + THROW_NEW_ERROR( + isolate, + NewRangeError(MessageTemplate::kNotFiniteNumber, + isolate->factory()->NewStringFromAsciiChecked(func_name)), + Object); + } + + icu::RelativeDateTimeFormatter* formatter = + JSRelativeTimeFormat::UnpackFormatter(format_holder); + CHECK_NOT_NULL(formatter); + + URelativeDateTimeUnit unit_enum; + if (!GetURelativeDateTimeUnit(unit, &unit_enum)) { + THROW_NEW_ERROR( + isolate, + NewRangeError(MessageTemplate::kInvalidUnit, + isolate->factory()->NewStringFromAsciiChecked(func_name), + unit), + Object); + } + + UErrorCode status = U_ZERO_ERROR; + icu::UnicodeString formatted; + if (unit_enum == UDAT_REL_UNIT_QUARTER) { + // ICU have not yet implement UDAT_REL_UNIT_QUARTER. + } else { + if (format_holder->numeric() == JSRelativeTimeFormat::Numeric::ALWAYS) { + formatter->formatNumeric(number, unit_enum, formatted, status); + } else { + DCHECK_EQ(JSRelativeTimeFormat::Numeric::AUTO, format_holder->numeric()); + formatter->format(number, unit_enum, formatted, status); + } + } + + if (U_FAILURE(status)) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object); + } + + if (to_parts) { + icu::UnicodeString integer; + icu::FieldPosition pos; + formatter->getNumberFormat().format(std::abs(number), integer, pos, status); + if (U_FAILURE(status)) { + THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), + Object); + } + + Handle<JSArray> elements; + ASSIGN_RETURN_ON_EXCEPTION( + isolate, elements, + GenerateRelativeTimeFormatParts(isolate, formatted, integer, unit), + Object); + return elements; + } + + return factory->NewStringFromTwoByte(Vector<const uint16_t>( + reinterpret_cast<const uint16_t*>(formatted.getBuffer()), + formatted.length())); +} + +} // namespace + +BUILTIN(RelativeTimeFormatPrototypeFormat) { HandleScope scope(isolate); + // 1. Let relativeTimeFormat be the this value. + // 2. If Type(relativeTimeFormat) is not Object or relativeTimeFormat does not + // have an [[InitializedRelativeTimeFormat]] internal slot whose value is + // true, throw a TypeError exception. CHECK_RECEIVER(JSRelativeTimeFormat, format_holder, - "Intl.RelativeTimeFormat.prototype.resolvedOptions"); - return *JSRelativeTimeFormat::ResolvedOptions(isolate, format_holder); + "Intl.RelativeTimeFormat.prototype.format"); + RETURN_RESULT_OR_FAILURE(isolate, + RelativeTimeFormatPrototypeFormatCommon( + args, isolate, format_holder, "format", false)); +} + +BUILTIN(RelativeTimeFormatPrototypeFormatToParts) { + HandleScope scope(isolate); + // 1. Let relativeTimeFormat be the this value. + // 2. If Type(relativeTimeFormat) is not Object or relativeTimeFormat does not + // have an [[InitializedRelativeTimeFormat]] internal slot whose value is + // true, throw a TypeError exception. + CHECK_RECEIVER(JSRelativeTimeFormat, format_holder, + "Intl.RelativeTimeFormat.prototype.formatToParts"); + RETURN_RESULT_OR_FAILURE( + isolate, RelativeTimeFormatPrototypeFormatCommon( + args, isolate, format_holder, "formatToParts", true)); } // Locale getters. @@ -762,5 +1072,262 @@ BUILTIN(LocalePrototypeToString) { return locale_holder->locale(); } +BUILTIN(RelativeTimeFormatConstructor) { + HandleScope scope(isolate); + // 1. If NewTarget is undefined, throw a TypeError exception. + if (args.new_target()->IsUndefined(isolate)) { // [[Call]] + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, + isolate->factory()->NewStringFromStaticChars( + "Intl.RelativeTimeFormat"))); + } + // [[Construct]] + Handle<JSFunction> target = args.target(); + Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target()); + + Handle<JSObject> result; + // 2. Let relativeTimeFormat be + // ! OrdinaryCreateFromConstructor(NewTarget, + // "%RelativeTimeFormatPrototype%"). + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, + JSObject::New(target, new_target)); + Handle<JSRelativeTimeFormat> format = + Handle<JSRelativeTimeFormat>::cast(result); + format->set_flags(0); + + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); + + // 3. Return ? InitializeRelativeTimeFormat(relativeTimeFormat, locales, + // options). + RETURN_RESULT_OR_FAILURE(isolate, + JSRelativeTimeFormat::InitializeRelativeTimeFormat( + isolate, format, locales, options)); +} + +BUILTIN(RelativeTimeFormatPrototypeResolvedOptions) { + HandleScope scope(isolate); + CHECK_RECEIVER(JSRelativeTimeFormat, format_holder, + "Intl.RelativeTimeFormat.prototype.resolvedOptions"); + return *JSRelativeTimeFormat::ResolvedOptions(isolate, format_holder); +} + +BUILTIN(StringPrototypeToLocaleLowerCase) { + HandleScope scope(isolate); + TO_THIS_STRING(string, "String.prototype.toLocaleLowerCase"); + RETURN_RESULT_OR_FAILURE( + isolate, Intl::StringLocaleConvertCase(isolate, string, false, + args.atOrUndefined(isolate, 1))); +} + +BUILTIN(StringPrototypeToLocaleUpperCase) { + HandleScope scope(isolate); + TO_THIS_STRING(string, "String.prototype.toLocaleUpperCase"); + RETURN_RESULT_OR_FAILURE( + isolate, Intl::StringLocaleConvertCase(isolate, string, true, + args.atOrUndefined(isolate, 1))); +} + +BUILTIN(PluralRulesConstructor) { + HandleScope scope(isolate); + + // 1. If NewTarget is undefined, throw a TypeError exception. + if (args.new_target()->IsUndefined(isolate)) { // [[Call]] + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, + isolate->factory()->NewStringFromStaticChars( + "Intl.PluralRules"))); + } + + // [[Construct]] + Handle<JSFunction> target = args.target(); + Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target()); + + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); + + // 2. Let pluralRules be ? OrdinaryCreateFromConstructor(newTarget, + // "%PluralRulesPrototype%", « [[InitializedPluralRules]], + // [[Locale]], [[Type]], [[MinimumIntegerDigits]], + // [[MinimumFractionDigits]], [[MaximumFractionDigits]], + // [[MinimumSignificantDigits]], [[MaximumSignificantDigits]] »). + Handle<JSObject> plural_rules_obj; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, plural_rules_obj, + JSObject::New(target, new_target)); + Handle<JSPluralRules> plural_rules = + Handle<JSPluralRules>::cast(plural_rules_obj); + + // 3. Return ? InitializePluralRules(pluralRules, locales, options). + RETURN_RESULT_OR_FAILURE( + isolate, JSPluralRules::InitializePluralRules(isolate, plural_rules, + locales, options)); +} + +BUILTIN(CollatorConstructor) { + HandleScope scope(isolate); + Handle<JSReceiver> new_target; + // 1. If NewTarget is undefined, let newTarget be the active + // function object, else let newTarget be NewTarget. + if (args.new_target()->IsUndefined(isolate)) { + new_target = args.target(); + } else { + new_target = Handle<JSReceiver>::cast(args.new_target()); + } + + // [[Construct]] + Handle<JSFunction> target = args.target(); + + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); + + // 5. Let collator be ? OrdinaryCreateFromConstructor(newTarget, + // "%CollatorPrototype%", internalSlotsList). + Handle<JSObject> collator_obj; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, collator_obj, + JSObject::New(target, new_target)); + Handle<JSCollator> collator = Handle<JSCollator>::cast(collator_obj); + collator->set_flags(0); + + // 6. Return ? InitializeCollator(collator, locales, options). + RETURN_RESULT_OR_FAILURE(isolate, JSCollator::InitializeCollator( + isolate, collator, locales, options)); +} + +BUILTIN(CollatorPrototypeCompare) { + const char* const method = "get Intl.Collator.prototype.compare"; + HandleScope scope(isolate); + + // 1. Let collator be this value. + // 2. If Type(collator) is not Object, throw a TypeError exception. + // 3. If collator does not have an [[InitializedCollator]] internal slot, + // throw a TypeError exception. + CHECK_RECEIVER(JSCollator, collator, method); + + // 4. If collator.[[BoundCompare]] is undefined, then + Handle<Object> bound_compare(collator->bound_compare(), isolate); + if (!bound_compare->IsUndefined(isolate)) { + DCHECK(bound_compare->IsJSFunction()); + // 5. Return collator.[[BoundCompare]]. + return *bound_compare; + } + + Handle<NativeContext> native_context(isolate->context()->native_context(), + isolate); + Handle<Context> context = isolate->factory()->NewBuiltinContext( + native_context, JSCollator::ContextSlot::kLength); + + // 4.b. Set F.[[Collator]] to collator. + context->set(JSCollator::ContextSlot::kCollator, *collator); + + Handle<SharedFunctionInfo> info = Handle<SharedFunctionInfo>( + native_context->collator_internal_compare_shared_fun(), isolate); + Handle<Map> map = isolate->strict_function_without_prototype_map(); + + // 4.a. Let F be a new built-in function object as defined in 10.3.3.1. + Handle<JSFunction> new_bound_compare_function = + isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context); + + // 4.c. Set collator.[[BoundCompare]] to F. + collator->set_bound_compare(*new_bound_compare_function); + + // 5. Return collator.[[BoundCompare]]. + return *new_bound_compare_function; +} + +BUILTIN(CollatorInternalCompare) { + HandleScope scope(isolate); + Handle<Context> context = Handle<Context>(isolate->context(), isolate); + + // 1. Let collator be F.[[Collator]]. + // 2. Assert: Type(collator) is Object and collator has an + // [[InitializedCollator]] internal slot. + Handle<JSCollator> collator_holder = Handle<JSCollator>( + JSCollator::cast(context->get(JSCollator::ContextSlot::kCollator)), + isolate); + + // 3. If x is not provided, let x be undefined. + Handle<Object> x = args.atOrUndefined(isolate, 1); + // 4. If y is not provided, let y be undefined. + Handle<Object> y = args.atOrUndefined(isolate, 2); + + // 5. Let X be ? ToString(x). + Handle<String> string_x; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string_x, + Object::ToString(isolate, x)); + // 6. Let Y be ? ToString(y). + Handle<String> string_y; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string_y, + Object::ToString(isolate, y)); + + // 7. Return CompareStrings(collator, X, Y). + return *Intl::CompareStrings(isolate, collator_holder, string_x, string_y); +} + +BUILTIN(BreakIteratorPrototypeAdoptText) { + const char* const method = "get Intl.v8BreakIterator.prototype.adoptText"; + HandleScope scope(isolate); + + CHECK_RECEIVER(JSObject, break_iterator_holder, method); + if (!Intl::IsObjectOfType(isolate, break_iterator_holder, + Intl::Type::kBreakIterator)) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, + NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, + isolate->factory()->NewStringFromAsciiChecked(method), + break_iterator_holder)); + } + + Handle<Object> bound_adopt_text = + Handle<Object>(break_iterator_holder->GetEmbedderField( + V8BreakIterator::kBoundAdoptTextIndex), + isolate); + + if (!bound_adopt_text->IsUndefined(isolate)) { + DCHECK(bound_adopt_text->IsJSFunction()); + return *bound_adopt_text; + } + + Handle<NativeContext> native_context(isolate->context()->native_context(), + isolate); + Handle<Context> context = isolate->factory()->NewBuiltinContext( + native_context, static_cast<int>(V8BreakIterator::ContextSlot::kLength)); + + context->set(static_cast<int>(V8BreakIterator::ContextSlot::kV8BreakIterator), + *break_iterator_holder); + + Handle<SharedFunctionInfo> info = Handle<SharedFunctionInfo>( + native_context->break_iterator_internal_adopt_text_shared_fun(), isolate); + Handle<Map> map = isolate->strict_function_without_prototype_map(); + + Handle<JSFunction> new_bound_adopt_text_function = + isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context); + + break_iterator_holder->SetEmbedderField(V8BreakIterator::kBoundAdoptTextIndex, + *new_bound_adopt_text_function); + + return *new_bound_adopt_text_function; +} + +BUILTIN(BreakIteratorInternalAdoptText) { + HandleScope scope(isolate); + Handle<Context> context = Handle<Context>(isolate->context(), isolate); + + Handle<JSObject> break_iterator_holder = Handle<JSObject>( + JSObject::cast(context->get( + static_cast<int>(V8BreakIterator::ContextSlot::kV8BreakIterator))), + isolate); + + DCHECK(Intl::IsObjectOfType(isolate, break_iterator_holder, + Intl::Type::kBreakIterator)); + + Handle<Object> input_text = args.atOrUndefined(isolate, 1); + Handle<String> text; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, text, + Object::ToString(isolate, input_text)); + + V8BreakIterator::AdoptText(isolate, break_iterator_holder, text); + return ReadOnlyRoots(isolate).undefined_value(); +} + } // namespace internal } // namespace v8 |