Commit 3c164506 authored by bmeurer's avatar bmeurer Committed by Commit bot

[es6] Implement Date.prototype[@@toPrimitive] as C++ builtin.

This way we don't need to expose JSReceiver::OrdinaryToPrimitive
as runtime function, and we don't need the separate JS trampoline.

This also adds tests for ToPrimitive on date objects, which are
special.

R=mstarzinger@chromium.org
BUG=v8:4307
LOG=n

Review URL: https://codereview.chromium.org/1324713002

Cr-Commit-Position: refs/heads/master@{#30473}
parent 89fd5b09
......@@ -380,26 +380,43 @@ void Bootstrapper::DetachGlobal(Handle<Context> env) {
}
static Handle<JSFunction> InstallFunction(Handle<JSObject> target,
const char* name, InstanceType type,
int instance_size,
MaybeHandle<JSObject> maybe_prototype,
Builtins::Name call,
bool strict_function_map = false) {
namespace {
Handle<JSFunction> InstallFunction(Handle<JSObject> target, Handle<Name> name,
InstanceType type, int instance_size,
MaybeHandle<JSObject> maybe_prototype,
Builtins::Name call,
PropertyAttributes attributes,
bool strict_function_map = false) {
Isolate* isolate = target->GetIsolate();
Factory* factory = isolate->factory();
Handle<String> internalized_name = factory->InternalizeUtf8String(name);
Handle<String> name_string = Name::ToFunctionName(name).ToHandleChecked();
Handle<Code> call_code = Handle<Code>(isolate->builtins()->builtin(call));
Handle<JSObject> prototype;
static const bool kReadOnlyPrototype = false;
static const bool kInstallConstructor = false;
Handle<JSFunction> function =
maybe_prototype.ToHandle(&prototype)
? factory->NewFunction(internalized_name, call_code, prototype, type,
? factory->NewFunction(name_string, call_code, prototype, type,
instance_size, kReadOnlyPrototype,
kInstallConstructor, strict_function_map)
: factory->NewFunctionWithoutPrototype(internalized_name, call_code,
: factory->NewFunctionWithoutPrototype(name_string, call_code,
strict_function_map);
JSObject::AddProperty(target, name, function, attributes);
if (target->IsJSGlobalObject()) {
function->shared()->set_instance_class_name(*name_string);
}
function->shared()->set_native(true);
return function;
}
Handle<JSFunction> InstallFunction(Handle<JSObject> target, const char* name,
InstanceType type, int instance_size,
MaybeHandle<JSObject> maybe_prototype,
Builtins::Name call,
bool strict_function_map = false) {
Factory* const factory = target->GetIsolate()->factory();
PropertyAttributes attributes;
if (target->IsJSBuiltinsObject()) {
attributes =
......@@ -407,14 +424,13 @@ static Handle<JSFunction> InstallFunction(Handle<JSObject> target,
} else {
attributes = DONT_ENUM;
}
JSObject::AddProperty(target, internalized_name, function, attributes);
if (target->IsJSGlobalObject()) {
function->shared()->set_instance_class_name(*internalized_name);
}
function->shared()->set_native(true);
return function;
return InstallFunction(target, factory->InternalizeUtf8String(name), type,
instance_size, maybe_prototype, call, attributes,
strict_function_map);
}
} // namespace
void Genesis::SetFunctionInstanceDescriptor(Handle<Map> map,
FunctionMode function_mode) {
......@@ -2291,6 +2307,29 @@ bool Genesis::InstallNatives(ContextType context_type) {
native_context()->set_string_function_prototype_map(
HeapObject::cast(string_function->initial_map()->prototype())->map());
// Install Date.prototype[@@toPrimitive].
{
Handle<String> key = factory()->Date_string();
Handle<JSFunction> date = Handle<JSFunction>::cast(
Object::GetProperty(handle(native_context()->global_object()), key)
.ToHandleChecked());
Handle<JSObject> proto =
Handle<JSObject>(JSObject::cast(date->instance_prototype()));
// Install the @@toPrimitive function.
Handle<JSFunction> to_primitive =
InstallFunction(proto, factory()->to_primitive_symbol(), JS_OBJECT_TYPE,
JSObject::kHeaderSize, MaybeHandle<JSObject>(),
Builtins::kDateToPrimitive,
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
// Set the expected parameters for @@toPrimitive to 1; required by builtin.
to_primitive->shared()->set_internal_formal_parameter_count(1);
// Set the length for the function to satisfy ECMA-262.
to_primitive->shared()->set_length(1);
}
// Install Function.prototype.call and apply.
{
Handle<String> key = factory()->Function_string();
......
......@@ -739,6 +739,30 @@ BUILTIN(ArrayConcat) {
}
// -----------------------------------------------------------------------------
//
// 20.3.4.45 Date.prototype [ @@toPrimitive ] ( hint )
BUILTIN(DateToPrimitive) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
if (!args.receiver()->IsJSReceiver()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->NewStringFromAsciiChecked(
"Date.prototype [ @@toPrimitive ]"),
args.receiver()));
}
Handle<JSReceiver> receiver = args.at<JSReceiver>(0);
Handle<Object> hint = args.at<Object>(1);
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
JSDate::ToPrimitive(receiver, hint));
return *result;
}
// -----------------------------------------------------------------------------
// Throwers for restricted function properties and strict arguments object
// properties
......
......@@ -57,6 +57,8 @@ enum BuiltinExtraArguments {
V(ArraySplice, NO_EXTRA_ARGUMENTS) \
V(ArrayConcat, NO_EXTRA_ARGUMENTS) \
\
V(DateToPrimitive, NO_EXTRA_ARGUMENTS) \
\
V(HandleApiCall, NEEDS_CALLED_FUNCTION) \
V(HandleApiCallConstruct, NEEDS_CALLED_FUNCTION) \
V(HandleApiCallAsFunction, NO_EXTRA_ARGUMENTS) \
......
......@@ -248,7 +248,6 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
case Runtime::kInlineToPrimitive:
case Runtime::kInlineToPrimitive_Number:
case Runtime::kInlineToPrimitive_String:
case Runtime::kInlineOrdinaryToPrimitive:
case Runtime::kInlineToNumber:
case Runtime::kInlineToString:
case Runtime::kInlineToName:
......
......@@ -365,21 +365,6 @@ function DateToLocaleTimeString() {
}
// 20.3.4.45 Date.prototype [ @@toPrimitive ] ( hint )
function DateToPrimitive(hint) {
if (!IS_SPEC_OBJECT(this)) {
throw MakeTypeError(kIncompatibleMethodReceiver,
"Date.prototype [ @@toPrimitive ]", this);
}
if (hint === "default") {
hint = "string";
} else if (hint !== "number" && hint !== "string") {
throw MakeTypeError(kInvalidHint, hint);
}
return %OrdinaryToPrimitive(this, hint);
}
// ECMA 262 - 15.9.5.8
function DateValueOf() {
CHECK_DATE(this);
......@@ -848,9 +833,6 @@ utils.InstallFunctions(GlobalDate, DONT_ENUM, [
// Set up non-enumerable constructor property of the Date prototype object.
%AddNamedProperty(GlobalDate.prototype, "constructor", GlobalDate, DONT_ENUM);
utils.SetFunctionName(DateToPrimitive, toPrimitiveSymbol);
%AddNamedProperty(GlobalDate.prototype, toPrimitiveSymbol, DateToPrimitive,
DONT_ENUM | READ_ONLY);
// Set up non-enumerable functions of the Date prototype object and
// set their names.
......
......@@ -37,6 +37,7 @@
#include "src/objects-inl.h"
#include "src/prototype.h"
#include "src/safepoint-table.h"
#include "src/string-builder.h"
#include "src/string-search.h"
#include "src/string-stream.h"
#include "src/utils.h"
......@@ -6041,25 +6042,26 @@ MaybeHandle<Object> JSReceiver::ToPrimitive(Handle<JSReceiver> receiver,
NewTypeError(MessageTemplate::kCannotConvertToPrimitive),
Object);
}
return OrdinaryToPrimitive(receiver,
(hint == ToPrimitiveHint::kString)
? isolate->factory()->string_string()
: isolate->factory()->number_string());
return OrdinaryToPrimitive(receiver, (hint == ToPrimitiveHint::kString)
? OrdinaryToPrimitiveHint::kString
: OrdinaryToPrimitiveHint::kNumber);
}
// static
MaybeHandle<Object> JSReceiver::OrdinaryToPrimitive(Handle<JSReceiver> receiver,
Handle<String> hint) {
MaybeHandle<Object> JSReceiver::OrdinaryToPrimitive(
Handle<JSReceiver> receiver, OrdinaryToPrimitiveHint hint) {
Isolate* const isolate = receiver->GetIsolate();
Handle<String> method_names[2];
if (hint.is_identical_to(isolate->factory()->number_string())) {
method_names[0] = isolate->factory()->valueOf_string();
method_names[1] = isolate->factory()->toString_string();
} else {
DCHECK(hint.is_identical_to(isolate->factory()->string_string()));
method_names[0] = isolate->factory()->toString_string();
method_names[1] = isolate->factory()->valueOf_string();
switch (hint) {
case OrdinaryToPrimitiveHint::kNumber:
method_names[0] = isolate->factory()->valueOf_string();
method_names[1] = isolate->factory()->toString_string();
break;
case OrdinaryToPrimitiveHint::kString:
method_names[0] = isolate->factory()->toString_string();
method_names[1] = isolate->factory()->valueOf_string();
break;
}
for (Handle<String> name : method_names) {
Handle<Object> method;
......@@ -8350,6 +8352,21 @@ bool String::LooksValid() {
}
// static
MaybeHandle<String> Name::ToFunctionName(Handle<Name> name) {
if (name->IsString()) return Handle<String>::cast(name);
// ES6 section 9.2.11 SetFunctionName, step 4.
Isolate* const isolate = name->GetIsolate();
Handle<Object> description(Handle<Symbol>::cast(name)->name(), isolate);
if (description->IsUndefined()) return isolate->factory()->empty_string();
IncrementalStringBuilder builder(isolate);
builder.AppendCharacter('[');
builder.AppendString(Handle<String>::cast(description));
builder.AppendCharacter(']');
return builder.Finish();
}
namespace {
bool AreDigits(const uint8_t* s, int from, int to) {
......@@ -15866,6 +15883,27 @@ void JSDate::SetValue(Object* value, bool is_value_nan) {
}
// static
MaybeHandle<Object> JSDate::ToPrimitive(Handle<JSReceiver> receiver,
Handle<Object> hint) {
Isolate* const isolate = receiver->GetIsolate();
if (hint->IsString()) {
Handle<String> hint_string = Handle<String>::cast(hint);
if (hint_string->Equals(isolate->heap()->number_string())) {
return JSReceiver::OrdinaryToPrimitive(receiver,
OrdinaryToPrimitiveHint::kNumber);
}
if (hint_string->Equals(isolate->heap()->default_string()) ||
hint_string->Equals(isolate->heap()->string_string())) {
return JSReceiver::OrdinaryToPrimitive(receiver,
OrdinaryToPrimitiveHint::kString);
}
}
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kInvalidHint, hint),
Object);
}
void JSDate::SetCachedFields(int64_t local_time_ms, DateCache* date_cache) {
int days = DateCache::DaysFromTime(local_time_ms);
int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
......
......@@ -173,6 +173,11 @@ enum KeyedAccessStoreMode {
enum class ToPrimitiveHint { kDefault, kNumber, kString };
// Valid hints for the abstract operation OrdinaryToPrimitive,
// implemented according to ES6, section 7.1.1.
enum class OrdinaryToPrimitiveHint { kNumber, kString };
enum TypeofMode { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
......@@ -1679,7 +1684,7 @@ class JSReceiver: public HeapObject {
Handle<JSReceiver> receiver,
ToPrimitiveHint hint = ToPrimitiveHint::kDefault);
MUST_USE_RESULT static MaybeHandle<Object> OrdinaryToPrimitive(
Handle<JSReceiver> receiver, Handle<String> hint);
Handle<JSReceiver> receiver, OrdinaryToPrimitiveHint hint);
// Implementation of [[HasProperty]], ECMA-262 5th edition, section 8.12.6.
MUST_USE_RESULT static inline Maybe<bool> HasProperty(
......@@ -7265,6 +7270,9 @@ class JSDate: public JSObject {
void SetValue(Object* value, bool is_value_nan);
// ES6 section 20.3.4.45 Date.prototype [ @@toPrimitive ]
static MUST_USE_RESULT MaybeHandle<Object> ToPrimitive(
Handle<JSReceiver> receiver, Handle<Object> hint);
// Dispatched behavior.
DECLARE_PRINTER(JSDate)
......@@ -8115,6 +8123,10 @@ class Name: public HeapObject {
static inline Handle<Name> Flatten(Handle<Name> name,
PretenureFlag pretenure = NOT_TENURED);
// Return a string version of this name that is converted according to the
// rules described in ES6 section 9.2.11.
MUST_USE_RESULT static MaybeHandle<String> ToFunctionName(Handle<Name> name);
DECLARE_CAST(Name)
DECLARE_PRINTER(Name)
......
......@@ -1461,18 +1461,6 @@ RUNTIME_FUNCTION(Runtime_ToPrimitive_String) {
}
RUNTIME_FUNCTION(Runtime_OrdinaryToPrimitive) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
CONVERT_ARG_HANDLE_CHECKED(String, hint, 1);
Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, JSReceiver::OrdinaryToPrimitive(receiver, hint));
return *result;
}
RUNTIME_FUNCTION(Runtime_ToNumber) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
......
......@@ -495,7 +495,6 @@ namespace internal {
F(ToPrimitive, 1, 1) \
F(ToPrimitive_Number, 1, 1) \
F(ToPrimitive_String, 1, 1) \
F(OrdinaryToPrimitive, 2, 1) \
F(ToNumber, 1, 1) \
F(ToString, 1, 1) \
F(ToName, 1, 1) \
......
......@@ -48,3 +48,7 @@ var d = {
};
assertEquals("string", %ToName(d));
assertEquals("string", %_ToName(d));
var e = new Date(0);
assertEquals(e.toString(), %ToName(e));
assertEquals(e.toString(), %_ToName(e));
......@@ -55,3 +55,7 @@ var d = {
};
assertEquals(987654321, %ToNumber(d));
assertEquals(987654321, %_ToNumber(d));
var e = new Date(0);
assertEquals(0, %ToNumber(e));
assertEquals(0, %_ToNumber(e));
......@@ -67,10 +67,6 @@ assertEquals("xyz", %ToPrimitive_String(a));
assertEquals("xyz", %_ToPrimitive(a));
assertEquals("xyz", %_ToPrimitive_Number(a));
assertEquals("xyz", %_ToPrimitive_String(a));
assertEquals("xyz", %OrdinaryToPrimitive(a, "number"));
assertEquals("xyz", %OrdinaryToPrimitive(a, "string"));
assertEquals("xyz", %_OrdinaryToPrimitive(a, "number"));
assertEquals("xyz", %_OrdinaryToPrimitive(a, "string"));
var b = { valueOf: function() { return 42 }};
assertEquals(42, %ToPrimitive(b));
......@@ -79,10 +75,6 @@ assertEquals("[object Object]", %ToPrimitive_String(b));
assertEquals(42, %_ToPrimitive(b));
assertEquals(42, %_ToPrimitive_Number(b));
assertEquals("[object Object]", %_ToPrimitive_String(b));
assertEquals(42, %OrdinaryToPrimitive(b, "number"));
assertEquals("[object Object]", %OrdinaryToPrimitive(b, "string"));
assertEquals(42, %_OrdinaryToPrimitive(b, "number"));
assertEquals("[object Object]", %_OrdinaryToPrimitive(b, "string"));
var c = {
toString: function() { return "x"},
......@@ -94,10 +86,6 @@ assertEquals("x", %ToPrimitive_String(c));
assertEquals(123, %_ToPrimitive(c));
assertEquals(123, %_ToPrimitive_Number(c));
assertEquals("x", %_ToPrimitive_String(c));
assertEquals(123, %OrdinaryToPrimitive(c, "number"));
assertEquals("x", %OrdinaryToPrimitive(c, "string"));
assertEquals(123, %_OrdinaryToPrimitive(c, "number"));
assertEquals("x", %_OrdinaryToPrimitive(c, "string"));
var d = {
[Symbol.toPrimitive]: function(hint) { return hint }
......@@ -108,3 +96,11 @@ assertEquals("string", %ToPrimitive_String(d));
assertEquals("default", %_ToPrimitive(d));
assertEquals("number", %_ToPrimitive_Number(d));
assertEquals("string", %_ToPrimitive_String(d));
var e = new Date(0);
assertEquals(e.toString(), %ToPrimitive(e));
assertEquals(0, %ToPrimitive_Number(e));
assertEquals(e.toString(), %ToPrimitive_String(e));
assertEquals(e.toString(), %_ToPrimitive(e));
assertEquals(0, %_ToPrimitive_Number(e));
assertEquals(e.toString(), %_ToPrimitive_String(e));
......@@ -48,3 +48,7 @@ var d = {
};
assertEquals("string", %ToString(d));
assertEquals("string", %_ToString(d));
var e = new Date(0);
assertEquals(e.toString(), %ToName(e));
assertEquals(e.toString(), %_ToName(e));
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