ES6: Make function name configurable
Function name property is now standardized in ES6. It was a Mozilla proprietary extension before. With ES6, the property was made configurable, so that it can be used instead of another proprietary property, displayName. This is a revert of revertc791d84112
. Last time this broke a Chrome browser test which has since been updated:5f75a3be4c
BUG=v8:3333 LOG=N R=mstarzinger@chromium.org,verwaest@chromium.org CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_chromium_rel_ng;tryserver.blink:linux_blink_rel Review URL: https://codereview.chromium.org/977003004 Cr-Commit-Position: refs/heads/master@{#26996}
This commit is contained in:
parent
c4b9d144e0
commit
f6cd009efd
@ -1131,12 +1131,44 @@ void Accessors::FunctionNameGetter(
|
||||
}
|
||||
|
||||
|
||||
MUST_USE_RESULT static MaybeHandle<Object> SetFunctionName(
|
||||
Isolate* isolate, Handle<JSFunction> function, Handle<Object> value) {
|
||||
Handle<Object> old_value;
|
||||
bool is_observed = function->map()->is_observed();
|
||||
if (is_observed) {
|
||||
old_value = handle(function->shared()->name(), isolate);
|
||||
}
|
||||
|
||||
Handle<Name> name = isolate->factory()->name_string();
|
||||
LookupIterator it(function, name);
|
||||
CHECK_EQ(LookupIterator::ACCESSOR, it.state());
|
||||
DCHECK(it.HolderIsReceiverOrHiddenPrototype());
|
||||
it.ReconfigureDataProperty(value, it.property_details().attributes());
|
||||
value = it.WriteDataValue(value);
|
||||
|
||||
if (is_observed && !old_value->SameValue(*value)) {
|
||||
return JSObject::EnqueueChangeRecord(function, "update", name, old_value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
void Accessors::FunctionNameSetter(
|
||||
v8::Local<v8::Name> name,
|
||||
v8::Local<v8::Value> val,
|
||||
const v8::PropertyCallbackInfo<void>& info) {
|
||||
// Function name is non writable, non configurable.
|
||||
UNREACHABLE();
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
|
||||
HandleScope scope(isolate);
|
||||
Handle<Object> value = Utils::OpenHandle(*val);
|
||||
|
||||
if (SetPropertyOnInstanceIfInherited(isolate, info, name, value)) return;
|
||||
|
||||
Handle<JSFunction> object =
|
||||
Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder()));
|
||||
if (SetFunctionName(isolate, object, value).is_null()) {
|
||||
isolate->OptionalRescheduleException(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -382,45 +382,47 @@ void Genesis::SetFunctionInstanceDescriptor(
|
||||
int size = IsFunctionModeWithPrototype(function_mode) ? 5 : 4;
|
||||
Map::EnsureDescriptorSlack(map, size);
|
||||
|
||||
PropertyAttributes attribs = static_cast<PropertyAttributes>(
|
||||
DONT_ENUM | DONT_DELETE | READ_ONLY);
|
||||
PropertyAttributes ro_attribs =
|
||||
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
|
||||
PropertyAttributes roc_attribs =
|
||||
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
|
||||
|
||||
Handle<AccessorInfo> length =
|
||||
Accessors::FunctionLengthInfo(isolate(), attribs);
|
||||
Accessors::FunctionLengthInfo(isolate(), ro_attribs);
|
||||
{ // Add length.
|
||||
AccessorConstantDescriptor d(Handle<Name>(Name::cast(length->name())),
|
||||
length, attribs);
|
||||
length, ro_attribs);
|
||||
map->AppendDescriptor(&d);
|
||||
}
|
||||
Handle<AccessorInfo> name =
|
||||
Accessors::FunctionNameInfo(isolate(), attribs);
|
||||
Accessors::FunctionNameInfo(isolate(), ro_attribs);
|
||||
{ // Add name.
|
||||
AccessorConstantDescriptor d(Handle<Name>(Name::cast(name->name())), name,
|
||||
attribs);
|
||||
roc_attribs);
|
||||
map->AppendDescriptor(&d);
|
||||
}
|
||||
Handle<AccessorInfo> args =
|
||||
Accessors::FunctionArgumentsInfo(isolate(), attribs);
|
||||
Accessors::FunctionArgumentsInfo(isolate(), ro_attribs);
|
||||
{ // Add arguments.
|
||||
AccessorConstantDescriptor d(Handle<Name>(Name::cast(args->name())), args,
|
||||
attribs);
|
||||
ro_attribs);
|
||||
map->AppendDescriptor(&d);
|
||||
}
|
||||
Handle<AccessorInfo> caller =
|
||||
Accessors::FunctionCallerInfo(isolate(), attribs);
|
||||
Accessors::FunctionCallerInfo(isolate(), ro_attribs);
|
||||
{ // Add caller.
|
||||
AccessorConstantDescriptor d(Handle<Name>(Name::cast(caller->name())),
|
||||
caller, attribs);
|
||||
caller, ro_attribs);
|
||||
map->AppendDescriptor(&d);
|
||||
}
|
||||
if (IsFunctionModeWithPrototype(function_mode)) {
|
||||
if (function_mode == FUNCTION_WITH_WRITEABLE_PROTOTYPE) {
|
||||
attribs = static_cast<PropertyAttributes>(attribs & ~READ_ONLY);
|
||||
ro_attribs = static_cast<PropertyAttributes>(ro_attribs & ~READ_ONLY);
|
||||
}
|
||||
Handle<AccessorInfo> prototype =
|
||||
Accessors::FunctionPrototypeInfo(isolate(), attribs);
|
||||
Accessors::FunctionPrototypeInfo(isolate(), ro_attribs);
|
||||
AccessorConstantDescriptor d(Handle<Name>(Name::cast(prototype->name())),
|
||||
prototype, attribs);
|
||||
prototype, ro_attribs);
|
||||
map->AppendDescriptor(&d);
|
||||
}
|
||||
}
|
||||
@ -540,6 +542,8 @@ void Genesis::SetStrictFunctionInstanceDescriptor(
|
||||
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
|
||||
PropertyAttributes ro_attribs =
|
||||
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
|
||||
PropertyAttributes roc_attribs =
|
||||
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
|
||||
|
||||
// Add length.
|
||||
if (function_mode == BOUND_FUNCTION) {
|
||||
@ -557,10 +561,10 @@ void Genesis::SetStrictFunctionInstanceDescriptor(
|
||||
map->AppendDescriptor(&d);
|
||||
}
|
||||
Handle<AccessorInfo> name =
|
||||
Accessors::FunctionNameInfo(isolate(), ro_attribs);
|
||||
Accessors::FunctionNameInfo(isolate(), roc_attribs);
|
||||
{ // Add name.
|
||||
AccessorConstantDescriptor d(Handle<Name>(Name::cast(name->name())), name,
|
||||
ro_attribs);
|
||||
roc_attribs);
|
||||
map->AppendDescriptor(&d);
|
||||
}
|
||||
{ // Add arguments.
|
||||
|
115
test/mjsunit/es6/function-name-configurable.js
Normal file
115
test/mjsunit/es6/function-name-configurable.js
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
function getStrictF() {
|
||||
'use strict';
|
||||
return function f() {};
|
||||
}
|
||||
|
||||
|
||||
function getSloppyF() {
|
||||
return function f() {};
|
||||
}
|
||||
|
||||
|
||||
function test(testFunction) {
|
||||
testFunction(getStrictF());
|
||||
testFunction(getSloppyF());
|
||||
}
|
||||
|
||||
|
||||
function testDescriptor(f) {
|
||||
var descr = Object.getOwnPropertyDescriptor(f, 'name');
|
||||
assertTrue(descr.configurable);
|
||||
assertFalse(descr.enumerable);
|
||||
assertEquals('f', descr.value);
|
||||
assertFalse(descr.writable);
|
||||
}
|
||||
test(testDescriptor);
|
||||
|
||||
|
||||
function testSet(f) {
|
||||
f.name = 'g';
|
||||
assertEquals('f', f.name);
|
||||
}
|
||||
test(testSet);
|
||||
|
||||
|
||||
function testSetStrict(f) {
|
||||
'use strict';
|
||||
assertThrows(function() {
|
||||
f.name = 'g';
|
||||
}, TypeError);
|
||||
}
|
||||
test(testSetStrict);
|
||||
|
||||
|
||||
function testReconfigureAsDataProperty(f) {
|
||||
Object.defineProperty(f, 'name', {
|
||||
value: 'g',
|
||||
});
|
||||
assertEquals('g', f.name);
|
||||
Object.defineProperty(f, 'name', {
|
||||
writable: true
|
||||
});
|
||||
f.name = 'h';
|
||||
assertEquals('h', f.name);
|
||||
|
||||
f.name = 42;
|
||||
assertEquals(42, f.name);
|
||||
}
|
||||
test(testReconfigureAsDataProperty);
|
||||
|
||||
|
||||
function testReconfigureAsAccessorProperty(f) {
|
||||
var name = 'g';
|
||||
Object.defineProperty(f, 'name', {
|
||||
get: function() { return name; },
|
||||
set: function(v) { name = v; }
|
||||
});
|
||||
assertEquals('g', f.name);
|
||||
f.name = 'h';
|
||||
assertEquals('h', f.name);
|
||||
}
|
||||
test(testReconfigureAsAccessorProperty);
|
||||
|
||||
|
||||
function testFunctionToString(f) {
|
||||
Object.defineProperty(f, 'name', {
|
||||
value: {toString: function() { assertUnreachable(); }},
|
||||
});
|
||||
assertEquals('function f() {}', f.toString());
|
||||
}
|
||||
test(testFunctionToString);
|
||||
|
||||
|
||||
(function testSetOnInstance() {
|
||||
// This needs to come before testDelete below
|
||||
assertTrue(Function.prototype.hasOwnProperty('name'));
|
||||
|
||||
function f() {}
|
||||
delete f.name;
|
||||
assertEquals('Empty', f.name);
|
||||
|
||||
f.name = 42;
|
||||
assertEquals('Empty', f.name); // non writable prototype property.
|
||||
assertFalse(f.hasOwnProperty('name'));
|
||||
|
||||
Object.defineProperty(Function.prototype, 'name', {writable: true});
|
||||
|
||||
f.name = 123;
|
||||
assertTrue(f.hasOwnProperty('name'));
|
||||
assertEquals(123, f.name);
|
||||
})();
|
||||
|
||||
|
||||
(function testDelete() {
|
||||
function f() {}
|
||||
assertTrue(delete f.name);
|
||||
assertFalse(f.hasOwnProperty('name'));
|
||||
assertEquals('Empty', f.name);
|
||||
|
||||
assertTrue(delete Function.prototype.name);
|
||||
assertEquals(undefined, f.name);
|
||||
})();
|
@ -1142,7 +1142,8 @@ var properties = ["a", "1", 1, "length", "setPrototype", "name", "caller"];
|
||||
function blacklisted(obj, prop) {
|
||||
return (obj instanceof Int32Array && prop == 1) ||
|
||||
(obj instanceof Int32Array && prop === "length") ||
|
||||
(obj instanceof ArrayBuffer && prop == 1)
|
||||
(obj instanceof ArrayBuffer && prop == 1) ||
|
||||
(obj instanceof Function && prop === "name"); // Has its own test.
|
||||
}
|
||||
|
||||
for (var i in objects) for (var j in properties) {
|
||||
@ -1798,3 +1799,28 @@ for (var b1 = 0; b1 < 2; ++b1)
|
||||
for (var n = 0; n < 3; ++n)
|
||||
for (var i in mutationByIncr)
|
||||
TestFastElementsLength(mutationByIncr[i], b1 != 0, b2 != 0, 7*n, 7*n+1);
|
||||
|
||||
|
||||
(function TestFunctionName() {
|
||||
reset();
|
||||
|
||||
function fun() {}
|
||||
Object.observe(fun, observer.callback);
|
||||
fun.name = 'x'; // No change. Not writable.
|
||||
Object.defineProperty(fun, 'name', {value: 'a'});
|
||||
Object.defineProperty(fun, 'name', {writable: true});
|
||||
fun.name = 'b';
|
||||
delete fun.name;
|
||||
fun.name = 'x'; // No change. Function.prototype.name is non writable
|
||||
Object.defineProperty(Function.prototype, 'name', {writable: true});
|
||||
fun.name = 'c';
|
||||
fun.name = 'c'; // Same, no update.
|
||||
Object.deliverChangeRecords(observer.callback);
|
||||
observer.assertCallbackRecords([
|
||||
{ object: fun, type: 'update', name: 'name', oldValue: 'fun' },
|
||||
{ object: fun, type: 'reconfigure', name: 'name'},
|
||||
{ object: fun, type: 'update', name: 'name', oldValue: 'a' },
|
||||
{ object: fun, type: 'delete', name: 'name', oldValue: 'b' },
|
||||
{ object: fun, type: 'add', name: 'name' },
|
||||
]);
|
||||
})();
|
||||
|
@ -80,8 +80,9 @@ assertFalse(Object.getOwnPropertyDescriptor(f, 'prototype').writable);
|
||||
assertThrows("'use strict'; f.prototype = {}");
|
||||
assertThrows("Object.defineProperty(f, 'prototype', { value: {} })");
|
||||
|
||||
// Verify that non-writability of other properties is respected.
|
||||
assertThrows("Object.defineProperty(f, 'name', { value: {} })");
|
||||
// Verify that non-configurability of other properties is respected, but
|
||||
// non-writability is ignored by Object.defineProperty().
|
||||
Object.defineProperty(f, 'name', { value: {} });
|
||||
assertThrows("Object.defineProperty(f, 'length', { value: {} })");
|
||||
assertThrows("Object.defineProperty(f, 'caller', { value: {} })");
|
||||
assertThrows("Object.defineProperty(f, 'arguments', { value: {} })");
|
||||
|
@ -39,7 +39,7 @@ function g(x) {
|
||||
|
||||
function checkNameDescriptor(f) {
|
||||
var descriptor = Object.getOwnPropertyDescriptor(f, "name");
|
||||
assertFalse(descriptor.configurable);
|
||||
assertTrue(descriptor.configurable);
|
||||
assertFalse(descriptor.enumerable);
|
||||
assertFalse(descriptor.writable);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user