Commit 256b4a82 authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[intl] Port NumberFormat.prototype.format and bound format function to C++

This increases the size of a NumberFormat instance by a word to store
the bound format function.

The instance to be bound is stored on the context of this builtin function.

Bug: v8:5751, v8:7800
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Ie85d8db7d10aabb5c40e77687e6f7112a84f3ebd
Reviewed-on: https://chromium-review.googlesource.com/1122153Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54353}
parent cf080aeb
......@@ -2902,6 +2902,17 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
SimpleInstallFunction(isolate_, prototype, "formatToParts",
Builtins::kNumberFormatPrototypeFormatToParts, 1,
false);
SimpleInstallGetter(isolate_, prototype,
factory->InternalizeUtf8String("format"),
Builtins::kNumberFormatPrototypeFormatNumber, false);
{
Handle<SharedFunctionInfo> info = SimpleCreateBuiltinSharedFunctionInfo(
isolate_, Builtins::kNumberFormatInternalFormatNumber,
factory->empty_string(), 1);
native_context()->set_number_format_internal_format_number_shared_fun(
*info);
}
}
{
......
......@@ -1338,7 +1338,11 @@ namespace internal {
CPP(LocalePrototypeHourCycle) \
CPP(LocalePrototypeNumeric) \
CPP(LocalePrototypeNumberingSystem) \
CPP(LocalePrototypeToString)
CPP(LocalePrototypeToString) \
/* ecma402 #sec-number-format-functions */ \
CPP(NumberFormatInternalFormatNumber) \
/* ecma402 #sec-intl.numberformat.prototype.format */ \
CPP(NumberFormatPrototypeFormatNumber)
#else
#define BUILTIN_LIST(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
BUILTIN_LIST_BASE(CPP, API, TFJ, TFC, TFS, TFH, ASM) \
......
......@@ -502,6 +502,96 @@ BUILTIN(DateTimeFormatPrototypeFormatToParts) {
return FormatDateToParts(isolate, date_format, date_value);
}
BUILTIN(NumberFormatPrototypeFormatNumber) {
const char* const method = "get Intl.NumberFormat.prototype.format";
HandleScope scope(isolate);
// 1. Let nf be the this value.
// 2. If Type(nf) is not Object, throw a TypeError exception.
CHECK_RECEIVER(JSReceiver, receiver, method);
// 3. Let nf be ? UnwrapNumberFormat(nf).
Handle<JSObject> number_format_holder;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, number_format_holder,
NumberFormat::Unwrap(isolate, receiver, method));
DCHECK(Intl::IsObjectOfType(isolate, number_format_holder,
Intl::Type::kNumberFormat));
Handle<Object> bound_format = Handle<Object>(
number_format_holder->GetEmbedderField(NumberFormat::kBoundFormatIndex),
isolate);
// 4. If nf.[[BoundFormat]] is undefined, then
if (!bound_format->IsUndefined(isolate)) {
DCHECK(bound_format->IsJSFunction());
// 5. Return nf.[[BoundFormat]].
return *bound_format;
}
Handle<Context> native_context =
Handle<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);
// 4. c. Set nf.[[BoundFormat]] to F.
number_format_holder->SetEmbedderField(NumberFormat::kBoundFormatIndex,
*new_bound_format_function);
// 5. Return nf.[[BoundFormat]].
return *new_bound_format_function;
}
BUILTIN(NumberFormatInternalFormatNumber) {
HandleScope scope(isolate);
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));
// 3. If value is not provided, let value be undefined.
Handle<Object> value = args.atOrUndefined(isolate, 1);
// 4. Let x be ? ToNumber(value).
Handle<Object> number_obj;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_obj,
Object::ToNumber(value));
// Spec treats -0 as 0.
if (number_obj->IsMinusZero()) {
number_obj = Handle<Smi>(Smi::kZero, isolate);
}
double number = number_obj->Number();
// Return FormatNumber(nf, x).
RETURN_RESULT_OR_FAILURE(isolate, NumberFormat::FormatNumber(
isolate, number_format_holder, number));
}
// Intl.Locale implementation
BUILTIN(LocaleConstructor) {
HandleScope scope(isolate);
......
......@@ -201,6 +201,8 @@ enum ContextLookupFlags {
intl_date_time_format_function) \
V(INTL_NUMBER_FORMAT_FUNCTION_INDEX, JSFunction, \
intl_number_format_function) \
V(INTL_NUMBER_FORMAT_INTERNAL_FORMAT_NUMBER_SHARED_FUN, SharedFunctionInfo, \
number_format_internal_format_number_shared_fun) \
V(INTL_LOCALE_FUNCTION_INDEX, JSFunction, intl_locale_function) \
V(INTL_COLLATOR_FUNCTION_INDEX, JSFunction, intl_collator_function) \
V(INTL_PLURAL_RULES_FUNCTION_INDEX, JSFunction, intl_plural_rules_function) \
......
......@@ -1482,6 +1482,17 @@ Handle<Context> Factory::NewBlockContext(Handle<Context> previous,
return context;
}
Handle<Context> Factory::NewBuiltinContext(Handle<Context> native_context,
int length) {
DCHECK_GE(length, Context::MIN_CONTEXT_SLOTS);
Handle<Context> context =
NewFixedArrayWithMap<Context>(Heap::kFunctionContextMapRootIndex, length);
context->set_scope_info(ReadOnlyRoots(isolate()).empty_scope_info());
context->set_extension(*the_hole_value());
context->set_native_context(*native_context);
return context;
}
Handle<Struct> Factory::NewStruct(InstanceType type, PretenureFlag pretenure) {
Map* map;
switch (type) {
......
......@@ -392,6 +392,13 @@ class V8_EXPORT_PRIVATE Factory {
Handle<Context> NewBlockContext(Handle<Context> previous,
Handle<ScopeInfo> scope_info);
// Create a context that's used by builtin functions.
//
// These are similar to function context but don't have a previous
// context or any scope info. These are used to store spec defined
// context values.
Handle<Context> NewBuiltinContext(Handle<Context> native_context, int length);
Handle<Struct> NewStruct(InstanceType type,
PretenureFlag pretenure = NOT_TENURED);
......
......@@ -1468,10 +1468,6 @@ function formatNumber(formatter, value) {
return %InternalNumberFormat(formatter, number);
}
AddBoundMethod(GlobalIntlNumberFormat, 'format', formatNumber, 1,
NUMBER_FORMAT_TYPE, true);
/**
* Returns a string that matches LDML representation of the options object.
*/
......
......@@ -909,7 +909,8 @@ icu::DecimalFormat* NumberFormat::InitializeNumberFormat(
icu::DecimalFormat* NumberFormat::UnpackNumberFormat(Isolate* isolate,
Handle<JSObject> obj) {
return reinterpret_cast<icu::DecimalFormat*>(obj->GetEmbedderField(0));
return reinterpret_cast<icu::DecimalFormat*>(
obj->GetEmbedderField(NumberFormat::kDecimalFormatIndex));
}
void NumberFormat::DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data) {
......@@ -1209,33 +1210,59 @@ MaybeHandle<Object> LegacyUnwrapReceiver(Isolate* isolate,
} // namespace
MaybeHandle<JSReceiver> Intl::UnwrapReceiver(Isolate* isolate,
Handle<JSReceiver> receiver,
Handle<JSFunction> constructor,
Intl::Type type,
Handle<String> method_name,
bool check_legacy_constructor) {
MaybeHandle<JSObject> Intl::UnwrapReceiver(Isolate* isolate,
Handle<JSReceiver> receiver,
Handle<JSFunction> constructor,
Intl::Type type,
Handle<String> method_name,
bool check_legacy_constructor) {
Handle<Object> new_receiver = receiver;
if (check_legacy_constructor) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, new_receiver,
LegacyUnwrapReceiver(isolate, receiver, constructor, type), JSReceiver);
LegacyUnwrapReceiver(isolate, receiver, constructor, type), JSObject);
}
// 3. If Type(new_receiver) is not Object or nf does not have an
// [[Initialized...]] internal slot, then
if (!new_receiver->IsJSReceiver() ||
!Intl::IsObjectOfType(isolate, new_receiver, type)) {
if (!Intl::IsObjectOfType(isolate, new_receiver, type)) {
// 3. a. Throw a TypeError exception.
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
method_name, receiver),
JSReceiver);
JSObject);
}
// The above IsObjectOfType returns true only for JSObjects, which
// makes this cast safe.
return Handle<JSReceiver>::cast(new_receiver);
return Handle<JSObject>::cast(new_receiver);
}
MaybeHandle<JSObject> NumberFormat::Unwrap(Isolate* isolate,
Handle<JSReceiver> receiver,
const char* method_name) {
Handle<Context> native_context =
Handle<Context>(isolate->context()->native_context(), isolate);
Handle<JSFunction> constructor = Handle<JSFunction>(
JSFunction::cast(native_context->intl_number_format_function()), isolate);
Handle<String> method_name_str =
isolate->factory()->NewStringFromAsciiChecked(method_name);
return Intl::UnwrapReceiver(isolate, receiver, constructor,
Intl::Type::kNumberFormat, method_name_str, true);
}
MaybeHandle<Object> NumberFormat::FormatNumber(
Isolate* isolate, Handle<JSObject> number_format_holder, double value) {
icu::DecimalFormat* number_format =
NumberFormat::UnpackNumberFormat(isolate, number_format_holder);
CHECK_NOT_NULL(number_format);
icu::UnicodeString result;
number_format->format(value, result);
return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
}
// TODO(bstell): enable this anonymous namespace once these routines are called:
......
......@@ -12,6 +12,7 @@
#include <set>
#include <string>
#include "src/contexts.h"
#include "src/intl.h"
#include "src/objects.h"
#include "unicode/uversion.h"
......@@ -71,9 +72,47 @@ class NumberFormat {
// holds the pointer gets garbage collected.
static void DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data);
// The UnwrapNumberFormat abstract operation gets the underlying
// NumberFormat operation for various methods which implement
// ECMA-402 v1 semantics for supporting initializing existing Intl
// objects.
//
// ecma402/#sec-unwrapnumberformat
static MaybeHandle<JSObject> Unwrap(Isolate* isolate,
Handle<JSReceiver> receiver,
const char* method_name);
// ecm402/#sec-formatnumber
static MaybeHandle<Object> FormatNumber(Isolate* isolate,
Handle<JSObject> number_format_holder,
double value);
// Layout description.
static const int kDecimalFormat = JSObject::kHeaderSize;
static const int kSize = kDecimalFormat + kPointerSize;
#define NUMBER_FORMAT_FIELDS(V) \
/* Pointer fields. */ \
V(kDecimalFormat, kPointerSize) \
V(kBoundFormat, kPointerSize) \
V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, NUMBER_FORMAT_FIELDS)
#undef NUMBER_FORMAT_FIELDS
// ContextSlot defines the context structure for the bound
// NumberFormat.prototype.format function.
enum ContextSlot {
// The number format instance that the function holding this
// context is bound to.
kNumberFormat = Context::MIN_CONTEXT_SLOTS,
kLength
};
// TODO(gsathya): Remove this and use regular accessors once
// NumberFormat is a sub class of JSObject.
//
// This needs to be consistent with the above LayoutDescription.
static const int kDecimalFormatIndex = 0;
static const int kBoundFormatIndex = 1;
private:
NumberFormat();
......@@ -201,7 +240,7 @@ class Intl {
// Returns the underlying Intl receiver for various methods which
// implement ECMA-402 v1 semantics for supporting initializing
// existing Intl objects.
V8_WARN_UNUSED_RESULT static MaybeHandle<JSReceiver> UnwrapReceiver(
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> UnwrapReceiver(
Isolate* isolate, Handle<JSReceiver> receiver,
Handle<JSFunction> constructor, Intl::Type type,
Handle<String> method_name /* TODO(gsathya): Make this char const* */,
......
......@@ -288,7 +288,8 @@ RUNTIME_FUNCTION(Runtime_CreateNumberFormat) {
if (!number_format) return isolate->ThrowIllegalOperation();
local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(number_format));
local_object->SetEmbedderField(NumberFormat::kDecimalFormatIndex,
reinterpret_cast<Smi*>(number_format));
Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
......@@ -303,22 +304,15 @@ RUNTIME_FUNCTION(Runtime_InternalNumberFormat) {
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, number, 1);
Handle<Object> value;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(number));
icu::DecimalFormat* number_format =
NumberFormat::UnpackNumberFormat(isolate, number_format_holder);
CHECK_NOT_NULL(number_format);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
icu::UnicodeString result;
number_format->format(value->Number(), result);
Handle<Object> number_obj;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_obj,
Object::ToNumber(value));
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(result.getBuffer()),
result.length())));
double number = number_obj->Number();
RETURN_RESULT_OR_FAILURE(isolate, NumberFormat::FormatNumber(
isolate, number_format_holder, number));
}
RUNTIME_FUNCTION(Runtime_CurrencyDigits) {
......
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