Commit 37bd1149 authored by dslomov@chromium.org's avatar dslomov@chromium.org

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=dslomov@chromium.org

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

Patch from Caitlin Potter <caitpotter88@gmail.com>.

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24783 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 5df6dc39
...@@ -245,6 +245,7 @@ action("js2c_experimental") { ...@@ -245,6 +245,7 @@ action("js2c_experimental") {
"src/harmony-array.js", "src/harmony-array.js",
"src/harmony-typedarray.js", "src/harmony-typedarray.js",
"src/harmony-classes.js", "src/harmony-classes.js",
"src/harmony-tostring.js"
] ]
outputs = [ outputs = [
......
...@@ -2130,6 +2130,7 @@ class V8_EXPORT Symbol : public Name { ...@@ -2130,6 +2130,7 @@ class V8_EXPORT Symbol : public Name {
// Well-known symbols // Well-known symbols
static Local<Symbol> GetIterator(Isolate* isolate); static Local<Symbol> GetIterator(Isolate* isolate);
static Local<Symbol> GetUnscopables(Isolate* isolate); static Local<Symbol> GetUnscopables(Isolate* isolate);
static Local<Symbol> GetToStringTag(Isolate* isolate);
V8_INLINE static Symbol* Cast(v8::Value* obj); V8_INLINE static Symbol* Cast(v8::Value* obj);
......
...@@ -3409,6 +3409,37 @@ Local<Array> v8::Object::GetOwnPropertyNames() { ...@@ -3409,6 +3409,37 @@ Local<Array> v8::Object::GetOwnPropertyNames() {
} }
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() { Local<String> v8::Object::ObjectProtoToString() {
i::Isolate* i_isolate = Utils::OpenHandle(this)->GetIsolate(); i::Isolate* i_isolate = Utils::OpenHandle(this)->GetIsolate();
Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate); Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
...@@ -3418,6 +3449,7 @@ Local<String> v8::Object::ObjectProtoToString() { ...@@ -3418,6 +3449,7 @@ Local<String> v8::Object::ObjectProtoToString() {
i::Handle<i::JSObject> self = Utils::OpenHandle(this); i::Handle<i::JSObject> self = Utils::OpenHandle(this);
i::Handle<i::Object> name(self->class_name(), i_isolate); i::Handle<i::Object> name(self->class_name(), i_isolate);
i::Handle<i::Object> tag;
// Native implementation of Object.prototype.toString (v8natives.js): // Native implementation of Object.prototype.toString (v8natives.js):
// var c = %_ClassOf(this); // var c = %_ClassOf(this);
...@@ -3432,6 +3464,27 @@ Local<String> v8::Object::ObjectProtoToString() { ...@@ -3432,6 +3464,27 @@ Local<String> v8::Object::ObjectProtoToString() {
i_isolate->factory()->Arguments_string())) { i_isolate->factory()->Arguments_string())) {
return v8::String::NewFromUtf8(isolate, "[object Object]"); return v8::String::NewFromUtf8(isolate, "[object Object]");
} else { } 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 "; const char* prefix = "[object ";
Local<String> str = Utils::ToLocal(class_name); Local<String> str = Utils::ToLocal(class_name);
const char* postfix = "]"; const char* postfix = "]";
...@@ -6245,6 +6298,11 @@ Local<Symbol> v8::Symbol::GetUnscopables(Isolate* isolate) { ...@@ -6245,6 +6298,11 @@ Local<Symbol> v8::Symbol::GetUnscopables(Isolate* isolate) {
} }
Local<Symbol> v8::Symbol::GetToStringTag(Isolate* isolate) {
return GetWellKnownSymbol(isolate, "Symbol.toStringTag");
}
Local<Private> v8::Private::New(Isolate* isolate, Local<String> name) { Local<Private> v8::Private::New(Isolate* isolate, Local<String> name) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
LOG_API(i_isolate, "Private::New()"); LOG_API(i_isolate, "Private::New()");
......
...@@ -349,7 +349,7 @@ function ArrayToString() { ...@@ -349,7 +349,7 @@ function ArrayToString() {
func = array.join; func = array.join;
} }
if (!IS_SPEC_FUNCTION(func)) { if (!IS_SPEC_FUNCTION(func)) {
return %_CallFunction(array, ObjectToString); return %_CallFunction(array, NoSideEffectsObjectToString);
} }
return %_CallFunction(array, func); return %_CallFunction(array, func);
} }
......
...@@ -77,6 +77,9 @@ function SetUpArrayBuffer() { ...@@ -77,6 +77,9 @@ function SetUpArrayBuffer() {
%AddNamedProperty( %AddNamedProperty(
$ArrayBuffer.prototype, "constructor", $ArrayBuffer, DONT_ENUM); $ArrayBuffer.prototype, "constructor", $ArrayBuffer, DONT_ENUM);
%AddNamedProperty($ArrayBuffer.prototype,
symbolToStringTag, "ArrayBuffer", DONT_ENUM | READ_ONLY);
InstallGetter($ArrayBuffer.prototype, "byteLength", ArrayBufferGetByteLen); InstallGetter($ArrayBuffer.prototype, "byteLength", ArrayBufferGetByteLen);
InstallFunctions($ArrayBuffer, DONT_ENUM, $Array( InstallFunctions($ArrayBuffer, DONT_ENUM, $Array(
......
...@@ -1600,6 +1600,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_object_literals) ...@@ -1600,6 +1600,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_object_literals)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_regexps) EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_regexps)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_arrow_functions) EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_arrow_functions)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_numeric_literals) EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_numeric_literals)
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_tostring)
void Genesis::InstallNativeFunctions_harmony_proxies() { void Genesis::InstallNativeFunctions_harmony_proxies() {
...@@ -1624,6 +1625,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_classes) ...@@ -1624,6 +1625,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_classes)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object_literals) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object_literals)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_arrow_functions) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_arrow_functions)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_numeric_literals) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_numeric_literals)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tostring)
void Genesis::InitializeGlobal_harmony_regexps() { void Genesis::InitializeGlobal_harmony_regexps() {
Handle<JSObject> builtins(native_context()->builtins()); Handle<JSObject> builtins(native_context()->builtins());
...@@ -2164,6 +2166,8 @@ bool Genesis::InstallExperimentalNatives() { ...@@ -2164,6 +2166,8 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_regexps_natives[] = {NULL}; static const char* harmony_regexps_natives[] = {NULL};
static const char* harmony_arrow_functions_natives[] = {NULL}; static const char* harmony_arrow_functions_natives[] = {NULL};
static const char* harmony_numeric_literals_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(); for (int i = ExperimentalNatives::GetDebuggerCount();
i < ExperimentalNatives::GetBuiltinsCount(); i++) { i < ExperimentalNatives::GetBuiltinsCount(); i++) {
......
...@@ -133,6 +133,8 @@ function SetUpSet() { ...@@ -133,6 +133,8 @@ function SetUpSet() {
%SetCode($Set, SetConstructor); %SetCode($Set, SetConstructor);
%FunctionSetPrototype($Set, new $Object()); %FunctionSetPrototype($Set, new $Object());
%AddNamedProperty($Set.prototype, "constructor", $Set, DONT_ENUM); %AddNamedProperty($Set.prototype, "constructor", $Set, DONT_ENUM);
%AddNamedProperty(
$Set.prototype, symbolToStringTag, "Set", DONT_ENUM | READ_ONLY);
%FunctionSetLength(SetForEach, 1); %FunctionSetLength(SetForEach, 1);
...@@ -282,6 +284,8 @@ function SetUpMap() { ...@@ -282,6 +284,8 @@ function SetUpMap() {
%SetCode($Map, MapConstructor); %SetCode($Map, MapConstructor);
%FunctionSetPrototype($Map, new $Object()); %FunctionSetPrototype($Map, new $Object());
%AddNamedProperty($Map.prototype, "constructor", $Map, DONT_ENUM); %AddNamedProperty($Map.prototype, "constructor", $Map, DONT_ENUM);
%AddNamedProperty(
$Map.prototype, symbolToStringTag, "Map", DONT_ENUM | READ_ONLY);
%FunctionSetLength(MapForEach, 1); %FunctionSetLength(MapForEach, 1);
......
...@@ -162,7 +162,8 @@ DEFINE_IMPLICATION(harmony, es_staging) ...@@ -162,7 +162,8 @@ DEFINE_IMPLICATION(harmony, es_staging)
V(harmony_classes, "harmony classes") \ V(harmony_classes, "harmony classes") \
V(harmony_object_literals, "harmony object literal extensions") \ V(harmony_object_literals, "harmony object literal extensions") \
V(harmony_regexps, "reg-exp related harmony features") \ 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) \ #define STAGED_FEATURES(V) \
V(harmony_numeric_literals, "harmony numeric literals (0o77, 0b11)") V(harmony_numeric_literals, "harmony numeric literals (0o77, 0b11)")
......
...@@ -74,6 +74,8 @@ function SetUpGenerators() { ...@@ -74,6 +74,8 @@ function SetUpGenerators() {
GeneratorObjectIterator, DONT_ENUM | DONT_DELETE | READ_ONLY); GeneratorObjectIterator, DONT_ENUM | DONT_DELETE | READ_ONLY);
%AddNamedProperty(GeneratorObjectPrototype, "constructor", %AddNamedProperty(GeneratorObjectPrototype, "constructor",
GeneratorFunctionPrototype, DONT_ENUM | DONT_DELETE | READ_ONLY); GeneratorFunctionPrototype, DONT_ENUM | DONT_DELETE | READ_ONLY);
%AddNamedProperty(GeneratorObjectPrototype,
symbolToStringTag, "Generator", DONT_ENUM | READ_ONLY);
%InternalSetPrototype(GeneratorFunctionPrototype, $Function.prototype); %InternalSetPrototype(GeneratorFunctionPrototype, $Function.prototype);
%SetCode(GeneratorFunctionPrototype, GeneratorFunctionPrototypeConstructor); %SetCode(GeneratorFunctionPrototype, GeneratorFunctionPrototypeConstructor);
%AddNamedProperty(GeneratorFunctionPrototype, "constructor", %AddNamedProperty(GeneratorFunctionPrototype, "constructor",
......
// 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();
...@@ -325,7 +325,10 @@ namespace internal { ...@@ -325,7 +325,10 @@ namespace internal {
V(next_string, "next") \ V(next_string, "next") \
V(byte_length_string, "byteLength") \ V(byte_length_string, "byteLength") \
V(byte_offset_string, "byteOffset") \ 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) \ #define PRIVATE_SYMBOL_LIST(V) \
V(frozen_symbol) \ V(frozen_symbol) \
......
...@@ -223,7 +223,8 @@ function NoSideEffectToString(obj) { ...@@ -223,7 +223,8 @@ function NoSideEffectToString(obj) {
return str; return str;
} }
if (IS_SYMBOL(obj)) return %_CallFunction(obj, SymbolToString); 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"); var constructor = %GetDataProperty(obj, "constructor");
if (typeof constructor == "function") { if (typeof constructor == "function") {
var constructorName = constructor.name; var constructorName = constructor.name;
...@@ -235,7 +236,8 @@ function NoSideEffectToString(obj) { ...@@ -235,7 +236,8 @@ function NoSideEffectToString(obj) {
if (CanBeSafelyTreatedAsAnErrorObject(obj)) { if (CanBeSafelyTreatedAsAnErrorObject(obj)) {
return %_CallFunction(obj, ErrorToString); return %_CallFunction(obj, ErrorToString);
} }
return %_CallFunction(obj, ObjectToString);
return %_CallFunction(obj, NoSideEffectsObjectToString);
} }
// To determine whether we can safely stringify an object using ErrorToString // To determine whether we can safely stringify an object using ErrorToString
...@@ -274,7 +276,7 @@ function ToStringCheckErrorObject(obj) { ...@@ -274,7 +276,7 @@ function ToStringCheckErrorObject(obj) {
function ToDetailString(obj) { 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; var constructor = obj.constructor;
if (typeof constructor == "function") { if (typeof constructor == "function") {
var constructorName = constructor.name; var constructorName = constructor.name;
...@@ -1105,12 +1107,12 @@ function GetTypeName(receiver, requireConstructor) { ...@@ -1105,12 +1107,12 @@ function GetTypeName(receiver, requireConstructor) {
var constructor = receiver.constructor; var constructor = receiver.constructor;
if (!constructor) { if (!constructor) {
return requireConstructor ? null : return requireConstructor ? null :
%_CallFunction(receiver, ObjectToString); %_CallFunction(receiver, NoSideEffectsObjectToString);
} }
var constructorName = constructor.name; var constructorName = constructor.name;
if (!constructorName) { if (!constructorName) {
return requireConstructor ? null : return requireConstructor ? null :
%_CallFunction(receiver, ObjectToString); %_CallFunction(receiver, NoSideEffectsObjectToString);
} }
return constructorName; return constructorName;
} }
......
...@@ -365,6 +365,8 @@ var lastMicrotaskId = 0; ...@@ -365,6 +365,8 @@ var lastMicrotaskId = 0;
%CheckIsBootstrapping(); %CheckIsBootstrapping();
%AddNamedProperty(global, 'Promise', $Promise, DONT_ENUM); %AddNamedProperty(global, 'Promise', $Promise, DONT_ENUM);
%AddNamedProperty(
$Promise.prototype, symbolToStringTag, "Promise", DONT_ENUM | READ_ONLY);
InstallFunctions($Promise, DONT_ENUM, [ InstallFunctions($Promise, DONT_ENUM, [
"defer", PromiseDeferred, "defer", PromiseDeferred,
"accept", PromiseResolved, "accept", PromiseResolved,
......
...@@ -101,6 +101,8 @@ function SetUpSymbol() { ...@@ -101,6 +101,8 @@ function SetUpSymbol() {
// "isConcatSpreadable", symbolIsConcatSpreadable, // "isConcatSpreadable", symbolIsConcatSpreadable,
// "isRegExp", symbolIsRegExp, // "isRegExp", symbolIsRegExp,
"iterator", symbolIterator, "iterator", symbolIterator,
// TODO(dslomov, caitp): Currently defined in harmony-tostring.js ---
// Move here when shipping
// "toStringTag", symbolToStringTag, // "toStringTag", symbolToStringTag,
"unscopables", symbolUnscopables "unscopables", symbolUnscopables
)); ));
...@@ -110,6 +112,8 @@ function SetUpSymbol() { ...@@ -110,6 +112,8 @@ function SetUpSymbol() {
)); ));
%AddNamedProperty($Symbol.prototype, "constructor", $Symbol, DONT_ENUM); %AddNamedProperty($Symbol.prototype, "constructor", $Symbol, DONT_ENUM);
%AddNamedProperty(
$Symbol.prototype, symbolToStringTag, "Symbol", DONT_ENUM | READ_ONLY);
InstallFunctions($Symbol.prototype, DONT_ENUM, $Array( InstallFunctions($Symbol.prototype, DONT_ENUM, $Array(
"toString", SymbolToString, "toString", SymbolToString,
"valueOf", SymbolValueOf "valueOf", SymbolValueOf
......
...@@ -215,8 +215,9 @@ SetUpGlobal(); ...@@ -215,8 +215,9 @@ SetUpGlobal();
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Object // Object
var DefaultObjectToString = NoSideEffectsObjectToString;
// ECMA-262 - 15.2.4.2 // ECMA-262 - 15.2.4.2
function ObjectToString() { function NoSideEffectsObjectToString() {
if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]"; if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]";
if (IS_NULL(this)) return "[object Null]"; if (IS_NULL(this)) return "[object Null]";
return "[object " + %_ClassOf(ToObject(this)) + "]"; return "[object " + %_ClassOf(ToObject(this)) + "]";
...@@ -1409,7 +1410,7 @@ function SetUpObject() { ...@@ -1409,7 +1410,7 @@ function SetUpObject() {
// Set up non-enumerable functions on the Object.prototype object. // Set up non-enumerable functions on the Object.prototype object.
InstallFunctions($Object.prototype, DONT_ENUM, $Array( InstallFunctions($Object.prototype, DONT_ENUM, $Array(
"toString", ObjectToString, "toString", NoSideEffectsObjectToString,
"toLocaleString", ObjectToLocaleString, "toLocaleString", ObjectToLocaleString,
"valueOf", ObjectValueOf, "valueOf", ObjectValueOf,
"hasOwnProperty", ObjectHasOwnProperty, "hasOwnProperty", ObjectHasOwnProperty,
......
...@@ -114,6 +114,8 @@ function SetUpWeakMap() { ...@@ -114,6 +114,8 @@ function SetUpWeakMap() {
%SetCode($WeakMap, WeakMapConstructor); %SetCode($WeakMap, WeakMapConstructor);
%FunctionSetPrototype($WeakMap, new $Object()); %FunctionSetPrototype($WeakMap, new $Object());
%AddNamedProperty($WeakMap.prototype, "constructor", $WeakMap, DONT_ENUM); %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. // Set up the non-enumerable functions on the WeakMap prototype object.
InstallFunctions($WeakMap.prototype, DONT_ENUM, $Array( InstallFunctions($WeakMap.prototype, DONT_ENUM, $Array(
...@@ -214,6 +216,8 @@ function SetUpWeakSet() { ...@@ -214,6 +216,8 @@ function SetUpWeakSet() {
%SetCode($WeakSet, WeakSetConstructor); %SetCode($WeakSet, WeakSetConstructor);
%FunctionSetPrototype($WeakSet, new $Object()); %FunctionSetPrototype($WeakSet, new $Object());
%AddNamedProperty($WeakSet.prototype, "constructor", $WeakSet, DONT_ENUM); %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. // Set up the non-enumerable functions on the WeakSet prototype object.
InstallFunctions($WeakSet.prototype, DONT_ENUM, $Array( InstallFunctions($WeakSet.prototype, DONT_ENUM, $Array(
......
...@@ -2021,6 +2021,19 @@ void SymbolAccessorSetter(Local<Name> name, Local<Value> value, ...@@ -2021,6 +2021,19 @@ void SymbolAccessorSetter(Local<Name> name, Local<Value> value,
SimpleAccessorSetter(Local<String>::Cast(sym->Name()), value, info); 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, void EmptyInterceptorGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) { const v8::PropertyCallbackInfo<v8::Value>& info) {
} }
...@@ -13492,6 +13505,123 @@ THREADED_TEST(ObjectProtoToString) { ...@@ -13492,6 +13505,123 @@ THREADED_TEST(ObjectProtoToString) {
} }
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) { THREADED_TEST(ObjectGetConstructorName) {
LocalContext context; LocalContext context;
v8::HandleScope scope(context->GetIsolate()); v8::HandleScope scope(context->GetIsolate());
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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) { function assertSize(expected, collection) {
...@@ -300,7 +300,7 @@ assertEquals("WeakSet", WeakSet.name); ...@@ -300,7 +300,7 @@ assertEquals("WeakSet", WeakSet.name);
function TestPrototype(C) { function TestPrototype(C) {
assertTrue(C.prototype instanceof Object); assertTrue(C.prototype instanceof Object);
assertEquals({ assertEquals({
value: {}, value: C.prototype,
writable: false, writable: false,
enumerable: false, enumerable: false,
configurable: false configurable: false
...@@ -1423,3 +1423,12 @@ function TestMapConstructorIterableValue(ctor) { ...@@ -1423,3 +1423,12 @@ function TestMapConstructorIterableValue(ctor) {
} }
TestMapConstructorIterableValue(Map); TestMapConstructorIterableValue(Map);
TestMapConstructorIterableValue(WeakMap); TestMapConstructorIterableValue(WeakMap);
function TestCollectionToString(C) {
assertEquals("[object " + C.name + "]",
Object.prototype.toString.call(new C()));
}
TestCollectionToString(Map);
TestCollectionToString(Set);
TestCollectionToString(WeakMap);
TestCollectionToString(WeakSet);
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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. // Test instantations of generators.
...@@ -66,6 +66,7 @@ function TestGeneratorObject() { ...@@ -66,6 +66,7 @@ function TestGeneratorObject() {
assertTrue(iter instanceof g); assertTrue(iter instanceof g);
assertEquals("Generator", %_ClassOf(iter)); assertEquals("Generator", %_ClassOf(iter));
assertEquals("[object Generator]", String(iter)); assertEquals("[object Generator]", String(iter));
assertEquals("[object Generator]", Object.prototype.toString.call(iter));
assertEquals([], Object.getOwnPropertyNames(iter)); assertEquals([], Object.getOwnPropertyNames(iter));
assertTrue(iter !== new g()); assertTrue(iter !== new g());
} }
......
// 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();
...@@ -25,13 +25,21 @@ ...@@ -25,13 +25,21 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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. // Make sure we don't rely on functions patchable by monkeys.
var call = Function.prototype.call.call.bind(Function.prototype.call) var call = Function.prototype.call.call.bind(Function.prototype.call)
var observe = Object.observe; var observe = Object.observe;
var getOwnPropertyNames = Object.getOwnPropertyNames var getOwnPropertyNames = Object.getOwnPropertyNames;
var defineProperty = Object.defineProperty var defineProperty = Object.defineProperty;
(function() {
// Test before clearing global (fails otherwise)
assertEquals("[object Promise]",
Object.prototype.toString.call(new Promise(function() {})));
})();
function clear(o) { function clear(o) {
if (o === null || (typeof o !== 'object' && typeof o !== 'function')) return if (o === null || (typeof o !== 'object' && typeof o !== 'function')) return
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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 = [] var symbols = []
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --harmony-tostring
// ArrayBuffer // ArrayBuffer
function TestByteLength(param, expectedByteLength) { function TestByteLength(param, expectedByteLength) {
...@@ -52,6 +54,8 @@ function TestArrayBufferCreation() { ...@@ -52,6 +54,8 @@ function TestArrayBufferCreation() {
var ab = new ArrayBuffer(); var ab = new ArrayBuffer();
assertSame(0, ab.byteLength); assertSame(0, ab.byteLength);
assertEquals("[object ArrayBuffer]",
Object.prototype.toString.call(ab));
} }
TestArrayBufferCreation(); TestArrayBufferCreation();
...@@ -123,6 +127,9 @@ function TestTypedArray(constr, elementSize, typicalElement) { ...@@ -123,6 +127,9 @@ function TestTypedArray(constr, elementSize, typicalElement) {
var ab = new ArrayBuffer(256*elementSize); var ab = new ArrayBuffer(256*elementSize);
var a0 = new constr(30); var a0 = new constr(30);
assertEquals("[object " + constr.name + "]",
Object.prototype.toString.call(a0));
assertTrue(ArrayBuffer.isView(a0)); assertTrue(ArrayBuffer.isView(a0));
assertSame(elementSize, a0.BYTES_PER_ELEMENT); assertSame(elementSize, a0.BYTES_PER_ELEMENT);
assertSame(30, a0.length); assertSame(30, a0.length);
......
...@@ -1608,6 +1608,7 @@ ...@@ -1608,6 +1608,7 @@
'../../src/generator.js', '../../src/generator.js',
'../../src/harmony-string.js', '../../src/harmony-string.js',
'../../src/harmony-array.js', '../../src/harmony-array.js',
'../../src/harmony-tostring.js',
'../../src/harmony-typedarray.js', '../../src/harmony-typedarray.js',
'../../src/harmony-classes.js', '../../src/harmony-classes.js',
], ],
......
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