Commit 6c1e67f8 authored by Josh Wolfe's avatar Josh Wolfe Committed by Commit Bot

[intl] Implement Intl.NumberFormat.prototype.formatToParts

Includes unit tests for the post-processing step
flatten_regions_to_parts().

Bug: v8:5244
TBR: bmeurer@chromium.org, rossberg@chromium.org
Cq-Include-Trybots: master.tryserver.v8:v8_linux_noi18n_rel_ng
Change-Id: I306dd1721cc00c5820b061f14c4b6866f8d938f6
Reviewed-on: https://chromium-review.googlesource.com/529973
Commit-Queue: Josh Wolfe <jwolfe@igalia.com>
Reviewed-by: 's avatarDaniel Ehrenberg <littledan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46369}
parent c746dcf9
......@@ -1203,6 +1203,7 @@ v8_source_set("v8_base") {
"src/builtins/builtins-internal.cc",
"src/builtins/builtins-interpreter.cc",
"src/builtins/builtins-intl.cc",
"src/builtins/builtins-intl.h",
"src/builtins/builtins-json.cc",
"src/builtins/builtins-math.cc",
"src/builtins/builtins-number.cc",
......@@ -2362,6 +2363,7 @@ v8_source_set("v8_base") {
} else {
sources -= [
"src/builtins/builtins-intl.cc",
"src/builtins/builtins-intl.h",
"src/char-predicates.cc",
"src/intl.cc",
"src/intl.h",
......
......@@ -53,12 +53,12 @@ bool IsStdlibMemberValid(Isolate* isolate, Handle<JSReceiver> stdlib,
bool* is_typed_array) {
switch (member) {
case wasm::AsmJsParser::StandardMember::kInfinity: {
Handle<Name> name = isolate->factory()->infinity_string();
Handle<Name> name = isolate->factory()->Infinity_string();
Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
return value->IsNumber() && std::isinf(value->Number());
}
case wasm::AsmJsParser::StandardMember::kNaN: {
Handle<Name> name = isolate->factory()->nan_string();
Handle<Name> name = isolate->factory()->NaN_string();
Handle<Object> value = JSReceiver::GetDataProperty(stdlib, name);
return value->IsNaN();
}
......
......@@ -4161,6 +4161,22 @@ void Genesis::InitializeGlobal_harmony_regexp_dotall() {
native_context()->set_regexp_prototype_map(*prototype_map);
}
#ifdef V8_INTL_SUPPORT
void Genesis::InitializeGlobal_harmony_number_format_to_parts() {
if (!FLAG_harmony_number_format_to_parts) return;
Handle<JSObject> number_format_prototype(JSObject::cast(
native_context()->intl_number_format_function()->prototype()));
Handle<String> name = factory()->InternalizeUtf8String("formatToParts");
InstallFunction(number_format_prototype,
SimpleCreateFunction(
isolate(), name,
Builtins::kNumberFormatPrototypeFormatToParts, 0, false),
name);
}
#endif // V8_INTL_SUPPORT
Handle<JSFunction> Genesis::CreateArrayBuffer(Handle<String> name,
Builtins::Name call_byteLength,
BuiltinFunctionId byteLength_id,
......
......@@ -1063,7 +1063,9 @@ namespace internal {
/* ES #sec-string.prototype.touppercase */ \
CPP(StringPrototypeToUpperCaseIntl) \
/* ES #sec-string.prototype.normalize */ \
CPP(StringPrototypeNormalizeIntl)
CPP(StringPrototypeNormalizeIntl) \
/* ecma402 #sec-intl.numberformat.prototype.formattoparts */ \
CPP(NumberFormatPrototypeFormatToParts)
#else
#define BUILTIN_LIST(CPP, API, TFJ, TFC, TFS, TFH, ASM, DBG) \
BUILTIN_LIST_BASE(CPP, API, TFJ, TFC, TFS, TFH, ASM, DBG) \
......
This diff is collapsed.
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_BUILTINS_BUILTINS_INTL_H_
#define V8_BUILTINS_BUILTINS_INTL_H_
#include <stdint.h>
#include <vector>
namespace v8 {
namespace internal {
struct NumberFormatSpan {
int32_t field_id;
int32_t begin_pos;
int32_t end_pos;
NumberFormatSpan() {}
NumberFormatSpan(int32_t field_id, int32_t begin_pos, int32_t end_pos)
: field_id(field_id), begin_pos(begin_pos), end_pos(end_pos) {}
};
std::vector<NumberFormatSpan> FlattenRegionsToParts(
std::vector<NumberFormatSpan>* regions);
} // namespace internal
} // namespace v8
#endif // V8_BUILTINS_BUILTINS_H_
......@@ -39,10 +39,10 @@ BUILTIN(NumberPrototypeToExponential) {
isolate, fraction_digits, Object::ToInteger(isolate, fraction_digits));
double const fraction_digits_number = fraction_digits->Number();
if (std::isnan(value_number)) return isolate->heap()->nan_string();
if (std::isnan(value_number)) return isolate->heap()->NaN_string();
if (std::isinf(value_number)) {
return (value_number < 0.0) ? isolate->heap()->minus_infinity_string()
: isolate->heap()->infinity_string();
return (value_number < 0.0) ? isolate->heap()->minus_Infinity_string()
: isolate->heap()->Infinity_string();
}
if (fraction_digits_number < 0.0 || fraction_digits_number > 20.0) {
THROW_NEW_ERROR_RETURN_FAILURE(
......@@ -91,10 +91,10 @@ BUILTIN(NumberPrototypeToFixed) {
"toFixed() digits")));
}
if (std::isnan(value_number)) return isolate->heap()->nan_string();
if (std::isnan(value_number)) return isolate->heap()->NaN_string();
if (std::isinf(value_number)) {
return (value_number < 0.0) ? isolate->heap()->minus_infinity_string()
: isolate->heap()->infinity_string();
return (value_number < 0.0) ? isolate->heap()->minus_Infinity_string()
: isolate->heap()->Infinity_string();
}
char* const str = DoubleToFixedCString(
value_number, static_cast<int>(fraction_digits_number));
......@@ -153,10 +153,10 @@ BUILTIN(NumberPrototypeToPrecision) {
Object::ToInteger(isolate, precision));
double const precision_number = precision->Number();
if (std::isnan(value_number)) return isolate->heap()->nan_string();
if (std::isnan(value_number)) return isolate->heap()->NaN_string();
if (std::isinf(value_number)) {
return (value_number < 0.0) ? isolate->heap()->minus_infinity_string()
: isolate->heap()->infinity_string();
return (value_number < 0.0) ? isolate->heap()->minus_Infinity_string()
: isolate->heap()->Infinity_string();
}
if (precision_number < 1.0 || precision_number > 21.0) {
THROW_NEW_ERROR_RETURN_FAILURE(
......@@ -217,10 +217,10 @@ BUILTIN(NumberPrototypeToString) {
}
// Slow case.
if (std::isnan(value_number)) return isolate->heap()->nan_string();
if (std::isnan(value_number)) return isolate->heap()->NaN_string();
if (std::isinf(value_number)) {
return (value_number < 0.0) ? isolate->heap()->minus_infinity_string()
: isolate->heap()->infinity_string();
return (value_number < 0.0) ? isolate->heap()->minus_Infinity_string()
: isolate->heap()->Infinity_string();
}
char* const str =
DoubleToRadixCString(value_number, static_cast<int>(radix_number));
......
......@@ -2795,8 +2795,8 @@ Handle<RegExpMatchInfo> Factory::NewRegExpMatchInfo() {
Handle<Object> Factory::GlobalConstantFor(Handle<Name> name) {
if (Name::Equals(name, undefined_string())) return undefined_value();
if (Name::Equals(name, nan_string())) return nan_value();
if (Name::Equals(name, infinity_string())) return infinity_value();
if (Name::Equals(name, NaN_string())) return nan_value();
if (Name::Equals(name, Infinity_string())) return infinity_value();
return Handle<Object>::null();
}
......
......@@ -189,7 +189,7 @@ DEFINE_BOOL(harmony_shipping, true, "enable all shipped harmony features")
DEFINE_IMPLICATION(es_staging, harmony)
// Features that are still work in progress (behind individual flags).
#define HARMONY_INPROGRESS(V) \
#define HARMONY_INPROGRESS_BASE(V) \
V(harmony_array_prototype_values, "harmony Array.prototype.values") \
V(harmony_function_sent, "harmony function.sent") \
V(harmony_tailcalls, "harmony tail calls") \
......@@ -200,6 +200,16 @@ DEFINE_IMPLICATION(es_staging, harmony)
V(harmony_dynamic_import, "harmony dynamic import") \
V(harmony_promise_finally, "harmony Promise.prototype.finally")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \
HARMONY_INPROGRESS_BASE(V) \
V(harmony_number_format_to_parts, \
"Intl.NumberFormat.prototype." \
"formatToParts")
#else
#define HARMONY_INPROGRESS(V) HARMONY_INPROGRESS_BASE(V)
#endif
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \
V(harmony_function_tostring, "harmony Function.prototype.toString") \
......
......@@ -46,9 +46,11 @@
V(constructor_string, "constructor") \
V(construct_string, "construct") \
V(create_string, "create") \
V(currency_string, "currency") \
V(Date_string, "Date") \
V(dayperiod_string, "dayperiod") \
V(day_string, "day") \
V(decimal_string, "decimal") \
V(default_string, "default") \
V(defineProperty_string, "defineProperty") \
V(deleteProperty_string, "deleteProperty") \
......@@ -73,6 +75,7 @@
V(EvalError_string, "EvalError") \
V(false_string, "false") \
V(flags_string, "flags") \
V(fraction_string, "fraction") \
V(function_string, "function") \
V(Function_string, "Function") \
V(Generator_string, "Generator") \
......@@ -82,6 +85,7 @@
V(get_string, "get") \
V(get_space_string, "get ") \
V(global_string, "global") \
V(group_string, "group") \
V(groups_string, "groups") \
V(has_string, "has") \
V(hour_string, "hour") \
......@@ -89,7 +93,9 @@
V(illegal_access_string, "illegal access") \
V(illegal_argument_string, "illegal argument") \
V(index_string, "index") \
V(infinity_string, "Infinity") \
V(infinity_string, "infinity") \
V(Infinity_string, "Infinity") \
V(integer_string, "integer") \
V(input_string, "input") \
V(isExtensible_string, "isExtensible") \
V(isView_string, "isView") \
......@@ -103,15 +109,17 @@
V(literal_string, "literal") \
V(Map_string, "Map") \
V(message_string, "message") \
V(minus_infinity_string, "-Infinity") \
V(minus_Infinity_string, "-Infinity") \
V(minus_zero_string, "-0") \
V(minusSign_string, "minusSign") \
V(minute_string, "minute") \
V(Module_string, "Module") \
V(month_string, "month") \
V(multiline_string, "multiline") \
V(name_string, "name") \
V(native_string, "native") \
V(nan_string, "NaN") \
V(nan_string, "nan") \
V(NaN_string, "NaN") \
V(new_target_string, ".new.target") \
V(next_string, "next") \
V(NFC_string, "NFC") \
......@@ -128,6 +136,8 @@
V(ok, "ok") \
V(one_string, "1") \
V(ownKeys_string, "ownKeys") \
V(percentSign_string, "percentSign") \
V(plusSign_string, "plusSign") \
V(position_string, "position") \
V(preventExtensions_string, "preventExtensions") \
V(Promise_string, "Promise") \
......
......@@ -1284,7 +1284,7 @@ static Handle<Object> TryConvertKey(Handle<Object> key, Isolate* isolate) {
if (key->IsHeapNumber()) {
double value = Handle<HeapNumber>::cast(key)->value();
if (std::isnan(value)) {
key = isolate->factory()->nan_string();
key = isolate->factory()->NaN_string();
} else {
int int_value = FastD2I(value);
if (value == int_value && Smi::IsValid(int_value)) {
......
......@@ -651,6 +651,7 @@
'builtins/builtins-sharedarraybuffer.cc',
'builtins/builtins-string.cc',
'builtins/builtins-intl.cc',
'builtins/builtins-intl.h',
'builtins/builtins-symbol.cc',
'builtins/builtins-typedarray.cc',
'builtins/builtins-utils.h',
......@@ -1842,6 +1843,7 @@
}, { # v8_enable_i18n_support==0
'sources!': [
'builtins/builtins-intl.cc',
'builtins/builtins-intl.h',
'char-predicates.cc',
'intl.cc',
'intl.h',
......
......@@ -148,6 +148,7 @@ v8_executable("cctest") {
"test-heap-profiler.cc",
"test-identity-map.cc",
"test-inobject-slack-tracking.cc",
"test-intl.cc",
"test-list.cc",
"test-liveedit.cc",
"test-lockers.cc",
......@@ -351,6 +352,12 @@ v8_executable("cctest") {
deps += [ "../..:v8" ]
}
if (v8_enable_i18n_support) {
defines += [ "V8_INTL_SUPPORT" ]
} else {
sources -= [ "test-intl.cc" ]
}
cflags = []
ldflags = []
......
......@@ -165,6 +165,7 @@
'test-hashmap.cc',
'test-heap-profiler.cc',
'test-identity-map.cc',
'test-intl.cc',
'test-inobject-slack-tracking.cc',
'test-list.cc',
'test-liveedit.cc',
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef V8_INTL_SUPPORT
#include "src/builtins/builtins-intl.h"
#include "test/cctest/cctest.h"
namespace v8 {
namespace internal {
// This operator overloading enables CHECK_EQ to be used with
// std::vector<NumberFormatSpan>
bool operator==(const NumberFormatSpan& lhs, const NumberFormatSpan& rhs) {
return memcmp(&lhs, &rhs, sizeof(lhs)) == 0;
}
template <typename _CharT, typename _Traits, typename T>
std::basic_ostream<_CharT, _Traits>& operator<<(
std::basic_ostream<_CharT, _Traits>& self, const std::vector<T>& v) {
self << "[";
for (auto it = v.begin(); it < v.end();) {
self << *it;
it++;
if (it < v.end()) self << ", ";
}
return self << "]";
}
template <typename _CharT, typename _Traits>
std::basic_ostream<_CharT, _Traits>& operator<<(
std::basic_ostream<_CharT, _Traits>& self, const NumberFormatSpan& part) {
return self << "{" << part.field_id << "," << part.begin_pos << ","
<< part.end_pos << "}";
}
void test_flatten_regions_to_parts(
const std::vector<NumberFormatSpan>& regions,
const std::vector<NumberFormatSpan>& expected_parts) {
std::vector<NumberFormatSpan> mutable_regions = regions;
std::vector<NumberFormatSpan> parts = FlattenRegionsToParts(&mutable_regions);
CHECK_EQ(expected_parts, parts);
}
TEST(FlattenRegionsToParts) {
test_flatten_regions_to_parts(
std::vector<NumberFormatSpan>{
NumberFormatSpan(-1, 0, 10), NumberFormatSpan(1, 2, 8),
NumberFormatSpan(2, 2, 4), NumberFormatSpan(3, 6, 8),
},
std::vector<NumberFormatSpan>{
NumberFormatSpan(-1, 0, 2), NumberFormatSpan(2, 2, 4),
NumberFormatSpan(1, 4, 6), NumberFormatSpan(3, 6, 8),
NumberFormatSpan(-1, 8, 10),
});
test_flatten_regions_to_parts(
std::vector<NumberFormatSpan>{
NumberFormatSpan(0, 0, 1),
},
std::vector<NumberFormatSpan>{
NumberFormatSpan(0, 0, 1),
});
test_flatten_regions_to_parts(
std::vector<NumberFormatSpan>{
NumberFormatSpan(-1, 0, 1), NumberFormatSpan(0, 0, 1),
},
std::vector<NumberFormatSpan>{
NumberFormatSpan(0, 0, 1),
});
test_flatten_regions_to_parts(
std::vector<NumberFormatSpan>{
NumberFormatSpan(0, 0, 1), NumberFormatSpan(-1, 0, 1),
},
std::vector<NumberFormatSpan>{
NumberFormatSpan(0, 0, 1),
});
test_flatten_regions_to_parts(
std::vector<NumberFormatSpan>{
NumberFormatSpan(-1, 0, 10), NumberFormatSpan(1, 0, 1),
NumberFormatSpan(2, 0, 2), NumberFormatSpan(3, 0, 3),
NumberFormatSpan(4, 0, 4), NumberFormatSpan(5, 0, 5),
NumberFormatSpan(15, 5, 10), NumberFormatSpan(16, 6, 10),
NumberFormatSpan(17, 7, 10), NumberFormatSpan(18, 8, 10),
NumberFormatSpan(19, 9, 10),
},
std::vector<NumberFormatSpan>{
NumberFormatSpan(1, 0, 1), NumberFormatSpan(2, 1, 2),
NumberFormatSpan(3, 2, 3), NumberFormatSpan(4, 3, 4),
NumberFormatSpan(5, 4, 5), NumberFormatSpan(15, 5, 6),
NumberFormatSpan(16, 6, 7), NumberFormatSpan(17, 7, 8),
NumberFormatSpan(18, 8, 9), NumberFormatSpan(19, 9, 10),
});
// : 4
// : 22 33 3
// : 11111 22
// input regions: 0000000 111
// : ------------
// output parts: 0221340--231
test_flatten_regions_to_parts(
std::vector<NumberFormatSpan>{
NumberFormatSpan(-1, 0, 12), NumberFormatSpan(0, 0, 7),
NumberFormatSpan(1, 9, 12), NumberFormatSpan(1, 1, 6),
NumberFormatSpan(2, 9, 11), NumberFormatSpan(2, 1, 3),
NumberFormatSpan(3, 10, 11), NumberFormatSpan(3, 4, 6),
NumberFormatSpan(4, 5, 6),
},
std::vector<NumberFormatSpan>{
NumberFormatSpan(0, 0, 1), NumberFormatSpan(2, 1, 3),
NumberFormatSpan(1, 3, 4), NumberFormatSpan(3, 4, 5),
NumberFormatSpan(4, 5, 6), NumberFormatSpan(0, 6, 7),
NumberFormatSpan(-1, 7, 9), NumberFormatSpan(2, 9, 10),
NumberFormatSpan(3, 10, 11), NumberFormatSpan(1, 11, 12),
});
}
} // namespace internal
} // namespace v8
#endif // V8_INTL_SUPPORT
This diff is collapsed.
......@@ -316,7 +316,7 @@
'language/expressions/async-arrow-function/dflt-params-duplicates': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=5244
'intl402/NumberFormat/prototype/formatToParts/*': [SKIP],
'intl402/NumberFormat/prototype/formatToParts/*': ['--harmony-number-format-to-parts'],
# https://bugs.chromium.org/p/v8/issues/detail?id=5327
'built-ins/TypedArray/prototype/set/array-arg-negative-integer-offset-throws': [FAIL],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment