diff options
Diffstat (limited to 'deps/v8/src/builtins/builtins-intl.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-intl.cc | 1296 |
1 files changed, 628 insertions, 668 deletions
diff --git a/deps/v8/src/builtins/builtins-intl.cc b/deps/v8/src/builtins/builtins-intl.cc index 1d54d0da80..01c8a9ddcd 100644 --- a/deps/v8/src/builtins/builtins-intl.cc +++ b/deps/v8/src/builtins/builtins-intl.cc @@ -10,7 +10,6 @@ #include <list> #include <memory> -#include "src/builtins/builtins-intl.h" #include "src/builtins/builtins-utils-inl.h" #include "src/builtins/builtins.h" #include "src/date.h" @@ -19,11 +18,16 @@ #include "src/objects-inl.h" #include "src/objects/intl-objects.h" #include "src/objects/js-array-inl.h" +#include "src/objects/js-break-iterator-inl.h" #include "src/objects/js-collator-inl.h" +#include "src/objects/js-date-time-format-inl.h" #include "src/objects/js-list-format-inl.h" #include "src/objects/js-locale-inl.h" +#include "src/objects/js-number-format-inl.h" #include "src/objects/js-plural-rules-inl.h" #include "src/objects/js-relative-time-format-inl.h" +#include "src/objects/js-segmenter-inl.h" +#include "src/property-descriptor.h" #include "unicode/datefmt.h" #include "unicode/decimfmt.h" @@ -32,12 +36,10 @@ #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 { @@ -128,329 +130,30 @@ BUILTIN(StringPrototypeNormalizeIntl) { result.length()))); } -namespace { - -// The list comes from third_party/icu/source/i18n/unicode/unum.h. -// They're mapped to NumberFormat part types mentioned throughout -// https://tc39.github.io/ecma402/#sec-partitionnumberpattern . -Handle<String> IcuNumberFieldIdToNumberType(int32_t field_id, double number, - Isolate* isolate) { - switch (static_cast<UNumberFormatFields>(field_id)) { - case UNUM_INTEGER_FIELD: - if (std::isfinite(number)) return isolate->factory()->integer_string(); - if (std::isnan(number)) return isolate->factory()->nan_string(); - return isolate->factory()->infinity_string(); - case UNUM_FRACTION_FIELD: - return isolate->factory()->fraction_string(); - case UNUM_DECIMAL_SEPARATOR_FIELD: - return isolate->factory()->decimal_string(); - case UNUM_GROUPING_SEPARATOR_FIELD: - return isolate->factory()->group_string(); - case UNUM_CURRENCY_FIELD: - return isolate->factory()->currency_string(); - case UNUM_PERCENT_FIELD: - return isolate->factory()->percentSign_string(); - case UNUM_SIGN_FIELD: - return number < 0 ? isolate->factory()->minusSign_string() - : isolate->factory()->plusSign_string(); - - case UNUM_EXPONENT_SYMBOL_FIELD: - case UNUM_EXPONENT_SIGN_FIELD: - case UNUM_EXPONENT_FIELD: - // We should never get these because we're not using any scientific - // formatter. - UNREACHABLE(); - return Handle<String>(); - - case UNUM_PERMILL_FIELD: - // We're not creating any permill formatter, and it's not even clear how - // that would be possible with the ICU API. - UNREACHABLE(); - return Handle<String>(); - - default: - UNREACHABLE(); - return Handle<String>(); - } -} - -// 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 cmp_NumberFormatSpan(const NumberFormatSpan& a, - const NumberFormatSpan& b) { - // Regions that start earlier should be encountered earlier. - if (a.begin_pos < b.begin_pos) return true; - if (a.begin_pos > b.begin_pos) return false; - // For regions that start in the same place, regions that last longer should - // be encountered earlier. - if (a.end_pos < b.end_pos) return false; - if (a.end_pos > b.end_pos) return true; - // For regions that are exactly the same, one of them must be the "literal" - // backdrop we added, which has a field_id of -1, so consider higher field_ids - // to be later. - return a.field_id < b.field_id; -} - -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)) { - THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object); - } - - Handle<JSArray> result = factory->NewJSArray(0); - int32_t length = formatted.length(); - if (length == 0) return result; - - std::vector<NumberFormatSpan> regions; - // Add a "literal" backdrop for the entire string. This will be used if no - // other region covers some part of the formatted string. It's possible - // there's another field with exactly the same begin and end as this backdrop, - // in which case the backdrop's field_id of -1 will give it lower priority. - regions.push_back(NumberFormatSpan(-1, 0, formatted.length())); - - { - icu::FieldPosition fp; - while (fp_iter.next(fp)) { - regions.push_back(NumberFormatSpan(fp.getField(), fp.getBeginIndex(), - fp.getEndIndex())); - } - } - - std::vector<NumberFormatSpan> parts = FlattenRegionsToParts(®ions); - - int index = 0; - for (auto it = parts.begin(); it < parts.end(); it++) { - NumberFormatSpan part = *it; - Handle<String> field_type_string = - part.field_id == -1 - ? isolate->factory()->literal_string() - : IcuNumberFieldIdToNumberType(part.field_id, number, isolate); - 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; -} - -MaybeHandle<Object> FormatDateToParts(Isolate* isolate, icu::DateFormat* format, - double date_value) { - Factory* factory = isolate->factory(); - - icu::UnicodeString formatted; - icu::FieldPositionIterator fp_iter; - icu::FieldPosition fp; - UErrorCode status = U_ZERO_ERROR; - format->format(date_value, formatted, &fp_iter, status); - if (U_FAILURE(status)) { - THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object); - } +BUILTIN(V8BreakIteratorSupportedLocalesOf) { + HandleScope scope(isolate); + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); - 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; - 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) { - 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; - } - 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) { - 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_OR_FAILURE( + isolate, Intl::SupportedLocalesOf(isolate, ICUService::kBreakIterator, + locales, options)); } -} // namespace - -// Flattens a list of possibly-overlapping "regions" to a list of -// non-overlapping "parts". At least one of the input regions must span the -// entire space of possible indexes. The regions parameter will sorted in-place -// according to some criteria; this is done for performance to avoid copying the -// input. -std::vector<NumberFormatSpan> FlattenRegionsToParts( - std::vector<NumberFormatSpan>* regions) { - // The intention of this algorithm is that it's used to translate ICU "fields" - // to JavaScript "parts" of a formatted string. Each ICU field and JavaScript - // part has an integer field_id, which corresponds to something like "grouping - // separator", "fraction", or "percent sign", and has a begin and end - // position. Here's a diagram of: - - // var nf = new Intl.NumberFormat(['de'], {style:'currency',currency:'EUR'}); - // nf.formatToParts(123456.78); - - // : 6 - // input regions: 0000000211 7 - // ('-' means -1): ------------ - // formatted string: "123.456,78 €" - // output parts: 0006000211-7 - - // To illustrate the requirements of this algorithm, here's a contrived and - // convoluted example of inputs and expected outputs: - - // : 4 - // : 22 33 3 - // : 11111 22 - // input regions: 0000000 111 - // : ------------ - // formatted string: "abcdefghijkl" - // output parts: 0221340--231 - // (The characters in the formatted string are irrelevant to this function.) - - // We arrange the overlapping input regions like a mountain range where - // smaller regions are "on top" of larger regions, and we output a birds-eye - // view of the mountains, so that smaller regions take priority over larger - // regions. - std::sort(regions->begin(), regions->end(), cmp_NumberFormatSpan); - std::vector<size_t> overlapping_region_index_stack; - // At least one item in regions must be a region spanning the entire string. - // Due to the sorting above, the first item in the vector will be one of them. - overlapping_region_index_stack.push_back(0); - NumberFormatSpan top_region = regions->at(0); - size_t region_iterator = 1; - int32_t entire_size = top_region.end_pos; - - std::vector<NumberFormatSpan> out_parts; - - // The "climber" is a cursor that advances from left to right climbing "up" - // and "down" the mountains. Whenever the climber moves to the right, that - // represents an item of output. - int32_t climber = 0; - while (climber < entire_size) { - int32_t next_region_begin_pos; - if (region_iterator < regions->size()) { - next_region_begin_pos = regions->at(region_iterator).begin_pos; - } else { - // finish off the rest of the input by proceeding to the end. - next_region_begin_pos = entire_size; - } +BUILTIN(NumberFormatSupportedLocalesOf) { + HandleScope scope(isolate); + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); - if (climber < next_region_begin_pos) { - while (top_region.end_pos < next_region_begin_pos) { - if (climber < top_region.end_pos) { - // step down - out_parts.push_back(NumberFormatSpan(top_region.field_id, climber, - top_region.end_pos)); - climber = top_region.end_pos; - } else { - // drop down - } - overlapping_region_index_stack.pop_back(); - top_region = regions->at(overlapping_region_index_stack.back()); - } - if (climber < next_region_begin_pos) { - // cross a plateau/mesa/valley - out_parts.push_back(NumberFormatSpan(top_region.field_id, climber, - next_region_begin_pos)); - climber = next_region_begin_pos; - } - } - if (region_iterator < regions->size()) { - overlapping_region_index_stack.push_back(region_iterator++); - top_region = regions->at(overlapping_region_index_stack.back()); - } - } - return out_parts; + RETURN_RESULT_OR_FAILURE( + isolate, Intl::SupportedLocalesOf(isolate, ICUService::kNumberFormat, + locales, options)); } BUILTIN(NumberFormatPrototypeFormatToParts) { const char* const method = "Intl.NumberFormat.prototype.formatToParts"; HandleScope handle_scope(isolate); - CHECK_RECEIVER(JSObject, number_format_holder, method); - - if (!Intl::IsObjectOfType(isolate, number_format_holder, - Intl::Type::kNumberFormat)) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, - NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, - isolate->factory()->NewStringFromAsciiChecked(method), - number_format_holder)); - } + CHECK_RECEIVER(JSNumberFormat, number_format, method); Handle<Object> x; if (args.length() >= 2) { @@ -460,12 +163,33 @@ BUILTIN(NumberFormatPrototypeFormatToParts) { x = isolate->factory()->nan_value(); } - icu::DecimalFormat* number_format = - NumberFormat::UnpackNumberFormat(number_format_holder); - CHECK_NOT_NULL(number_format); + RETURN_RESULT_OR_FAILURE(isolate, JSNumberFormat::FormatToParts( + isolate, number_format, x->Number())); +} + +BUILTIN(DateTimeFormatPrototypeResolvedOptions) { + const char* const method = "Intl.DateTimeFormat.prototype.resolvedOptions"; + HandleScope scope(isolate); + CHECK_RECEIVER(JSReceiver, format_holder, method); + + // 3. Let dtf be ? UnwrapDateTimeFormat(dtf). + Handle<JSDateTimeFormat> date_time_format; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, date_time_format, + JSDateTimeFormat::UnwrapDateTimeFormat(isolate, format_holder)); + + RETURN_RESULT_OR_FAILURE( + isolate, JSDateTimeFormat::ResolvedOptions(isolate, date_time_format)); +} + +BUILTIN(DateTimeFormatSupportedLocalesOf) { + HandleScope scope(isolate); + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); RETURN_RESULT_OR_FAILURE( - isolate, FormatNumberToParts(isolate, number_format, x->Number())); + isolate, Intl::SupportedLocalesOf(isolate, ICUService::kDateFormat, + locales, options)); } BUILTIN(DateTimeFormatPrototypeFormatToParts) { @@ -474,13 +198,14 @@ BUILTIN(DateTimeFormatPrototypeFormatToParts) { CHECK_RECEIVER(JSObject, date_format_holder, method); Factory* factory = isolate->factory(); - if (!Intl::IsObjectOfType(isolate, date_format_holder, - Intl::Type::kDateTimeFormat)) { + if (!date_format_holder->IsJSDateTimeFormat()) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, factory->NewStringFromAsciiChecked(method), date_format_holder)); } + Handle<JSDateTimeFormat> dtf = + Handle<JSDateTimeFormat>::cast(date_format_holder); Handle<Object> x = args.atOrUndefined(isolate, 1); if (x->IsUndefined(isolate)) { @@ -496,12 +221,142 @@ BUILTIN(DateTimeFormatPrototypeFormatToParts) { isolate, NewRangeError(MessageTemplate::kInvalidTimeValue)); } - icu::SimpleDateFormat* date_format = - DateFormat::UnpackDateFormat(date_format_holder); - CHECK_NOT_NULL(date_format); + RETURN_RESULT_OR_FAILURE( + isolate, JSDateTimeFormat::FormatToParts(isolate, dtf, date_value)); +} + +namespace { +Handle<JSFunction> CreateBoundFunction(Isolate* isolate, + Handle<JSObject> object, + Builtins::Name builtin_id, int len) { + Handle<NativeContext> native_context(isolate->context()->native_context(), + isolate); + Handle<Context> context = isolate->factory()->NewBuiltinContext( + native_context, + static_cast<int>(Intl::BoundFunctionContextSlot::kLength)); + + context->set(static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction), + *object); + + Handle<SharedFunctionInfo> info = + isolate->factory()->NewSharedFunctionInfoForBuiltin( + isolate->factory()->empty_string(), builtin_id, kNormalFunction); + info->set_internal_formal_parameter_count(len); + info->set_length(len); + + Handle<Map> map = isolate->strict_function_without_prototype_map(); + + Handle<JSFunction> new_bound_function = + isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context); + return new_bound_function; +} + +/** + * Common code shared between DateTimeFormatConstructor and + * NumberFormatConstrutor + */ +template <class T> +Object* FormatConstructor(BuiltinArguments args, Isolate* isolate, + Handle<Object> constructor, const char* method) { + 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); + + // 2. Let format be ? OrdinaryCreateFromConstructor(newTarget, + // "%<T>Prototype%", ...). + + Handle<JSObject> format_obj; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, format_obj, + JSObject::New(target, new_target, Handle<AllocationSite>::null())); + Handle<T> format = Handle<T>::cast(format_obj); + + // 3. Perform ? Initialize<T>(Format, locales, options). + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, format, T::Initialize(isolate, format, locales, options)); + // 4. Let this be the this value. + Handle<Object> receiver = args.receiver(); + + // 5. If NewTarget is undefined and ? InstanceofOperator(this, %<T>%) + // is true, then + // + // Look up the intrinsic value that has been stored on the context. + // Call the instanceof function + Handle<Object> is_instance_of_obj; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, is_instance_of_obj, + Object::InstanceOf(isolate, receiver, constructor)); + + // Get the boolean value of the result + bool is_instance_of = is_instance_of_obj->BooleanValue(isolate); + + if (args.new_target()->IsUndefined(isolate) && is_instance_of) { + if (!receiver->IsJSReceiver()) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, + NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, + isolate->factory()->NewStringFromAsciiChecked(method), + receiver)); + } + Handle<JSReceiver> rec = Handle<JSReceiver>::cast(receiver); + // a. Perform ? DefinePropertyOrThrow(this, + // %Intl%.[[FallbackSymbol]], PropertyDescriptor{ [[Value]]: format, + // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). + PropertyDescriptor desc; + desc.set_value(format); + desc.set_writable(false); + desc.set_enumerable(false); + desc.set_configurable(false); + Maybe<bool> success = JSReceiver::DefineOwnProperty( + isolate, rec, isolate->factory()->intl_fallback_symbol(), &desc, + kThrowOnError); + MAYBE_RETURN(success, ReadOnlyRoots(isolate).exception()); + CHECK(success.FromJust()); + // b. b. Return this. + return *receiver; + } + // 6. Return format. + return *format; +} + +} // namespace + +BUILTIN(NumberFormatConstructor) { + HandleScope scope(isolate); + + isolate->CountUsage(v8::Isolate::UseCounterFeature::kNumberFormat); + + return FormatConstructor<JSNumberFormat>( + args, isolate, isolate->intl_number_format_function(), + "Intl.NumberFormat"); +} + +BUILTIN(NumberFormatPrototypeResolvedOptions) { + HandleScope scope(isolate); + const char* const method = "Intl.NumberFormat.prototype.resolvedOptions"; + + // 1. Let nf be the this value. + // 2. If Type(nf) is not Object, throw a TypeError exception. + CHECK_RECEIVER(JSReceiver, number_format_holder, method); + + // 3. Let nf be ? UnwrapNumberFormat(nf) + Handle<JSNumberFormat> number_format; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, number_format, + JSNumberFormat::UnwrapNumberFormat(isolate, number_format_holder)); - RETURN_RESULT_OR_FAILURE(isolate, - FormatDateToParts(isolate, date_format, date_value)); + return *JSNumberFormat::ResolvedOptions(isolate, number_format); } BUILTIN(NumberFormatPrototypeFormatNumber) { @@ -513,17 +368,12 @@ BUILTIN(NumberFormatPrototypeFormatNumber) { CHECK_RECEIVER(JSReceiver, receiver, method); // 3. Let nf be ? UnwrapNumberFormat(nf). - Handle<JSObject> number_format_holder; + Handle<JSNumberFormat> number_format; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, number_format_holder, - NumberFormat::Unwrap(isolate, receiver, method)); + isolate, number_format, + JSNumberFormat::UnwrapNumberFormat(isolate, receiver)); - DCHECK(Intl::IsObjectOfType(isolate, number_format_holder, - Intl::Type::kNumberFormat)); - - Handle<Object> bound_format = Handle<Object>( - number_format_holder->GetEmbedderField(NumberFormat::kBoundFormatIndex), - isolate); + Handle<Object> bound_format(number_format->bound_format(), isolate); // 4. If nf.[[BoundFormat]] is undefined, then if (!bound_format->IsUndefined(isolate)) { @@ -532,29 +382,11 @@ BUILTIN(NumberFormatPrototypeFormatNumber) { return *bound_format; } - Handle<NativeContext> native_context(isolate->context()->native_context(), - isolate); - - Handle<Context> context = isolate->factory()->NewBuiltinContext( - native_context, NumberFormat::ContextSlot::kLength); - - // 4. b. Set F.[[NumberFormat]] to nf. - context->set(NumberFormat::ContextSlot::kNumberFormat, *number_format_holder); - - Handle<SharedFunctionInfo> info = Handle<SharedFunctionInfo>( - native_context->number_format_internal_format_number_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 - // Number Format Functions (11.1.4). - Handle<JSFunction> new_bound_format_function = - isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context); + Handle<JSFunction> new_bound_format_function = CreateBoundFunction( + isolate, number_format, Builtins::kNumberFormatInternalFormatNumber, 1); // 4. c. Set nf.[[BoundFormat]] to F. - number_format_holder->SetEmbedderField(NumberFormat::kBoundFormatIndex, - *new_bound_format_function); + number_format->set_bound_format(*new_bound_format_function); // 5. Return nf.[[BoundFormat]]. return *new_bound_format_function; @@ -566,14 +398,12 @@ BUILTIN(NumberFormatInternalFormatNumber) { Handle<Context> context = Handle<Context>(isolate->context(), isolate); // 1. Let nf be F.[[NumberFormat]]. - Handle<JSObject> number_format_holder = Handle<JSObject>( - JSObject::cast(context->get(NumberFormat::ContextSlot::kNumberFormat)), - isolate); - // 2. Assert: Type(nf) is Object and nf has an // [[InitializedNumberFormat]] internal slot. - DCHECK(Intl::IsObjectOfType(isolate, number_format_holder, - Intl::Type::kNumberFormat)); + Handle<JSNumberFormat> number_format = Handle<JSNumberFormat>( + JSNumberFormat::cast(context->get( + static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))), + isolate); // 3. If value is not provided, let value be undefined. Handle<Object> value = args.atOrUndefined(isolate, 1); @@ -590,8 +420,18 @@ BUILTIN(NumberFormatInternalFormatNumber) { double number = number_obj->Number(); // Return FormatNumber(nf, x). - RETURN_RESULT_OR_FAILURE(isolate, NumberFormat::FormatNumber( - isolate, number_format_holder, number)); + RETURN_RESULT_OR_FAILURE( + isolate, JSNumberFormat::FormatNumber(isolate, number_format, number)); +} + +BUILTIN(DateTimeFormatConstructor) { + HandleScope scope(isolate); + + isolate->CountUsage(v8::Isolate::UseCounterFeature::kDateTimeFormat); + + return FormatConstructor<JSDateTimeFormat>( + args, isolate, isolate->intl_date_time_format_function(), + "Intl.DateTimeFormat"); } BUILTIN(DateTimeFormatPrototypeFormat) { @@ -603,16 +443,12 @@ BUILTIN(DateTimeFormatPrototypeFormat) { CHECK_RECEIVER(JSReceiver, receiver, method); // 3. Let dtf be ? UnwrapDateTimeFormat(dtf). - Handle<JSObject> date_format_holder; + Handle<JSDateTimeFormat> format; ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, date_format_holder, - DateFormat::Unwrap(isolate, receiver, method)); - DCHECK(Intl::IsObjectOfType(isolate, date_format_holder, - Intl::Type::kDateTimeFormat)); + isolate, format, + JSDateTimeFormat::UnwrapDateTimeFormat(isolate, receiver)); - Handle<Object> bound_format = Handle<Object>( - date_format_holder->GetEmbedderField(DateFormat::kBoundFormatIndex), - isolate); + Handle<Object> bound_format = Handle<Object>(format->bound_format(), isolate); // 4. If dtf.[[BoundFormat]] is undefined, then if (!bound_format->IsUndefined(isolate)) { @@ -621,26 +457,11 @@ BUILTIN(DateTimeFormatPrototypeFormat) { return *bound_format; } - Handle<NativeContext> native_context(isolate->context()->native_context(), - isolate); - Handle<Context> context = isolate->factory()->NewBuiltinContext( - native_context, DateFormat::ContextSlot::kLength); - - // 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); + Handle<JSFunction> new_bound_format_function = CreateBoundFunction( + isolate, format, Builtins::kDateTimeFormatInternalFormat, 1); // 4.c. Set dtf.[[BoundFormat]] to F. - date_format_holder->SetEmbedderField(DateFormat::kBoundFormatIndex, - *new_bound_format_function); + format->set_bound_format(*new_bound_format_function); // 5. Return dtf.[[BoundFormat]]. return *new_bound_format_function; @@ -651,23 +472,24 @@ BUILTIN(DateTimeFormatInternalFormat) { 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<JSDateTimeFormat> date_format_holder = Handle<JSDateTimeFormat>( + JSDateTimeFormat::cast(context->get( + static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))), + isolate); Handle<Object> date = args.atOrUndefined(isolate, 1); - RETURN_RESULT_OR_FAILURE( - isolate, DateFormat::DateTimeFormat(isolate, date_format_holder, date)); + RETURN_RESULT_OR_FAILURE(isolate, JSDateTimeFormat::DateTimeFormat( + isolate, date_format_holder, date)); } BUILTIN(ListFormatConstructor) { HandleScope scope(isolate); + + isolate->CountUsage(v8::Isolate::UseCounterFeature::kListFormat); + // 1. If NewTarget is undefined, throw a TypeError exception. if (args.new_target()->IsUndefined(isolate)) { // [[Call]] THROW_NEW_ERROR_RETURN_FAILURE( @@ -682,8 +504,9 @@ BUILTIN(ListFormatConstructor) { Handle<JSObject> result; // 2. Let listFormat be OrdinaryCreateFromConstructor(NewTarget, // "%ListFormatPrototype%"). - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, - JSObject::New(target, new_target)); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, + JSObject::New(target, new_target, Handle<AllocationSite>::null())); Handle<JSListFormat> format = Handle<JSListFormat>::cast(result); format->set_flags(0); @@ -691,8 +514,8 @@ BUILTIN(ListFormatConstructor) { Handle<Object> options = args.atOrUndefined(isolate, 2); // 3. Return InitializeListFormat(listFormat, locales, options). - RETURN_RESULT_OR_FAILURE(isolate, JSListFormat::InitializeListFormat( - isolate, format, locales, options)); + RETURN_RESULT_OR_FAILURE( + isolate, JSListFormat::Initialize(isolate, format, locales, options)); } BUILTIN(ListFormatPrototypeResolvedOptions) { @@ -702,42 +525,62 @@ BUILTIN(ListFormatPrototypeResolvedOptions) { return *JSListFormat::ResolvedOptions(isolate, format_holder); } +BUILTIN(ListFormatSupportedLocalesOf) { + HandleScope scope(isolate); + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); + + RETURN_RESULT_OR_FAILURE( + isolate, Intl::SupportedLocalesOf(isolate, ICUService::kListFormatter, + locales, options)); +} + 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. + Handle<JSObject> locale; + // 6. Let locale be ? OrdinaryCreateFromConstructor(NewTarget, + // %LocalePrototype%, internalSlotsList). + ASSIGN_RETURN_ON_EXCEPTION( + isolate, locale, + JSObject::New(constructor, new_target, Handle<AllocationSite>::null()), + JSLocale); + + // 7. If Type(tag) is not String or Object, throw a TypeError exception. if (!tag->IsString() && !tag->IsJSReceiver()) { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kLocaleNotEmpty), JSLocale); } Handle<String> locale_string; + // 8. If Type(tag) is Object and tag has an [[InitializedLocale]] internal + // slot, then if (tag->IsJSLocale() && Handle<JSLocale>::cast(tag)->locale()->IsString()) { + // a. Let tag be tag.[[Locale]]. locale_string = Handle<String>(Handle<JSLocale>::cast(tag)->locale(), isolate); - } else { + } else { // 9. Else, + // a. Let tag be ? ToString(tag). ASSIGN_RETURN_ON_EXCEPTION(isolate, locale_string, Object::ToString(isolate, tag), JSLocale); } Handle<JSReceiver> options_object; - if (options->IsNullOrUndefined(isolate)) { - // Make empty options bag. + // 10. If options is undefined, then + if (options->IsUndefined(isolate)) { + // a. Let options be ! ObjectCreate(null). options_object = isolate->factory()->NewJSObjectWithNullProto(); - } else { + } else { // 11. Else + // a. Let options be ? ToObject(options). ASSIGN_RETURN_ON_EXCEPTION(isolate, options_object, Object::ToObject(isolate, options), JSLocale); } - return JSLocale::InitializeLocale(isolate, Handle<JSLocale>::cast(result), - locale_string, options_object); + return JSLocale::Initialize(isolate, Handle<JSLocale>::cast(locale), + locale_string, options_object); } } // namespace @@ -745,6 +588,9 @@ MaybeHandle<JSLocale> CreateLocale(Isolate* isolate, // Intl.Locale implementation BUILTIN(LocaleConstructor) { HandleScope scope(isolate); + + isolate->CountUsage(v8::Isolate::UseCounterFeature::kLocale); + if (args.new_target()->IsUndefined(isolate)) { // [[Call]] THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, @@ -786,186 +632,17 @@ BUILTIN(LocalePrototypeMinimize) { 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; - } +BUILTIN(RelativeTimeFormatSupportedLocalesOf) { + HandleScope scope(isolate); + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); - return factory->NewStringFromTwoByte(Vector<const uint16_t>( - reinterpret_cast<const uint16_t*>(formatted.getBuffer()), - formatted.length())); + RETURN_RESULT_OR_FAILURE( + isolate, + Intl::SupportedLocalesOf(isolate, ICUService::kRelativeDateTimeFormatter, + locales, options)); } -} // namespace - BUILTIN(RelativeTimeFormatPrototypeFormat) { HandleScope scope(isolate); // 1. Let relativeTimeFormat be the this value. @@ -974,9 +651,12 @@ BUILTIN(RelativeTimeFormatPrototypeFormat) { // true, throw a TypeError exception. CHECK_RECEIVER(JSRelativeTimeFormat, format_holder, "Intl.RelativeTimeFormat.prototype.format"); - RETURN_RESULT_OR_FAILURE(isolate, - RelativeTimeFormatPrototypeFormatCommon( - args, isolate, format_holder, "format", false)); + Handle<Object> value_obj = args.atOrUndefined(isolate, 1); + Handle<Object> unit_obj = args.atOrUndefined(isolate, 2); + + RETURN_RESULT_OR_FAILURE( + isolate, JSRelativeTimeFormat::Format(isolate, value_obj, unit_obj, + format_holder, "format", false)); } BUILTIN(RelativeTimeFormatPrototypeFormatToParts) { @@ -987,9 +667,11 @@ BUILTIN(RelativeTimeFormatPrototypeFormatToParts) { // 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)); + Handle<Object> value_obj = args.atOrUndefined(isolate, 1); + Handle<Object> unit_obj = args.atOrUndefined(isolate, 2); + RETURN_RESULT_OR_FAILURE(isolate, JSRelativeTimeFormat::Format( + isolate, value_obj, unit_obj, + format_holder, "formatToParts", true)); } // Locale getters. @@ -1033,7 +715,7 @@ BUILTIN(LocalePrototypeCaseFirst) { HandleScope scope(isolate); CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.caseFirst"); - return locale_holder->case_first(); + return *(locale_holder->CaseFirstAsString()); } BUILTIN(LocalePrototypeCollation) { @@ -1047,14 +729,23 @@ BUILTIN(LocalePrototypeHourCycle) { HandleScope scope(isolate); CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.hourCycle"); - return locale_holder->hour_cycle(); + return *(locale_holder->HourCycleAsString()); } BUILTIN(LocalePrototypeNumeric) { HandleScope scope(isolate); CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.numeric"); - return locale_holder->numeric(); + switch (locale_holder->numeric()) { + case JSLocale::Numeric::TRUE_VALUE: + return *(isolate->factory()->true_value()); + case JSLocale::Numeric::FALSE_VALUE: + return *(isolate->factory()->false_value()); + case JSLocale::Numeric::NOTSET: + return *(isolate->factory()->undefined_value()); + case JSLocale::Numeric::COUNT: + UNREACHABLE(); + } } BUILTIN(LocalePrototypeNumberingSystem) { @@ -1074,6 +765,9 @@ BUILTIN(LocalePrototypeToString) { BUILTIN(RelativeTimeFormatConstructor) { HandleScope scope(isolate); + + isolate->CountUsage(v8::Isolate::UseCounterFeature::kRelativeTimeFormat); + // 1. If NewTarget is undefined, throw a TypeError exception. if (args.new_target()->IsUndefined(isolate)) { // [[Call]] THROW_NEW_ERROR_RETURN_FAILURE( @@ -1089,8 +783,9 @@ BUILTIN(RelativeTimeFormatConstructor) { // 2. Let relativeTimeFormat be // ! OrdinaryCreateFromConstructor(NewTarget, // "%RelativeTimeFormatPrototype%"). - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, - JSObject::New(target, new_target)); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, + JSObject::New(target, new_target, Handle<AllocationSite>::null())); Handle<JSRelativeTimeFormat> format = Handle<JSRelativeTimeFormat>::cast(result); format->set_flags(0); @@ -1100,9 +795,8 @@ BUILTIN(RelativeTimeFormatConstructor) { // 3. Return ? InitializeRelativeTimeFormat(relativeTimeFormat, locales, // options). - RETURN_RESULT_OR_FAILURE(isolate, - JSRelativeTimeFormat::InitializeRelativeTimeFormat( - isolate, format, locales, options)); + RETURN_RESULT_OR_FAILURE(isolate, JSRelativeTimeFormat::Initialize( + isolate, format, locales, options)); } BUILTIN(RelativeTimeFormatPrototypeResolvedOptions) { @@ -1114,7 +808,11 @@ BUILTIN(RelativeTimeFormatPrototypeResolvedOptions) { BUILTIN(StringPrototypeToLocaleLowerCase) { HandleScope scope(isolate); + + isolate->CountUsage(v8::Isolate::UseCounterFeature::kStringToLocaleLowerCase); + TO_THIS_STRING(string, "String.prototype.toLocaleLowerCase"); + RETURN_RESULT_OR_FAILURE( isolate, Intl::StringLocaleConvertCase(isolate, string, false, args.atOrUndefined(isolate, 1))); @@ -1122,7 +820,11 @@ BUILTIN(StringPrototypeToLocaleLowerCase) { BUILTIN(StringPrototypeToLocaleUpperCase) { HandleScope scope(isolate); + + isolate->CountUsage(v8::Isolate::UseCounterFeature::kStringToLocaleUpperCase); + TO_THIS_STRING(string, "String.prototype.toLocaleUpperCase"); + RETURN_RESULT_OR_FAILURE( isolate, Intl::StringLocaleConvertCase(isolate, string, true, args.atOrUndefined(isolate, 1))); @@ -1131,6 +833,8 @@ BUILTIN(StringPrototypeToLocaleUpperCase) { BUILTIN(PluralRulesConstructor) { HandleScope scope(isolate); + isolate->CountUsage(v8::Isolate::UseCounterFeature::kPluralRules); + // 1. If NewTarget is undefined, throw a TypeError exception. if (args.new_target()->IsUndefined(isolate)) { // [[Call]] THROW_NEW_ERROR_RETURN_FAILURE( @@ -1152,19 +856,61 @@ BUILTIN(PluralRulesConstructor) { // [[MinimumFractionDigits]], [[MaximumFractionDigits]], // [[MinimumSignificantDigits]], [[MaximumSignificantDigits]] »). Handle<JSObject> plural_rules_obj; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, plural_rules_obj, - JSObject::New(target, new_target)); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, plural_rules_obj, + JSObject::New(target, new_target, Handle<AllocationSite>::null())); 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)); + isolate, + JSPluralRules::Initialize(isolate, plural_rules, locales, options)); +} + +BUILTIN(PluralRulesPrototypeResolvedOptions) { + HandleScope scope(isolate); + CHECK_RECEIVER(JSPluralRules, plural_rules_holder, + "Intl.PluralRules.prototype.resolvedOptions"); + return *JSPluralRules::ResolvedOptions(isolate, plural_rules_holder); +} + +BUILTIN(PluralRulesPrototypeSelect) { + HandleScope scope(isolate); + + // 1. Let pr be the this value. + // 2. If Type(pr) is not Object, throw a TypeError exception. + // 3. If pr does not have an [[InitializedPluralRules]] internal slot, throw a + // TypeError exception. + CHECK_RECEIVER(JSPluralRules, plural_rules, + "Intl.PluralRules.prototype.select"); + + // 4. Let n be ? ToNumber(value). + Handle<Object> number = args.atOrUndefined(isolate, 1); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number, + Object::ToNumber(isolate, number)); + double number_double = number->Number(); + + // 5. Return ? ResolvePlural(pr, n). + RETURN_RESULT_OR_FAILURE(isolate, JSPluralRules::ResolvePlural( + isolate, plural_rules, number_double)); +} + +BUILTIN(PluralRulesSupportedLocalesOf) { + HandleScope scope(isolate); + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); + + RETURN_RESULT_OR_FAILURE( + isolate, Intl::SupportedLocalesOf(isolate, ICUService::kPluralRules, + locales, options)); } BUILTIN(CollatorConstructor) { HandleScope scope(isolate); + + isolate->CountUsage(v8::Isolate::UseCounterFeature::kCollator); + Handle<JSReceiver> new_target; // 1. If NewTarget is undefined, let newTarget be the active // function object, else let newTarget be NewTarget. @@ -1183,14 +929,31 @@ BUILTIN(CollatorConstructor) { // 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)); + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, collator_obj, + JSObject::New(target, new_target, Handle<AllocationSite>::null())); 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)); + RETURN_RESULT_OR_FAILURE( + isolate, JSCollator::Initialize(isolate, collator, locales, options)); +} + +BUILTIN(CollatorPrototypeResolvedOptions) { + HandleScope scope(isolate); + CHECK_RECEIVER(JSCollator, collator_holder, + "Intl.Collator.prototype.resolvedOptions"); + return *JSCollator::ResolvedOptions(isolate, collator_holder); +} + +BUILTIN(CollatorSupportedLocalesOf) { + HandleScope scope(isolate); + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); + + RETURN_RESULT_OR_FAILURE( + isolate, Intl::SupportedLocalesOf(isolate, ICUService::kCollator, locales, + options)); } BUILTIN(CollatorPrototypeCompare) { @@ -1211,21 +974,8 @@ BUILTIN(CollatorPrototypeCompare) { 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); + Handle<JSFunction> new_bound_compare_function = CreateBoundFunction( + isolate, collator, Builtins::kCollatorInternalCompare, 2); // 4.c. Set collator.[[BoundCompare]] to F. collator->set_bound_compare(*new_bound_compare_function); @@ -1242,7 +992,8 @@ BUILTIN(CollatorInternalCompare) { // 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)), + JSCollator::cast(context->get( + static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))), isolate); // 3. If x is not provided, let x be undefined. @@ -1263,71 +1014,280 @@ BUILTIN(CollatorInternalCompare) { return *Intl::CompareStrings(isolate, collator_holder, string_x, string_y); } -BUILTIN(BreakIteratorPrototypeAdoptText) { - const char* const method = "get Intl.v8BreakIterator.prototype.adoptText"; +BUILTIN(SegmenterConstructor) { HandleScope scope(isolate); - CHECK_RECEIVER(JSObject, break_iterator_holder, method); - if (!Intl::IsObjectOfType(isolate, break_iterator_holder, - Intl::Type::kBreakIterator)) { + isolate->CountUsage(v8::Isolate::UseCounterFeature::kSegmenter); + + // 1. If NewTarget is undefined, throw a TypeError exception. + if (args.new_target()->IsUndefined(isolate)) { // [[Call]] THROW_NEW_ERROR_RETURN_FAILURE( - isolate, - NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, - isolate->factory()->NewStringFromAsciiChecked(method), - break_iterator_holder)); + isolate, NewTypeError(MessageTemplate::kConstructorNotFunction, + isolate->factory()->NewStringFromStaticChars( + "Intl.Segmenter"))); } + // [[Construct]] + Handle<JSFunction> target = args.target(); + Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target()); - Handle<Object> bound_adopt_text = - Handle<Object>(break_iterator_holder->GetEmbedderField( - V8BreakIterator::kBoundAdoptTextIndex), - isolate); + Handle<JSObject> result; + // 2. Let segmenter be OrdinaryCreateFromConstructor(NewTarget, + // "%SegmenterPrototype%"). + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, + JSObject::New(target, new_target, Handle<AllocationSite>::null())); + Handle<JSSegmenter> segmenter = Handle<JSSegmenter>::cast(result); + segmenter->set_flags(0); - if (!bound_adopt_text->IsUndefined(isolate)) { - DCHECK(bound_adopt_text->IsJSFunction()); - return *bound_adopt_text; + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); + + RETURN_RESULT_OR_FAILURE( + isolate, JSSegmenter::Initialize(isolate, segmenter, locales, options)); +} + +BUILTIN(SegmenterSupportedLocalesOf) { + HandleScope scope(isolate); + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); + + RETURN_RESULT_OR_FAILURE( + isolate, Intl::SupportedLocalesOf(isolate, ICUService::kSegmenter, + locales, options)); +} + +BUILTIN(SegmenterPrototypeResolvedOptions) { + HandleScope scope(isolate); + CHECK_RECEIVER(JSSegmenter, segmenter_holder, + "Intl.Segmenter.prototype.resolvedOptions"); + return *JSSegmenter::ResolvedOptions(isolate, segmenter_holder); +} + +BUILTIN(V8BreakIteratorConstructor) { + HandleScope scope(isolate); + Handle<JSReceiver> new_target; + + if (args.new_target()->IsUndefined(isolate)) { + new_target = args.target(); + } else { + new_target = Handle<JSReceiver>::cast(args.new_target()); } - Handle<NativeContext> native_context(isolate->context()->native_context(), - isolate); - Handle<Context> context = isolate->factory()->NewBuiltinContext( - native_context, static_cast<int>(V8BreakIterator::ContextSlot::kLength)); + // [[Construct]] + Handle<JSFunction> target = args.target(); - context->set(static_cast<int>(V8BreakIterator::ContextSlot::kV8BreakIterator), - *break_iterator_holder); + Handle<Object> locales = args.atOrUndefined(isolate, 1); + Handle<Object> options = args.atOrUndefined(isolate, 2); - 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<JSObject> break_iterator_obj; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, break_iterator_obj, + JSObject::New(target, new_target, Handle<AllocationSite>::null())); + Handle<JSV8BreakIterator> break_iterator = + Handle<JSV8BreakIterator>::cast(break_iterator_obj); - Handle<JSFunction> new_bound_adopt_text_function = - isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context); + RETURN_RESULT_OR_FAILURE( + isolate, + JSV8BreakIterator::Initialize(isolate, break_iterator, locales, options)); +} - break_iterator_holder->SetEmbedderField(V8BreakIterator::kBoundAdoptTextIndex, - *new_bound_adopt_text_function); +BUILTIN(V8BreakIteratorPrototypeResolvedOptions) { + HandleScope scope(isolate); + CHECK_RECEIVER(JSV8BreakIterator, break_iterator, + "Intl.v8BreakIterator.prototype.resolvedOptions"); + return *JSV8BreakIterator::ResolvedOptions(isolate, break_iterator); +} + +BUILTIN(V8BreakIteratorPrototypeAdoptText) { + const char* const method = "get Intl.v8BreakIterator.prototype.adoptText"; + HandleScope scope(isolate); + + CHECK_RECEIVER(JSV8BreakIterator, break_iterator, method); + + Handle<Object> bound_adopt_text(break_iterator->bound_adopt_text(), isolate); + if (!bound_adopt_text->IsUndefined(isolate)) { + DCHECK(bound_adopt_text->IsJSFunction()); + return *bound_adopt_text; + } + Handle<JSFunction> new_bound_adopt_text_function = CreateBoundFunction( + isolate, break_iterator, Builtins::kV8BreakIteratorInternalAdoptText, 1); + break_iterator->set_bound_adopt_text(*new_bound_adopt_text_function); return *new_bound_adopt_text_function; } -BUILTIN(BreakIteratorInternalAdoptText) { +BUILTIN(V8BreakIteratorInternalAdoptText) { 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))), + Handle<JSV8BreakIterator> break_iterator_holder = Handle<JSV8BreakIterator>( + JSV8BreakIterator::cast(context->get( + static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))), 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); + JSV8BreakIterator::AdoptText(isolate, break_iterator_holder, text); return ReadOnlyRoots(isolate).undefined_value(); } +BUILTIN(V8BreakIteratorPrototypeFirst) { + const char* const method = "get Intl.v8BreakIterator.prototype.first"; + HandleScope scope(isolate); + + CHECK_RECEIVER(JSV8BreakIterator, break_iterator_holder, method); + + Handle<Object> bound_first(break_iterator_holder->bound_first(), isolate); + if (!bound_first->IsUndefined(isolate)) { + DCHECK(bound_first->IsJSFunction()); + return *bound_first; + } + + Handle<JSFunction> new_bound_first_function = + CreateBoundFunction(isolate, break_iterator_holder, + Builtins::kV8BreakIteratorInternalFirst, 0); + break_iterator_holder->set_bound_first(*new_bound_first_function); + return *new_bound_first_function; +} + +BUILTIN(V8BreakIteratorInternalFirst) { + HandleScope scope(isolate); + Handle<Context> context = Handle<Context>(isolate->context(), isolate); + + Handle<JSV8BreakIterator> break_iterator_holder = Handle<JSV8BreakIterator>( + JSV8BreakIterator::cast(context->get( + static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))), + isolate); + + icu::BreakIterator* break_iterator = + break_iterator_holder->break_iterator()->raw(); + CHECK_NOT_NULL(break_iterator); + + return *isolate->factory()->NewNumberFromInt(break_iterator->first()); +} + +BUILTIN(V8BreakIteratorPrototypeNext) { + const char* const method = "get Intl.v8BreakIterator.prototype.next"; + HandleScope scope(isolate); + + CHECK_RECEIVER(JSV8BreakIterator, break_iterator_holder, method); + + Handle<Object> bound_next(break_iterator_holder->bound_next(), isolate); + if (!bound_next->IsUndefined(isolate)) { + DCHECK(bound_next->IsJSFunction()); + return *bound_next; + } + + Handle<JSFunction> new_bound_next_function = + CreateBoundFunction(isolate, break_iterator_holder, + Builtins::kV8BreakIteratorInternalNext, 0); + break_iterator_holder->set_bound_next(*new_bound_next_function); + return *new_bound_next_function; +} + +BUILTIN(V8BreakIteratorInternalNext) { + HandleScope scope(isolate); + Handle<Context> context = Handle<Context>(isolate->context(), isolate); + + Handle<JSV8BreakIterator> break_iterator_holder = Handle<JSV8BreakIterator>( + JSV8BreakIterator::cast(context->get( + static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))), + isolate); + + icu::BreakIterator* break_iterator = + break_iterator_holder->break_iterator()->raw(); + CHECK_NOT_NULL(break_iterator); + + return *isolate->factory()->NewNumberFromInt(break_iterator->next()); +} + +BUILTIN(V8BreakIteratorPrototypeCurrent) { + const char* const method = "get Intl.v8BreakIterator.prototype.current"; + HandleScope scope(isolate); + + CHECK_RECEIVER(JSV8BreakIterator, break_iterator_holder, method); + + Handle<Object> bound_current(break_iterator_holder->bound_current(), isolate); + if (!bound_current->IsUndefined(isolate)) { + DCHECK(bound_current->IsJSFunction()); + return *bound_current; + } + + Handle<JSFunction> new_bound_current_function = + CreateBoundFunction(isolate, break_iterator_holder, + Builtins::kV8BreakIteratorInternalCurrent, 0); + break_iterator_holder->set_bound_current(*new_bound_current_function); + return *new_bound_current_function; +} + +BUILTIN(V8BreakIteratorInternalCurrent) { + HandleScope scope(isolate); + Handle<Context> context = Handle<Context>(isolate->context(), isolate); + + Handle<JSV8BreakIterator> break_iterator_holder = Handle<JSV8BreakIterator>( + JSV8BreakIterator::cast(context->get( + static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))), + isolate); + + icu::BreakIterator* break_iterator = + break_iterator_holder->break_iterator()->raw(); + CHECK_NOT_NULL(break_iterator); + + return *isolate->factory()->NewNumberFromInt(break_iterator->current()); +} + +BUILTIN(V8BreakIteratorPrototypeBreakType) { + const char* const method = "get Intl.v8BreakIterator.prototype.breakType"; + HandleScope scope(isolate); + + CHECK_RECEIVER(JSV8BreakIterator, break_iterator_holder, method); + + Handle<Object> bound_break_type(break_iterator_holder->bound_break_type(), + isolate); + if (!bound_break_type->IsUndefined(isolate)) { + DCHECK(bound_break_type->IsJSFunction()); + return *bound_break_type; + } + + Handle<JSFunction> new_bound_break_type_function = + CreateBoundFunction(isolate, break_iterator_holder, + Builtins::kV8BreakIteratorInternalBreakType, 0); + break_iterator_holder->set_bound_break_type(*new_bound_break_type_function); + return *new_bound_break_type_function; +} + +BUILTIN(V8BreakIteratorInternalBreakType) { + HandleScope scope(isolate); + Handle<Context> context = Handle<Context>(isolate->context(), isolate); + + Handle<JSV8BreakIterator> break_iterator_holder = Handle<JSV8BreakIterator>( + JSV8BreakIterator::cast(context->get( + static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))), + isolate); + + icu::BreakIterator* break_iterator = + break_iterator_holder->break_iterator()->raw(); + CHECK_NOT_NULL(break_iterator); + + int32_t status = break_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) { + return ReadOnlyRoots(isolate).number_string(); + } 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"); + } +} + } // namespace internal } // namespace v8 |