ES6 Array.prototype.toString falls back on Object.prototype.toString if method "join...
authorcaitpotter88 <caitpotter88@gmail.com>
Fri, 23 Jan 2015 15:21:29 +0000 (07:21 -0800)
committerCommit bot <commit-bot@chromium.org>
Fri, 23 Jan 2015 15:21:44 +0000 (15:21 +0000)
BUG=v8:3793
LOG=Y
R=dslomov@chromium.org, arv@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#26253}

src/array.js
src/harmony-tostring.js
test/mjsunit/es6/array-tostring.js [new file with mode: 0644]
test/mjsunit/es6/object-tostring.js

index c702d8c..784f51f 100644 (file)
@@ -361,7 +361,7 @@ function ArrayToString() {
     func = array.join;
   }
   if (!IS_SPEC_FUNCTION(func)) {
-    return %_CallFunction(array, NoSideEffectsObjectToString);
+    return %_CallFunction(array, DefaultObjectToString);
   }
   return %_CallFunction(array, func);
 }
index 0336456..a5d892c 100644 (file)
@@ -33,7 +33,7 @@ function ObjectToStringHarmony() {
   if (IS_UNDEFINED(tag)) {
     tag = builtinTag;
   } else if (!IS_STRING(tag)) {
-    return "[object ???]"
+    return "[object ???]";
   } else if (tag !== builtinTag && kBuiltinStringTags[tag]) {
     return "[object ~" + tag + "]";
   }
diff --git a/test/mjsunit/es6/array-tostring.js b/test/mjsunit/es6/array-tostring.js
new file mode 100644 (file)
index 0000000..625011f
--- /dev/null
@@ -0,0 +1,157 @@
+// 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 ],
+  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 + "]",
+                 Array.prototype.toString.call(new funs[f][i]),
+                 funs[f][i]);
+    assertEquals("[object Function]",
+                 Array.prototype.toString.call(funs[f][i]),
+                 funs[f][i]);
+  }
+}
+
+
+function testToStringTag(className) {
+  // Using builtin toStringTags
+  var obj = {};
+  obj[Symbol.toStringTag] = className;
+  assertEquals("[object ~" + className + "]",
+               Array.prototype.toString.call(obj));
+
+  // Getter throws
+  obj = {};
+  Object.defineProperty(obj, Symbol.toStringTag, {
+    get: function() { throw className; }
+  });
+  assertThrows(function() {
+    Array.prototype.toString.call(obj);
+  }, className);
+
+  // Getter does not throw
+  obj = {};
+  Object.defineProperty(obj, Symbol.toStringTag, {
+    get: function() { return className; }
+  });
+  assertEquals("[object ~" + className + "]",
+               Array.prototype.toString.call(obj));
+
+  // Custom, non-builtin toStringTags
+  obj = {};
+  obj[Symbol.toStringTag] = "X" + className;
+  assertEquals("[object X" + className + "]",
+               Array.prototype.toString.call(obj));
+
+  // With getter
+  obj = {};
+  Object.defineProperty(obj, Symbol.toStringTag, {
+    get: function() { return "X" + className; }
+  });
+  assertEquals("[object X" + className + "]",
+               Array.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 + "]",
+               Array.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 + "]",
+               Array.prototype.toString.call(obj));
+}
+
+
+[
+  "Arguments",
+  "Boolean",
+  "Date",
+  "Error",
+  "Function",
+  "Number",
+  "RegExp",
+  "String"
+].forEach(testToStringTag);
+
+
+function testToStringTagNonString(value) {
+  var obj = {};
+  obj[Symbol.toStringTag] = value;
+  assertEquals("[object ???]", Array.prototype.toString.call(obj));
+
+  // With getter
+  obj = {};
+  Object.defineProperty(obj, Symbol.toStringTag, {
+    get: function() { return value; }
+  });
+  assertEquals("[object ???]", Array.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 testArrayToStringPropertyDesc() {
+  var desc = Object.getOwnPropertyDescriptor(Object.prototype, "toString");
+  assertTrue(desc.writable);
+  assertFalse(desc.enumerable);
+  assertTrue(desc.configurable);
+}
+testArrayToStringPropertyDesc();
+
+
+function testArrayToStringOwnNonStringValue() {
+  var obj = Object.defineProperty({}, Symbol.toStringTag, { value: 1 });
+  assertEquals("[object ???]", ([]).toString.call(obj));
+}
+testArrayToStringOwnNonStringValue();
+
+
+function testArrayToStringBasic() {
+  assertEquals("1,2,3", [1,2,3].toString());
+  assertEquals(",,3", [,,3].toString());
+}
+testArrayToStringBasic();
+
+
+function testArrayToStringObjectWithCallableJoin() {
+  var obj = { join: function() { return "CallableJoin"; } };
+  assertEquals("CallableJoin", Array.prototype.toString.call(obj));
+}
+testArrayToStringObjectWithCallableJoin();
index 26dff14..8999a18 100644 (file)
@@ -131,3 +131,9 @@ function testObjectToStringPropertyDesc() {
   assertTrue(desc.configurable);
 }
 testObjectToStringPropertyDesc();
+
+function testObjectToStringOwnNonStringValue() {
+  var obj = Object.defineProperty({}, Symbol.toStringTag, { value: 1 });
+  assertEquals("[object ???]", ({}).toString.call(obj));
+}
+testObjectToStringOwnNonStringValue();