Commit f7790f76 authored by arv's avatar arv Committed by Commit bot

ES6: Make function name configurable

This is partially based on r21609 but that CL was incomplete.

Function name is still non writable so one has to use defineProperty
to change the actual value.

BUG=v8:3333
LOG=N
R=adamk, mstarzinger@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#26924}
parent 1f0af25b
......@@ -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.
......
// 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);
}
......
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