Fix Object.prototype.toString() when @@toStringTag is not a string.

ES2017 draft 19.1.3.6: If @@toStringTag is not a string, Object.prototype.toString()
returns [object Object], except in the following cases:
 - Array
 - String
 - Arguments
 - Function
 - Error
 - Boolean
 - Number
 - Date
 - RegExp.

For anything else, e.g., Maps, Sets, TypedArrays, or the global object, toString() returns
[object Object] if @@toStringTag is absent or not a string. In order to be able to
easily identify the global object in d8, we set @@toStringTag to "global"
for d8.

CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_chromium_rel_ng;tryserver.blink:linux_blink_rel
BUG=

Review-Url: https://codereview.chromium.org/2071343002
Cr-Commit-Position: refs/heads/master@{#37218}
This commit is contained in:
franzih 2016-06-23 06:37:35 -07:00 committed by Commit bot
parent 5508e16592
commit bdc78957e5
4 changed files with 90 additions and 15 deletions

View File

@ -1135,6 +1135,10 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
String::NewFromUtf8(isolate, "version", NewStringType::kNormal)
.ToLocalChecked(),
FunctionTemplate::New(isolate, Version));
global_template->Set(
Symbol::GetToStringTag(isolate),
String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
.ToLocalChecked());
// Bind the Realm object.
Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);

View File

@ -2537,6 +2537,7 @@ MaybeHandle<String> JSReceiver::BuiltinStringTag(Handle<JSReceiver> object) {
if (is_array.FromJust()) {
return isolate->factory()->Array_string();
}
// TODO(adamk): According to ES2015, we should return "Function" when
// object has a [[Call]] internal method (corresponds to IsCallable).
// But this is well cemented in layout tests and might cause webbreakage.
@ -2545,7 +2546,23 @@ MaybeHandle<String> JSReceiver::BuiltinStringTag(Handle<JSReceiver> object) {
// }
// TODO(adamk): class_name() is expensive, replace with instance type
// checks where possible.
return handle(object->class_name(), isolate);
InstanceType instance_type = object->map()->instance_type();
switch (instance_type) {
case JS_PROXY_TYPE:
case JS_SPECIAL_API_OBJECT_TYPE:
case JS_API_OBJECT_TYPE:
case JS_VALUE_TYPE:
case JS_DATE_TYPE:
// Arguments and Error objects have type JS_OBJECT_TYPE
case JS_OBJECT_TYPE:
case JS_REGEXP_TYPE:
case JS_BOUND_FUNCTION_TYPE:
case JS_FUNCTION_TYPE:
return handle(object->class_name(), isolate);
default:
return isolate->factory()->Object_string();
}
}

View File

@ -13351,7 +13351,7 @@ THREADED_TEST(ObjectProtoToString) {
value =
context->Global()->ObjectProtoToString(context.local()).ToLocalChecked();
CHECK(value->IsString() &&
value->Equals(context.local(), v8_str("[object global]")).FromJust());
value->Equals(context.local(), v8_str("[object Object]")).FromJust());
// Check ordinary object
Local<Value> object =
@ -13397,7 +13397,7 @@ TEST(ObjectProtoToStringES6) {
value =
context->Global()->ObjectProtoToString(context.local()).ToLocalChecked();
CHECK(value->IsString() &&
value->Equals(context.local(), v8_str("[object global]")).FromJust());
value->Equals(context.local(), v8_str("[object Object]")).FromJust());
// Check ordinary object
Local<Value> object = CompileRun("new Object()");

View File

@ -15,15 +15,16 @@ var funs = {
RegExp: [ RegExp ],
Error: [ Error, TypeError, RangeError, SyntaxError, ReferenceError,
EvalError, URIError ]
}
for (f in funs) {
for (i in funs[f]) {
};
for (var f in funs) {
for (var i in funs[f]) {
assertEquals("[object " + f + "]",
Object.prototype.toString.call(new funs[f][i]),
funs[f][i]);
Object.prototype.toString.call(new funs[f][i]),
funs[f][i]);
assertEquals("[object Function]",
Object.prototype.toString.call(funs[f][i]),
funs[f][i]);
Object.prototype.toString.call(funs[f][i]),
funs[f][i]);
}
}
@ -130,11 +131,11 @@ function testObjectToStringPropertyDesc() {
}
testObjectToStringPropertyDesc();
function testObjectToStringOwnNonStringValue() {
var obj = Object.defineProperty({}, Symbol.toStringTag, { value: 1 });
function testObjectToStringOnNonStringValue(obj) {
Object.defineProperty(obj, Symbol.toStringTag, { value: 1 });
assertEquals("[object Object]", ({}).toString.call(obj));
}
testObjectToStringOwnNonStringValue();
testObjectToStringOnNonStringValue({});
// Proxies
@ -149,11 +150,64 @@ assertTag("Function", new Proxy(() => 42, {}));
assertTag("Foo", new Proxy(() => 42, {get() {return "Foo"}}));
assertTag("Function", new Proxy(() => 42, {get() {return 666}}));
revocable = Proxy.revocable([], {});
var revocable = Proxy.revocable([], {});
revocable.revoke();
assertThrows(() => Object.prototype.toString.call(revocable.proxy), TypeError);
handler = {};
var handler = {};
revocable = Proxy.revocable([], handler);
handler.get = () => revocable.revoke();
assertThrows(() => Object.prototype.toString.call(revocable.proxy), TypeError);
function* gen() { yield 1; }
assertTag("GeneratorFunction", gen);
Object.defineProperty(gen, Symbol.toStringTag, {writable: true});
gen[Symbol.toStringTag] = "different string";
assertTag("different string", gen);
gen[Symbol.toStringTag] = 1;
assertTag("Function", gen);
function overwriteToStringTagWithNonStringValue(tag, obj) {
assertTag(tag, obj);
Object.defineProperty(obj, Symbol.toStringTag, {
configurable: true,
value: "different string"
});
assertTag("different string", obj);
testObjectToStringOnNonStringValue(obj);
}
overwriteToStringTagWithNonStringValue("global", global);
overwriteToStringTagWithNonStringValue("Generator", gen());
var arrayBuffer = new ArrayBuffer();
overwriteToStringTagWithNonStringValue("ArrayBuffer", arrayBuffer);
overwriteToStringTagWithNonStringValue("DataView", new DataView(arrayBuffer));
overwriteToStringTagWithNonStringValue("Int8Array", new Int8Array());
overwriteToStringTagWithNonStringValue("Uint8Array", new Uint8Array());
overwriteToStringTagWithNonStringValue("Uint8ClampedArray",
new Uint8ClampedArray());
overwriteToStringTagWithNonStringValue("Int16Array", new Int16Array());
overwriteToStringTagWithNonStringValue("Uint16Array", new Uint16Array());
overwriteToStringTagWithNonStringValue("Int32Array", new Int32Array());
overwriteToStringTagWithNonStringValue("Uint32Array", new Uint32Array());
overwriteToStringTagWithNonStringValue("Float32Array", new Float32Array());
overwriteToStringTagWithNonStringValue("Float64Array", new Float64Array());
var set = new Set();
var map = new Map();
overwriteToStringTagWithNonStringValue("Set", set);
overwriteToStringTagWithNonStringValue("Map", map);
overwriteToStringTagWithNonStringValue("Set Iterator", set[Symbol.iterator]());
overwriteToStringTagWithNonStringValue("Map Iterator", map[Symbol.iterator]());
overwriteToStringTagWithNonStringValue("WeakSet", new WeakSet());
overwriteToStringTagWithNonStringValue("WeakMap", new WeakMap());
overwriteToStringTagWithNonStringValue("Promise", new Promise(function() {}));