From 3c164506f566ad99016f80c21caa56c060e3e262 Mon Sep 17 00:00:00 2001 From: bmeurer Date: Mon, 31 Aug 2015 05:52:59 -0700 Subject: [PATCH] [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} --- src/bootstrapper.cc | 69 ++++++++++++++++++++++++++++-------- src/builtins.cc | 24 +++++++++++++ src/builtins.h | 2 ++ src/compiler/linkage.cc | 1 - src/date.js | 18 ---------- src/objects.cc | 64 ++++++++++++++++++++++++++------- src/objects.h | 14 +++++++- src/runtime/runtime-object.cc | 12 ------- src/runtime/runtime.h | 1 - test/mjsunit/harmony/to-name.js | 4 +++ test/mjsunit/harmony/to-number.js | 4 +++ test/mjsunit/harmony/to-primitive.js | 20 +++++------ test/mjsunit/harmony/to-string.js | 4 +++ 13 files changed, 164 insertions(+), 73 deletions(-) diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 93f9151..06bd125 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -380,26 +380,43 @@ void Bootstrapper::DetachGlobal(Handle env) { } -static Handle InstallFunction(Handle target, - const char* name, InstanceType type, - int instance_size, - MaybeHandle maybe_prototype, - Builtins::Name call, - bool strict_function_map = false) { +namespace { + +Handle InstallFunction(Handle target, Handle name, + InstanceType type, int instance_size, + MaybeHandle maybe_prototype, + Builtins::Name call, + PropertyAttributes attributes, + bool strict_function_map = false) { Isolate* isolate = target->GetIsolate(); Factory* factory = isolate->factory(); - Handle internalized_name = factory->InternalizeUtf8String(name); + Handle name_string = Name::ToFunctionName(name).ToHandleChecked(); Handle call_code = Handle(isolate->builtins()->builtin(call)); Handle prototype; static const bool kReadOnlyPrototype = false; static const bool kInstallConstructor = false; Handle 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 InstallFunction(Handle target, const char* name, + InstanceType type, int instance_size, + MaybeHandle 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 InstallFunction(Handle 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, 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 key = factory()->Date_string(); + Handle date = Handle::cast( + Object::GetProperty(handle(native_context()->global_object()), key) + .ToHandleChecked()); + Handle proto = + Handle(JSObject::cast(date->instance_prototype())); + + // Install the @@toPrimitive function. + Handle to_primitive = + InstallFunction(proto, factory()->to_primitive_symbol(), JS_OBJECT_TYPE, + JSObject::kHeaderSize, MaybeHandle(), + Builtins::kDateToPrimitive, + static_cast(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 key = factory()->Function_string(); diff --git a/src/builtins.cc b/src/builtins.cc index 8fe53d9..a9690a7 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -740,6 +740,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 receiver = args.at(0); + Handle hint = args.at(1); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, + JSDate::ToPrimitive(receiver, hint)); + return *result; +} + + +// ----------------------------------------------------------------------------- // Throwers for restricted function properties and strict arguments object // properties diff --git a/src/builtins.h b/src/builtins.h index 5c78877..694273a 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -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) \ diff --git a/src/compiler/linkage.cc b/src/compiler/linkage.cc index 4b4bcee..3d30830 100644 --- a/src/compiler/linkage.cc +++ b/src/compiler/linkage.cc @@ -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: diff --git a/src/date.js b/src/date.js index 5df3afa..4f49565 100644 --- a/src/date.js +++ b/src/date.js @@ -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. diff --git a/src/objects.cc b/src/objects.cc index 260a64b..8d57271 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -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 JSReceiver::ToPrimitive(Handle 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 JSReceiver::OrdinaryToPrimitive(Handle receiver, - Handle hint) { +MaybeHandle JSReceiver::OrdinaryToPrimitive( + Handle receiver, OrdinaryToPrimitiveHint hint) { Isolate* const isolate = receiver->GetIsolate(); Handle 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 name : method_names) { Handle method; @@ -8350,6 +8352,21 @@ bool String::LooksValid() { } +// static +MaybeHandle Name::ToFunctionName(Handle name) { + if (name->IsString()) return Handle::cast(name); + // ES6 section 9.2.11 SetFunctionName, step 4. + Isolate* const isolate = name->GetIsolate(); + Handle description(Handle::cast(name)->name(), isolate); + if (description->IsUndefined()) return isolate->factory()->empty_string(); + IncrementalStringBuilder builder(isolate); + builder.AppendCharacter('['); + builder.AppendString(Handle::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 JSDate::ToPrimitive(Handle receiver, + Handle hint) { + Isolate* const isolate = receiver->GetIsolate(); + if (hint->IsString()) { + Handle hint_string = Handle::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); diff --git a/src/objects.h b/src/objects.h index f7aea06..03db209 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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 receiver, ToPrimitiveHint hint = ToPrimitiveHint::kDefault); MUST_USE_RESULT static MaybeHandle OrdinaryToPrimitive( - Handle receiver, Handle hint); + Handle receiver, OrdinaryToPrimitiveHint hint); // Implementation of [[HasProperty]], ECMA-262 5th edition, section 8.12.6. MUST_USE_RESULT static inline Maybe 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 ToPrimitive( + Handle receiver, Handle hint); // Dispatched behavior. DECLARE_PRINTER(JSDate) @@ -8115,6 +8123,10 @@ class Name: public HeapObject { static inline Handle Flatten(Handle 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 ToFunctionName(Handle name); + DECLARE_CAST(Name) DECLARE_PRINTER(Name) diff --git a/src/runtime/runtime-object.cc b/src/runtime/runtime-object.cc index aa2e9c0..7acffc9 100644 --- a/src/runtime/runtime-object.cc +++ b/src/runtime/runtime-object.cc @@ -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 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()); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index f359e8f..4ae9b88 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -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) \ diff --git a/test/mjsunit/harmony/to-name.js b/test/mjsunit/harmony/to-name.js index c15bfb5..6d5d64e 100644 --- a/test/mjsunit/harmony/to-name.js +++ b/test/mjsunit/harmony/to-name.js @@ -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)); diff --git a/test/mjsunit/harmony/to-number.js b/test/mjsunit/harmony/to-number.js index 7125766..6dc4db5 100644 --- a/test/mjsunit/harmony/to-number.js +++ b/test/mjsunit/harmony/to-number.js @@ -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)); diff --git a/test/mjsunit/harmony/to-primitive.js b/test/mjsunit/harmony/to-primitive.js index 7557425..09280bf 100644 --- a/test/mjsunit/harmony/to-primitive.js +++ b/test/mjsunit/harmony/to-primitive.js @@ -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)); diff --git a/test/mjsunit/harmony/to-string.js b/test/mjsunit/harmony/to-string.js index 4f03c46..103ba89 100644 --- a/test/mjsunit/harmony/to-string.js +++ b/test/mjsunit/harmony/to-string.js @@ -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)); -- 2.7.4