Revision: 24783
Author:   dslo...@chromium.org
Date:     Tue Oct 21 17:21:32 2014 UTC
Log:      Update ObjectToString to Harmony-draft algorithm

Updates Object.prototype.toString() to use algorithm described in harmony drafts.

Currently, the behaviour is essentially the same as ES262's version, however this changes when internal structures such as Promise make use of symbolToStringTag (as they are supposed to, see v8:3241), and changes further once
Symbol.toStringTag is exposed publicly.

BUG=v8:3241, v8:3502
LOG=N
R=dslo...@chromium.org

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

Patch from Caitlin Potter <caitpotte...@gmail.com>.
https://code.google.com/p/v8/source/detail?r=24783

Added:
 /branches/bleeding_edge/src/harmony-tostring.js
 /branches/bleeding_edge/test/mjsunit/es6/object-tostring.js
Modified:
 /branches/bleeding_edge/BUILD.gn
 /branches/bleeding_edge/include/v8.h
 /branches/bleeding_edge/src/api.cc
 /branches/bleeding_edge/src/array.js
 /branches/bleeding_edge/src/arraybuffer.js
 /branches/bleeding_edge/src/bootstrapper.cc
 /branches/bleeding_edge/src/collection.js
 /branches/bleeding_edge/src/flag-definitions.h
 /branches/bleeding_edge/src/generator.js
 /branches/bleeding_edge/src/heap/heap.h
 /branches/bleeding_edge/src/messages.js
 /branches/bleeding_edge/src/promise.js
 /branches/bleeding_edge/src/symbol.js
 /branches/bleeding_edge/src/v8natives.js
 /branches/bleeding_edge/src/weak-collection.js
 /branches/bleeding_edge/test/cctest/test-api.cc
 /branches/bleeding_edge/test/mjsunit/es6/collections.js
 /branches/bleeding_edge/test/mjsunit/es6/generators-objects.js
 /branches/bleeding_edge/test/mjsunit/es6/promises.js
 /branches/bleeding_edge/test/mjsunit/es6/symbols.js
 /branches/bleeding_edge/test/mjsunit/harmony/typedarrays.js
 /branches/bleeding_edge/tools/gyp/v8.gyp

=======================================
--- /dev/null
+++ /branches/bleeding_edge/src/harmony-tostring.js Tue Oct 21 17:21:32 2014 UTC
@@ -0,0 +1,66 @@
+// Copyright 2013 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.
+
+'use strict';
+
+// This file relies on the fact that the following declaration has been made
+// in runtime.js and symbol.js:
+// var $Object = global.Object;
+// var $Symbol = global.Symbol;
+
+var symbolToStringTag = InternalSymbol("Symbol.toStringTag");
+
+var kBuiltinStringTags = {
+  "__proto__": null,
+  "Arguments": true,
+  "Array": true,
+  "Boolean": true,
+  "Date": true,
+  "Error": true,
+  "Function": true,
+  "Number": true,
+  "RegExp": true,
+  "String": true
+};
+
+DefaultObjectToString = ObjectToStringHarmony;
+// ES6 draft 08-24-14, section 19.1.3.6
+function ObjectToStringHarmony() {
+ if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]";
+  if (IS_NULL(this)) return "[object Null]";
+  var O = ToObject(this);
+  var builtinTag = %_ClassOf(O);
+  var tag = O[symbolToStringTag];
+  if (IS_UNDEFINED(tag)) {
+    tag = builtinTag;
+  } else if (!IS_STRING(tag)) {
+    return "[object ???]"
+  } else if (tag !== builtinTag && kBuiltinStringTags[tag]) {
+    return "[object ~" + tag + "]";
+  }
+  return "[object " + tag + "]";
+}
+
+function HarmonyToStringExtendSymbolPrototype() {
+  %CheckIsBootstrapping();
+
+  InstallConstants($Symbol, $Array(
+    // TODO(dslomov, caitp): Move to symbol.js when shipping
+   "toStringTag", symbolToStringTag
+  ));
+}
+
+HarmonyToStringExtendSymbolPrototype();
+
+function HarmonyToStringExtendObjectPrototype() {
+  %CheckIsBootstrapping();
+
+  // Set up the non-enumerable functions on the Array prototype object.
+  var desc = ToPropertyDescriptor({
+    value: ObjectToStringHarmony
+  });
+  DefineOwnProperty($Object.prototype, "toString", desc, false);
+}
+
+HarmonyToStringExtendObjectPrototype();
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/es6/object-tostring.js Tue Oct 21 17:21:32 2014 UTC
@@ -0,0 +1,133 @@
+// Copyright 2014 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.
+
+// Flags: --harmony-tostring
+
+var global = this;
+
+var funs = {
+  Object:   [ Object ],
+  Function: [ Function ],
+  Array:    [ Array ],
+  String:   [ String ],
+  Boolean:  [ Boolean ],
+  Number:   [ Number ],
+  Date:     [ Date ],
+  RegExp:   [ RegExp ],
+  Error:    [ Error, TypeError, RangeError, SyntaxError, ReferenceError,
+              EvalError, URIError ]
+}
+for (f in funs) {
+  for (i in funs[f]) {
+    assertEquals("[object " + f + "]",
+                 Object.prototype.toString.call(new funs[f][i]),
+                 funs[f][i]);
+    assertEquals("[object Function]",
+                 Object.prototype.toString.call(funs[f][i]),
+                 funs[f][i]);
+  }
+}
+
+function testToStringTag(className) {
+  // Using builtin toStringTags
+  var obj = {};
+  obj[Symbol.toStringTag] = className;
+  assertEquals("[object ~" + className + "]",
+               Object.prototype.toString.call(obj));
+
+  // Getter throws
+  obj = {};
+  Object.defineProperty(obj, Symbol.toStringTag, {
+    get: function() { throw className; }
+  });
+  assertThrows(function() {
+    Object.prototype.toString.call(obj);
+  }, className);
+
+  // Getter does not throw
+  obj = {};
+  Object.defineProperty(obj, Symbol.toStringTag, {
+    get: function() { return className; }
+  });
+  assertEquals("[object ~" + className + "]",
+               Object.prototype.toString.call(obj));
+
+  // Custom, non-builtin toStringTags
+  obj = {};
+  obj[Symbol.toStringTag] = "X" + className;
+  assertEquals("[object X" + className + "]",
+               Object.prototype.toString.call(obj));
+
+  // With getter
+  obj = {};
+  Object.defineProperty(obj, Symbol.toStringTag, {
+    get: function() { return "X" + className; }
+  });
+  assertEquals("[object X" + className + "]",
+               Object.prototype.toString.call(obj));
+
+  // Undefined toStringTag should return [object className]
+  var obj = className === "Arguments" ?
+      (function() { return arguments; })() : new global[className];
+  obj[Symbol.toStringTag] = undefined;
+  assertEquals("[object " + className + "]",
+               Object.prototype.toString.call(obj));
+
+  // With getter
+  var obj = className === "Arguments" ?
+      (function() { return arguments; })() : new global[className];
+  Object.defineProperty(obj, Symbol.toStringTag, {
+    get: function() { return undefined; }
+  });
+  assertEquals("[object " + className + "]",
+               Object.prototype.toString.call(obj));
+}
+
+[
+  "Arguments",
+  "Array",
+  "Boolean",
+  "Date",
+  "Error",
+  "Function",
+  "Number",
+  "RegExp",
+  "String"
+].forEach(testToStringTag);
+
+function testToStringTagNonString(value) {
+  var obj = {};
+  obj[Symbol.toStringTag] = value;
+  assertEquals("[object ???]", Object.prototype.toString.call(obj));
+
+  // With getter
+  obj = {};
+  Object.defineProperty(obj, Symbol.toStringTag, {
+    get: function() { return value; }
+  });
+  assertEquals("[object ???]", Object.prototype.toString.call(obj));
+}
+
+[
+  null,
+  function() {},
+  [],
+  {},
+  /regexp/,
+  42,
+  Symbol("sym"),
+  new Date(),
+  (function() { return arguments; })(),
+  true,
+  new Error("oops"),
+  new String("str")
+].forEach(testToStringTagNonString);
+
+function testObjectToStringPropertyDesc() {
+  var desc = Object.getOwnPropertyDescriptor(Object.prototype, "toString");
+  assertTrue(desc.writable);
+  assertFalse(desc.enumerable);
+  assertTrue(desc.configurable);
+}
+testObjectToStringPropertyDesc();
=======================================
--- /branches/bleeding_edge/BUILD.gn    Tue Oct 21 12:38:46 2014 UTC
+++ /branches/bleeding_edge/BUILD.gn    Tue Oct 21 17:21:32 2014 UTC
@@ -245,6 +245,7 @@
     "src/harmony-array.js",
     "src/harmony-typedarray.js",
     "src/harmony-classes.js",
+    "src/harmony-tostring.js"
   ]

   outputs = [
=======================================
--- /branches/bleeding_edge/include/v8.h        Tue Oct 21 11:21:54 2014 UTC
+++ /branches/bleeding_edge/include/v8.h        Tue Oct 21 17:21:32 2014 UTC
@@ -2130,6 +2130,7 @@
   // Well-known symbols
   static Local<Symbol> GetIterator(Isolate* isolate);
   static Local<Symbol> GetUnscopables(Isolate* isolate);
+  static Local<Symbol> GetToStringTag(Isolate* isolate);

   V8_INLINE static Symbol* Cast(v8::Value* obj);

=======================================
--- /branches/bleeding_edge/src/api.cc  Tue Oct 21 11:21:54 2014 UTC
+++ /branches/bleeding_edge/src/api.cc  Tue Oct 21 17:21:32 2014 UTC
@@ -3407,6 +3407,37 @@
       isolate->factory()->NewJSArrayWithElements(elms);
   return Utils::ToLocal(scope.CloseAndEscape(result));
 }
+
+
+static bool GetPredefinedToString(i::Handle<i::String> tag,
+                                  Local<String>* result) {
+  i::Isolate* i_isolate = tag->GetIsolate();
+  Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
+  i::Factory* factory = i_isolate->factory();
+
+  if (i::String::Equals(tag, factory->Arguments_string())) {
+    *result = v8::String::NewFromUtf8(isolate, "[object ~Arguments]");
+  } else if (i::String::Equals(tag, factory->Array_string())) {
+    *result = v8::String::NewFromUtf8(isolate, "[object ~Array]");
+  } else if (i::String::Equals(tag, factory->Boolean_string())) {
+    *result = v8::String::NewFromUtf8(isolate, "[object ~Boolean]");
+  } else if (i::String::Equals(tag, factory->Date_string())) {
+    *result = v8::String::NewFromUtf8(isolate, "[object ~Date]");
+  } else if (i::String::Equals(tag, factory->Error_string())) {
+    *result = v8::String::NewFromUtf8(isolate, "[object ~Error]");
+  } else if (i::String::Equals(tag, factory->Function_string())) {
+    *result = v8::String::NewFromUtf8(isolate, "[object ~Function]");
+  } else if (i::String::Equals(tag, factory->Number_string())) {
+    *result = v8::String::NewFromUtf8(isolate, "[object ~Number]");
+  } else if (i::String::Equals(tag, factory->RegExp_string())) {
+    *result = v8::String::NewFromUtf8(isolate, "[object ~RegExp]");
+  } else if (i::String::Equals(tag, factory->String_string())) {
+    *result = v8::String::NewFromUtf8(isolate, "[object ~String]");
+  } else {
+    return false;
+  }
+  return true;
+}


 Local<String> v8::Object::ObjectProtoToString() {
@@ -3418,6 +3449,7 @@
   i::Handle<i::JSObject> self = Utils::OpenHandle(this);

   i::Handle<i::Object> name(self->class_name(), i_isolate);
+  i::Handle<i::Object> tag;

   // Native implementation of Object.prototype.toString (v8natives.js):
   //   var c = %_ClassOf(this);
@@ -3432,6 +3464,27 @@
                           i_isolate->factory()->Arguments_string())) {
       return v8::String::NewFromUtf8(isolate, "[object Object]");
     } else {
+      if (internal::FLAG_harmony_tostring) {
+        i::Handle<i::Symbol> toStringTag =
+            Utils::OpenHandle(*Symbol::GetToStringTag(isolate));
+        EXCEPTION_PREAMBLE(i_isolate);
+        has_pending_exception =
+            !i::Runtime::GetObjectProperty(i_isolate, self, toStringTag)
+                 .ToHandle(&tag);
+        EXCEPTION_BAILOUT_CHECK(i_isolate, Local<v8::String>());
+
+        if (!tag->IsUndefined()) {
+          if (!tag->IsString())
+            return v8::String::NewFromUtf8(isolate, "[object ???]");
+          i::Handle<i::String> tag_name = i::Handle<i::String>::cast(tag);
+          if (!i::String::Equals(class_name, tag_name)) {
+            Local<String> result;
+            if (GetPredefinedToString(tag_name, &result)) return result;
+
+            class_name = tag_name;
+          }
+        }
+      }
       const char* prefix = "[object ";
       Local<String> str = Utils::ToLocal(class_name);
       const char* postfix = "]";
@@ -6243,6 +6296,11 @@
 Local<Symbol> v8::Symbol::GetUnscopables(Isolate* isolate) {
   return GetWellKnownSymbol(isolate, "Symbol.unscopables");
 }
+
+
+Local<Symbol> v8::Symbol::GetToStringTag(Isolate* isolate) {
+  return GetWellKnownSymbol(isolate, "Symbol.toStringTag");
+}


 Local<Private> v8::Private::New(Isolate* isolate, Local<String> name) {
=======================================
--- /branches/bleeding_edge/src/array.js        Fri Oct 17 20:11:47 2014 UTC
+++ /branches/bleeding_edge/src/array.js        Tue Oct 21 17:21:32 2014 UTC
@@ -349,7 +349,7 @@
     func = array.join;
   }
   if (!IS_SPEC_FUNCTION(func)) {
-    return %_CallFunction(array, ObjectToString);
+    return %_CallFunction(array, NoSideEffectsObjectToString);
   }
   return %_CallFunction(array, func);
 }
=======================================
--- /branches/bleeding_edge/src/arraybuffer.js  Mon Jul 14 14:05:30 2014 UTC
+++ /branches/bleeding_edge/src/arraybuffer.js  Tue Oct 21 17:21:32 2014 UTC
@@ -77,6 +77,9 @@
   %AddNamedProperty(
       $ArrayBuffer.prototype, "constructor", $ArrayBuffer, DONT_ENUM);

+  %AddNamedProperty($ArrayBuffer.prototype,
+      symbolToStringTag, "ArrayBuffer", DONT_ENUM | READ_ONLY);
+
InstallGetter($ArrayBuffer.prototype, "byteLength", ArrayBufferGetByteLen);

   InstallFunctions($ArrayBuffer, DONT_ENUM, $Array(
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Mon Oct 20 13:33:34 2014 UTC
+++ /branches/bleeding_edge/src/bootstrapper.cc Tue Oct 21 17:21:32 2014 UTC
@@ -1600,6 +1600,7 @@
 EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_regexps)
 EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_arrow_functions)
 EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_numeric_literals)
+EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_tostring)


 void Genesis::InstallNativeFunctions_harmony_proxies() {
@@ -1624,6 +1625,7 @@
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object_literals)
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_arrow_functions)
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_numeric_literals)
+EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tostring)

 void Genesis::InitializeGlobal_harmony_regexps() {
   Handle<JSObject> builtins(native_context()->builtins());
@@ -2164,6 +2166,8 @@
   static const char* harmony_regexps_natives[] = {NULL};
   static const char* harmony_arrow_functions_natives[] = {NULL};
   static const char* harmony_numeric_literals_natives[] = {NULL};
+ static const char* harmony_tostring_natives[] = {"native harmony-tostring.js",
+                                                   NULL};

   for (int i = ExperimentalNatives::GetDebuggerCount();
        i < ExperimentalNatives::GetBuiltinsCount(); i++) {
=======================================
--- /branches/bleeding_edge/src/collection.js   Wed Oct 15 09:11:32 2014 UTC
+++ /branches/bleeding_edge/src/collection.js   Tue Oct 21 17:21:32 2014 UTC
@@ -133,6 +133,8 @@
   %SetCode($Set, SetConstructor);
   %FunctionSetPrototype($Set, new $Object());
   %AddNamedProperty($Set.prototype, "constructor", $Set, DONT_ENUM);
+  %AddNamedProperty(
+      $Set.prototype, symbolToStringTag, "Set", DONT_ENUM | READ_ONLY);

   %FunctionSetLength(SetForEach, 1);

@@ -282,6 +284,8 @@
   %SetCode($Map, MapConstructor);
   %FunctionSetPrototype($Map, new $Object());
   %AddNamedProperty($Map.prototype, "constructor", $Map, DONT_ENUM);
+  %AddNamedProperty(
+      $Map.prototype, symbolToStringTag, "Map", DONT_ENUM | READ_ONLY);

   %FunctionSetLength(MapForEach, 1);

=======================================
--- /branches/bleeding_edge/src/flag-definitions.h Mon Oct 20 13:33:34 2014 UTC +++ /branches/bleeding_edge/src/flag-definitions.h Tue Oct 21 17:21:32 2014 UTC
@@ -162,7 +162,8 @@
   V(harmony_classes, "harmony classes")                           \
   V(harmony_object_literals, "harmony object literal extensions") \
   V(harmony_regexps, "reg-exp related harmony features")          \
-  V(harmony_arrow_functions, "harmony arrow functions")
+  V(harmony_arrow_functions, "harmony arrow functions")           \
+  V(harmony_tostring, "harmony Symbol.toStringTag")

 #define STAGED_FEATURES(V) \
   V(harmony_numeric_literals, "harmony numeric literals (0o77, 0b11)")
=======================================
--- /branches/bleeding_edge/src/generator.js    Thu Sep 18 17:25:25 2014 UTC
+++ /branches/bleeding_edge/src/generator.js    Tue Oct 21 17:21:32 2014 UTC
@@ -74,6 +74,8 @@
       GeneratorObjectIterator, DONT_ENUM | DONT_DELETE | READ_ONLY);
   %AddNamedProperty(GeneratorObjectPrototype, "constructor",
       GeneratorFunctionPrototype, DONT_ENUM | DONT_DELETE | READ_ONLY);
+  %AddNamedProperty(GeneratorObjectPrototype,
+      symbolToStringTag, "Generator", DONT_ENUM | READ_ONLY);
   %InternalSetPrototype(GeneratorFunctionPrototype, $Function.prototype);
%SetCode(GeneratorFunctionPrototype, GeneratorFunctionPrototypeConstructor);
   %AddNamedProperty(GeneratorFunctionPrototype, "constructor",
=======================================
--- /branches/bleeding_edge/src/heap/heap.h     Tue Oct 21 11:54:10 2014 UTC
+++ /branches/bleeding_edge/src/heap/heap.h     Tue Oct 21 17:21:32 2014 UTC
@@ -325,7 +325,10 @@
   V(next_string, "next")                                   \
   V(byte_length_string, "byteLength")                      \
   V(byte_offset_string, "byteOffset")                      \
-  V(minus_zero_string, "-0")
+  V(minus_zero_string, "-0")                               \
+  V(Array_string, "Array")                                 \
+  V(Error_string, "Error")                                 \
+  V(RegExp_string, "RegExp")

 #define PRIVATE_SYMBOL_LIST(V)      \
   V(frozen_symbol)                  \
=======================================
--- /branches/bleeding_edge/src/messages.js     Tue Oct  7 16:24:59 2014 UTC
+++ /branches/bleeding_edge/src/messages.js     Tue Oct 21 17:21:32 2014 UTC
@@ -223,7 +223,8 @@
     return str;
   }
   if (IS_SYMBOL(obj)) return %_CallFunction(obj, SymbolToString);
- if (IS_OBJECT(obj) && %GetDataProperty(obj, "toString") === ObjectToString) {
+  if (IS_OBJECT(obj)
+      && %GetDataProperty(obj, "toString") === DefaultObjectToString) {
     var constructor = %GetDataProperty(obj, "constructor");
     if (typeof constructor == "function") {
       var constructorName = constructor.name;
@@ -235,7 +236,8 @@
   if (CanBeSafelyTreatedAsAnErrorObject(obj)) {
     return %_CallFunction(obj, ErrorToString);
   }
-  return %_CallFunction(obj, ObjectToString);
+
+  return %_CallFunction(obj, NoSideEffectsObjectToString);
 }

// To determine whether we can safely stringify an object using ErrorToString
@@ -274,7 +276,7 @@


 function ToDetailString(obj) {
-  if (obj != null && IS_OBJECT(obj) && obj.toString === ObjectToString) {
+ if (obj != null && IS_OBJECT(obj) && obj.toString === DefaultObjectToString) {
     var constructor = obj.constructor;
     if (typeof constructor == "function") {
       var constructorName = constructor.name;
@@ -1105,12 +1107,12 @@
   var constructor = receiver.constructor;
   if (!constructor) {
     return requireConstructor ? null :
-        %_CallFunction(receiver, ObjectToString);
+        %_CallFunction(receiver, NoSideEffectsObjectToString);
   }
   var constructorName = constructor.name;
   if (!constructorName) {
     return requireConstructor ? null :
-        %_CallFunction(receiver, ObjectToString);
+        %_CallFunction(receiver, NoSideEffectsObjectToString);
   }
   return constructorName;
 }
=======================================
--- /branches/bleeding_edge/src/promise.js      Tue Sep 30 15:29:08 2014 UTC
+++ /branches/bleeding_edge/src/promise.js      Tue Oct 21 17:21:32 2014 UTC
@@ -365,6 +365,8 @@

   %CheckIsBootstrapping();
   %AddNamedProperty(global, 'Promise', $Promise, DONT_ENUM);
+  %AddNamedProperty(
+ $Promise.prototype, symbolToStringTag, "Promise", DONT_ENUM | READ_ONLY);
   InstallFunctions($Promise, DONT_ENUM, [
     "defer", PromiseDeferred,
     "accept", PromiseResolved,
=======================================
--- /branches/bleeding_edge/src/symbol.js       Wed Oct 15 14:01:20 2014 UTC
+++ /branches/bleeding_edge/src/symbol.js       Tue Oct 21 17:21:32 2014 UTC
@@ -101,6 +101,8 @@
     // "isConcatSpreadable", symbolIsConcatSpreadable,
     // "isRegExp", symbolIsRegExp,
     "iterator", symbolIterator,
+    // TODO(dslomov, caitp): Currently defined in harmony-tostring.js ---
+    // Move here when shipping
     // "toStringTag", symbolToStringTag,
     "unscopables", symbolUnscopables
   ));
@@ -110,6 +112,8 @@
   ));

   %AddNamedProperty($Symbol.prototype, "constructor", $Symbol, DONT_ENUM);
+  %AddNamedProperty(
+ $Symbol.prototype, symbolToStringTag, "Symbol", DONT_ENUM | READ_ONLY);
   InstallFunctions($Symbol.prototype, DONT_ENUM, $Array(
     "toString", SymbolToString,
     "valueOf", SymbolValueOf
=======================================
--- /branches/bleeding_edge/src/v8natives.js    Thu Oct 16 11:24:45 2014 UTC
+++ /branches/bleeding_edge/src/v8natives.js    Tue Oct 21 17:21:32 2014 UTC
@@ -215,8 +215,9 @@
// ----------------------------------------------------------------------------
 // Object

+var DefaultObjectToString = NoSideEffectsObjectToString;
 // ECMA-262 - 15.2.4.2
-function ObjectToString() {
+function NoSideEffectsObjectToString() {
if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]";
   if (IS_NULL(this)) return "[object Null]";
   return "[object " + %_ClassOf(ToObject(this)) + "]";
@@ -1409,7 +1410,7 @@

   // Set up non-enumerable functions on the Object.prototype object.
   InstallFunctions($Object.prototype, DONT_ENUM, $Array(
-    "toString", ObjectToString,
+    "toString", NoSideEffectsObjectToString,
     "toLocaleString", ObjectToLocaleString,
     "valueOf", ObjectValueOf,
     "hasOwnProperty", ObjectHasOwnProperty,
=======================================
--- /branches/bleeding_edge/src/weak-collection.js Tue Aug 19 08:29:48 2014 UTC +++ /branches/bleeding_edge/src/weak-collection.js Tue Oct 21 17:21:32 2014 UTC
@@ -114,6 +114,8 @@
   %SetCode($WeakMap, WeakMapConstructor);
   %FunctionSetPrototype($WeakMap, new $Object());
%AddNamedProperty($WeakMap.prototype, "constructor", $WeakMap, DONT_ENUM);
+  %AddNamedProperty(
+ $WeakMap.prototype, symbolToStringTag, "WeakMap", DONT_ENUM | READ_ONLY);

   // Set up the non-enumerable functions on the WeakMap prototype object.
   InstallFunctions($WeakMap.prototype, DONT_ENUM, $Array(
@@ -214,6 +216,8 @@
   %SetCode($WeakSet, WeakSetConstructor);
   %FunctionSetPrototype($WeakSet, new $Object());
%AddNamedProperty($WeakSet.prototype, "constructor", $WeakSet, DONT_ENUM);
+  %AddNamedProperty(
+ $WeakSet.prototype, symbolToStringTag, "WeakSet", DONT_ENUM | READ_ONLY);

   // Set up the non-enumerable functions on the WeakSet prototype object.
   InstallFunctions($WeakSet.prototype, DONT_ENUM, $Array(
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc Mon Oct 20 09:23:43 2014 UTC +++ /branches/bleeding_edge/test/cctest/test-api.cc Tue Oct 21 17:21:32 2014 UTC
@@ -2020,6 +2020,19 @@
     return;
   SimpleAccessorSetter(Local<String>::Cast(sym->Name()), value, info);
 }
+
+void SymbolAccessorGetterReturnsDefault(
+    Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  CHECK(name->IsSymbol());
+  Local<Symbol> sym = Local<Symbol>::Cast(name);
+  if (sym->Name()->IsUndefined()) return;
+  info.GetReturnValue().Set(info.Data());
+}
+
+static void ThrowingSymbolAccessorGetter(
+    Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
+  info.GetReturnValue().Set(info.GetIsolate()->ThrowException(name));
+}

 void EmptyInterceptorGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
@@ -13490,6 +13503,123 @@
   value = object.As<v8::Object>()->ObjectProtoToString();
   CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
 }
+
+
+TEST(ObjectProtoToStringES6) {
+ // TODO(dslomov, caitp): merge into ObjectProtoToString test once shipped.
+  i::FLAG_harmony_tostring = true;
+  LocalContext context;
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope scope(isolate);
+  Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
+  templ->SetClassName(v8_str("MyClass"));
+
+  Local<String> customized_tostring = v8_str("customized toString");
+
+  // Replace Object.prototype.toString
+  CompileRun(
+      "Object.prototype.toString = function() {"
+      "  return 'customized toString';"
+      "}");
+
+  // Normal ToString call should call replaced Object.prototype.toString
+  Local<v8::Object> instance = templ->GetFunction()->NewInstance();
+  Local<String> value = instance->ToString();
+  CHECK(value->IsString() && value->Equals(customized_tostring));
+
+  // ObjectProtoToString should not call replace toString function.
+  value = instance->ObjectProtoToString();
+  CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
+
+  // Check global
+  value = context->Global()->ObjectProtoToString();
+  CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
+
+  // Check ordinary object
+  Local<Value> object = CompileRun("new Object()");
+  value = object.As<v8::Object>()->ObjectProtoToString();
+  CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
+
+  // Check that ES6 semantics using @@toStringTag work
+  Local<v8::Symbol> toStringTag = v8::Symbol::GetToStringTag(isolate);
+
+#define TEST_TOSTRINGTAG(type, tag, expected)                \
+  do {                                                       \
+    object = CompileRun("new " #type "()");                  \
+    object.As<v8::Object>()->Set(toStringTag, v8_str(#tag)); \
+    value = object.As<v8::Object>()->ObjectProtoToString();  \
+    CHECK(value->IsString() &&                               \
+          value->Equals(v8_str("[object " #expected "]")));  \
+  } while (0)
+
+  TEST_TOSTRINGTAG(Array, Object, Object);
+  TEST_TOSTRINGTAG(Object, Arguments, ~Arguments);
+  TEST_TOSTRINGTAG(Object, Array, ~Array);
+  TEST_TOSTRINGTAG(Object, Boolean, ~Boolean);
+  TEST_TOSTRINGTAG(Object, Date, ~Date);
+  TEST_TOSTRINGTAG(Object, Error, ~Error);
+  TEST_TOSTRINGTAG(Object, Function, ~Function);
+  TEST_TOSTRINGTAG(Object, Number, ~Number);
+  TEST_TOSTRINGTAG(Object, RegExp, ~RegExp);
+  TEST_TOSTRINGTAG(Object, String, ~String);
+  TEST_TOSTRINGTAG(Object, Foo, Foo);
+
+#undef TEST_TOSTRINGTAG
+
+  // @@toStringTag getter throws
+  Local<Value> obj = v8::Object::New(isolate);
+ obj.As<v8::Object>()->SetAccessor(toStringTag, ThrowingSymbolAccessorGetter);
+  {
+    TryCatch try_catch;
+    value = obj.As<v8::Object>()->ObjectProtoToString();
+    CHECK(value.IsEmpty());
+    CHECK(try_catch.HasCaught());
+  }
+
+  // @@toStringTag getter does not throw
+  obj = v8::Object::New(isolate);
+  obj.As<v8::Object>()->SetAccessor(
+      toStringTag, SymbolAccessorGetterReturnsDefault, 0, v8_str("Test"));
+  {
+    TryCatch try_catch;
+    value = obj.As<v8::Object>()->ObjectProtoToString();
+    CHECK(value->IsString() && value->Equals(v8_str("[object Test]")));
+    CHECK(!try_catch.HasCaught());
+  }
+
+  // JS @@toStringTag value
+  obj = CompileRun("obj = {}; obj[Symbol.toStringTag] = 'Test'; obj");
+  {
+    TryCatch try_catch;
+    value = obj.As<v8::Object>()->ObjectProtoToString();
+    CHECK(value->IsString() && value->Equals(v8_str("[object Test]")));
+    CHECK(!try_catch.HasCaught());
+  }
+
+  // JS @@toStringTag getter throws
+  obj = CompileRun(
+      "obj = {}; Object.defineProperty(obj, Symbol.toStringTag, {"
+      "  get: function() { throw 'Test'; }"
+      "}); obj");
+  {
+    TryCatch try_catch;
+    value = obj.As<v8::Object>()->ObjectProtoToString();
+    CHECK(value.IsEmpty());
+    CHECK(try_catch.HasCaught());
+  }
+
+  // JS @@toStringTag getter does not throw
+  obj = CompileRun(
+      "obj = {}; Object.defineProperty(obj, Symbol.toStringTag, {"
+      "  get: function() { return 'Test'; }"
+      "}); obj");
+  {
+    TryCatch try_catch;
+    value = obj.As<v8::Object>()->ObjectProtoToString();
+    CHECK(value->IsString() && value->Equals(v8_str("[object Test]")));
+    CHECK(!try_catch.HasCaught());
+  }
+}


 THREADED_TEST(ObjectGetConstructorName) {
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/collections.js Wed Oct 15 09:11:32 2014 UTC +++ /branches/bleeding_edge/test/mjsunit/es6/collections.js Tue Oct 21 17:21:32 2014 UTC
@@ -25,7 +25,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-// Flags: --expose-gc --allow-natives-syntax
+// Flags: --expose-gc --allow-natives-syntax --harmony-tostring


 function assertSize(expected, collection) {
@@ -300,7 +300,7 @@
 function TestPrototype(C) {
   assertTrue(C.prototype instanceof Object);
   assertEquals({
-    value: {},
+    value: C.prototype,
     writable: false,
     enumerable: false,
     configurable: false
@@ -1423,3 +1423,12 @@
 }
 TestMapConstructorIterableValue(Map);
 TestMapConstructorIterableValue(WeakMap);
+
+function TestCollectionToString(C) {
+  assertEquals("[object " + C.name + "]",
+      Object.prototype.toString.call(new C()));
+}
+TestCollectionToString(Map);
+TestCollectionToString(Set);
+TestCollectionToString(WeakMap);
+TestCollectionToString(WeakSet);
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/generators-objects.js Tue Sep 16 12:30:39 2014 UTC +++ /branches/bleeding_edge/test/mjsunit/es6/generators-objects.js Tue Oct 21 17:21:32 2014 UTC
@@ -25,7 +25,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-// Flags: --harmony-scoping --allow-natives-syntax
+// Flags: --harmony-scoping --allow-natives-syntax --harmony-tostring

 // Test instantations of generators.

@@ -66,6 +66,7 @@
   assertTrue(iter instanceof g);
   assertEquals("Generator", %_ClassOf(iter));
   assertEquals("[object Generator]", String(iter));
+  assertEquals("[object Generator]", Object.prototype.toString.call(iter));
   assertEquals([], Object.getOwnPropertyNames(iter));
   assertTrue(iter !== new g());
 }
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/promises.js Mon Jul 7 09:47:43 2014 UTC +++ /branches/bleeding_edge/test/mjsunit/es6/promises.js Tue Oct 21 17:21:32 2014 UTC
@@ -25,13 +25,21 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-// Flags: --allow-natives-syntax
+// Flags: --allow-natives-syntax --harmony-tostring

 // Make sure we don't rely on functions patchable by monkeys.
 var call = Function.prototype.call.call.bind(Function.prototype.call)
 var observe = Object.observe;
-var getOwnPropertyNames = Object.getOwnPropertyNames
-var defineProperty = Object.defineProperty
+var getOwnPropertyNames = Object.getOwnPropertyNames;
+var defineProperty = Object.defineProperty;
+
+
+(function() {
+  // Test before clearing global (fails otherwise)
+  assertEquals("[object Promise]",
+      Object.prototype.toString.call(new Promise(function() {})));
+})();
+

 function clear(o) {
if (o === null || (typeof o !== 'object' && typeof o !== 'function')) return
=======================================
--- /branches/bleeding_edge/test/mjsunit/es6/symbols.js Wed Oct 15 14:01:20 2014 UTC +++ /branches/bleeding_edge/test/mjsunit/es6/symbols.js Tue Oct 21 17:21:32 2014 UTC
@@ -25,7 +25,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-// Flags: --expose-gc --allow-natives-syntax
+// Flags: --expose-gc --allow-natives-syntax --harmony-tostring

 var symbols = []

=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/typedarrays.js Tue Oct 21 11:54:10 2014 UTC +++ /branches/bleeding_edge/test/mjsunit/harmony/typedarrays.js Tue Oct 21 17:21:32 2014 UTC
@@ -25,6 +25,8 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+// Flags: --harmony-tostring
+
 // ArrayBuffer

 function TestByteLength(param, expectedByteLength) {
@@ -52,6 +54,8 @@

   var ab = new ArrayBuffer();
   assertSame(0, ab.byteLength);
+  assertEquals("[object ArrayBuffer]",
+      Object.prototype.toString.call(ab));
 }

 TestArrayBufferCreation();
@@ -123,6 +127,9 @@
   var ab = new ArrayBuffer(256*elementSize);

   var a0 = new constr(30);
+  assertEquals("[object " + constr.name + "]",
+      Object.prototype.toString.call(a0));
+
   assertTrue(ArrayBuffer.isView(a0));
   assertSame(elementSize, a0.BYTES_PER_ELEMENT);
   assertSame(30, a0.length);
=======================================
--- /branches/bleeding_edge/tools/gyp/v8.gyp    Tue Oct 21 12:38:46 2014 UTC
+++ /branches/bleeding_edge/tools/gyp/v8.gyp    Tue Oct 21 17:21:32 2014 UTC
@@ -1608,6 +1608,7 @@
           '../../src/generator.js',
           '../../src/harmony-string.js',
           '../../src/harmony-array.js',
+          '../../src/harmony-tostring.js',
           '../../src/harmony-typedarray.js',
           '../../src/harmony-classes.js',
         ],

--
--
v8-dev mailing list
v8-dev@googlegroups.com
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to v8-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to