[es6] Implement Date.prototype[@@toPrimitive] as C++ builtin.
authorbmeurer <bmeurer@chromium.org>
Mon, 31 Aug 2015 12:52:59 +0000 (05:52 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 31 Aug 2015 12:53:10 +0000 (12:53 +0000)
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}

13 files changed:
src/bootstrapper.cc
src/builtins.cc
src/builtins.h
src/compiler/linkage.cc
src/date.js
src/objects.cc
src/objects.h
src/runtime/runtime-object.cc
src/runtime/runtime.h
test/mjsunit/harmony/to-name.js
test/mjsunit/harmony/to-number.js
test/mjsunit/harmony/to-primitive.js
test/mjsunit/harmony/to-string.js

index 93f9151..06bd125 100644 (file)
@@ -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();
index 8fe53d9..a9690a7 100644 (file)
@@ -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<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
 
index 5c78877..694273a 100644 (file)
@@ -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)             \
index 4b4bcee..3d30830 100644 (file)
@@ -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:
index 5df3afa..4f49565 100644 (file)
@@ -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.
index 260a64b..8d57271 100644 (file)
@@ -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);
index f7aea06..03db209 100644 (file)
@@ -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)
index aa2e9c0..7acffc9 100644 (file)
@@ -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());
index f359e8f..4ae9b88 100644 (file)
@@ -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)                                    \
index c15bfb5..6d5d64e 100644 (file)
@@ -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));
index 7125766..6dc4db5 100644 (file)
@@ -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));
index 7557425..09280bf 100644 (file)
@@ -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));
index 4f03c46..103ba89 100644 (file)
@@ -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));