Commit 2367abf0 authored by adamk's avatar adamk Committed by Commit bot

[es6] Handle function names in object and class literals

This required refactoring ParsePropertyDefinition to pass the parsed
string name as an out param, since ObjectLiteralProperty stores Smis
for Smi-representable property keys.

Computed properties are not yet handled in this patch.

BUG=v8:3699
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#33141}
parent 26f2f242
......@@ -255,6 +255,7 @@ class AstValue : public ZoneObject {
F(dot_catch, ".catch") \
F(empty, "") \
F(eval, "eval") \
F(get_space, "get ") \
F(let, "let") \
F(native, "native") \
F(new_target, ".new.target") \
......@@ -262,6 +263,7 @@ class AstValue : public ZoneObject {
F(proto, "__proto__") \
F(prototype, "prototype") \
F(rest_parameter, ".rest_parameter") \
F(set_space, "set ") \
F(this, "this") \
F(this_function, ".this_function") \
F(undefined, "undefined") \
......
......@@ -2589,8 +2589,8 @@ class FunctionLiteral final : public Expression {
DECLARE_NODE_TYPE(FunctionLiteral)
Handle<String> name() const { return raw_name_->string(); }
const AstRawString* raw_name() const { return raw_name_; }
void set_raw_name(const AstRawString* name) { raw_name_ = name; }
const AstString* raw_name() const { return raw_name_; }
void set_raw_name(const AstString* name) { raw_name_ = name; }
Scope* scope() const { return scope_; }
ZoneList<Statement*>* body() const { return body_; }
void set_function_token_position(int pos) { function_token_position_ = pos; }
......@@ -2692,7 +2692,7 @@ class FunctionLiteral final : public Expression {
}
protected:
FunctionLiteral(Zone* zone, const AstRawString* name,
FunctionLiteral(Zone* zone, const AstString* name,
AstValueFactory* ast_value_factory, Scope* scope,
ZoneList<Statement*>* body, int materialized_literal_count,
int expected_property_count, int parameter_count,
......@@ -2724,7 +2724,7 @@ class FunctionLiteral final : public Expression {
}
private:
const AstRawString* raw_name_;
const AstString* raw_name_;
Handle<String> name_;
Scope* scope_;
ZoneList<Statement*>* body_;
......
......@@ -739,7 +739,7 @@ class ParserBase : public Traits {
ObjectLiteralPropertyT ParsePropertyDefinition(
ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends,
bool is_static, bool* is_computed_name, bool* has_seen_constructor,
ExpressionClassifier* classifier, bool* ok);
ExpressionClassifier* classifier, IdentifierT* name, bool* ok);
typename Traits::Type::ExpressionList ParseArguments(
Scanner::Location* first_spread_pos, ExpressionClassifier* classifier,
bool* ok);
......@@ -1627,10 +1627,9 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT
ParserBase<Traits>::ParsePropertyDefinition(
ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends,
bool is_static, bool* is_computed_name, bool* has_seen_constructor,
ExpressionClassifier* classifier, bool* ok) {
ExpressionClassifier* classifier, IdentifierT* name, bool* ok) {
DCHECK(!in_class || is_static || has_seen_constructor != nullptr);
ExpressionT value = this->EmptyExpression();
IdentifierT name = this->EmptyIdentifier();
bool is_get = false;
bool is_set = false;
bool name_is_static = false;
......@@ -1642,12 +1641,12 @@ ParserBase<Traits>::ParsePropertyDefinition(
bool is_identifier = false;
bool is_escaped_keyword = false;
ExpressionT name_expression = ParsePropertyName(
&name, &is_get, &is_set, &name_is_static, is_computed_name,
&is_identifier, &is_escaped_keyword, classifier,
name, &is_get, &is_set, &name_is_static, is_computed_name, &is_identifier,
&is_escaped_keyword, classifier,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
if (fni_ != nullptr && !*is_computed_name) {
this->PushLiteralName(fni_, name);
this->PushLiteralName(fni_, *name);
}
bool escaped_static =
......@@ -1700,7 +1699,7 @@ ParserBase<Traits>::ParsePropertyDefinition(
}
ExpressionT lhs = this->ExpressionFromIdentifier(
name, next_beg_pos, next_end_pos, scope_, factory());
*name, next_beg_pos, next_end_pos, scope_, factory());
CheckDestructuringElement(lhs, classifier, next_beg_pos, next_end_pos);
if (peek() == Token::ASSIGN) {
......@@ -1749,7 +1748,7 @@ ParserBase<Traits>::ParsePropertyDefinition(
FunctionKind kind = is_generator ? FunctionKind::kConciseGeneratorMethod
: FunctionKind::kConciseMethod;
if (in_class && !is_static && this->IsConstructor(name)) {
if (in_class && !is_static && this->IsConstructor(*name)) {
*has_seen_constructor = true;
kind = has_extends ? FunctionKind::kSubclassConstructor
: FunctionKind::kBaseConstructor;
......@@ -1758,7 +1757,7 @@ ParserBase<Traits>::ParsePropertyDefinition(
if (!in_class) kind = WithObjectLiteralBit(kind);
value = this->ParseFunctionLiteral(
name, scanner()->location(), kSkipFunctionNameCheck, kind,
*name, scanner()->location(), kSkipFunctionNameCheck, kind,
RelocInfo::kNoPosition, FunctionLiteral::ANONYMOUS_EXPRESSION,
FunctionLiteral::NORMAL_ARITY, language_mode(),
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
......@@ -1771,20 +1770,22 @@ ParserBase<Traits>::ParsePropertyDefinition(
if (in_class && name_is_static && !is_static) {
// ClassElement (static)
// 'static' MethodDefinition
*name = this->EmptyIdentifier();
return ParsePropertyDefinition(checker, true, has_extends, true,
is_computed_name, nullptr, classifier, ok);
is_computed_name, nullptr, classifier, name,
ok);
}
if (is_get || is_set) {
// MethodDefinition (Accessors)
// get PropertyName '(' ')' '{' FunctionBody '}'
// set PropertyName '(' PropertySetParameterList ')' '{' FunctionBody '}'
name = this->EmptyIdentifier();
*name = this->EmptyIdentifier();
bool dont_care = false;
name_token = peek();
name_expression = ParsePropertyName(
&name, &dont_care, &dont_care, &dont_care, is_computed_name, &dont_care,
name, &dont_care, &dont_care, &dont_care, is_computed_name, &dont_care,
&dont_care, classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
if (!*is_computed_name) {
......@@ -1796,7 +1797,7 @@ ParserBase<Traits>::ParsePropertyDefinition(
FunctionKind kind = FunctionKind::kAccessorFunction;
if (!in_class) kind = WithObjectLiteralBit(kind);
typename Traits::Type::FunctionLiteral value = this->ParseFunctionLiteral(
name, scanner()->location(), kSkipFunctionNameCheck, kind,
*name, scanner()->location(), kSkipFunctionNameCheck, kind,
RelocInfo::kNoPosition, FunctionLiteral::ANONYMOUS_EXPRESSION,
is_get ? FunctionLiteral::GETTER_ARITY : FunctionLiteral::SETTER_ARITY,
language_mode(), CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
......@@ -1806,7 +1807,7 @@ ParserBase<Traits>::ParsePropertyDefinition(
// statically we can skip the extra runtime check.
if (!*is_computed_name) {
name_expression =
factory()->NewStringLiteral(name, name_expression->position());
factory()->NewStringLiteral(*name, name_expression->position());
}
return factory()->NewObjectLiteralProperty(
......@@ -1845,9 +1846,10 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
const bool is_static = false;
const bool has_extends = false;
bool is_computed_name = false;
IdentifierT name = this->EmptyIdentifier();
ObjectLiteralPropertyT property = this->ParsePropertyDefinition(
&checker, in_class, has_extends, is_static, &is_computed_name, NULL,
classifier, CHECK_OK);
classifier, &name, CHECK_OK);
if (is_computed_name) {
has_computed_names = true;
......@@ -1871,6 +1873,10 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
}
if (fni_ != nullptr) fni_->Infer();
if (allow_harmony_function_name()) {
Traits::SetFunctionNameFromPropertyName(property, name);
}
}
Expect(Token::RBRACE, CHECK_OK);
......
......@@ -4927,9 +4927,10 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
bool is_computed_name = false; // Classes do not care about computed
// property names here.
ExpressionClassifier classifier;
const AstRawString* name = nullptr;
ObjectLiteral::Property* property = ParsePropertyDefinition(
&checker, in_class, has_extends, is_static, &is_computed_name,
&has_seen_constructor, &classifier, CHECK_OK);
&has_seen_constructor, &classifier, &name, CHECK_OK);
ValidateExpression(&classifier, CHECK_OK);
if (has_seen_constructor && constructor == NULL) {
......@@ -4940,6 +4941,10 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
}
if (fni_ != NULL) fni_->Infer();
if (allow_harmony_function_name()) {
SetFunctionNameFromPropertyName(property, name);
}
}
Expect(Token::RBRACE, CHECK_OK);
......@@ -6522,5 +6527,43 @@ void ParserTraits::QueueDestructuringAssignmentForRewriting(Expression* expr) {
}
void ParserTraits::SetFunctionNameFromPropertyName(
ObjectLiteralProperty* property, const AstRawString* name) {
Expression* value = property->value();
if (!value->IsFunctionLiteral() && !value->IsClassLiteral()) return;
// TODO(adamk): Support computed names.
if (property->is_computed_name()) return;
DCHECK_NOT_NULL(name);
// Ignore "__proto__" as a name when it's being used to set the [[Prototype]]
// of an object literal.
if (property->kind() == ObjectLiteralProperty::PROTOTYPE) return;
if (value->IsFunctionLiteral()) {
auto function = value->AsFunctionLiteral();
if (function->is_anonymous()) {
if (property->kind() == ObjectLiteralProperty::GETTER) {
function->set_raw_name(parser_->ast_value_factory()->NewConsString(
parser_->ast_value_factory()->get_space_string(), name));
} else if (property->kind() == ObjectLiteralProperty::SETTER) {
function->set_raw_name(parser_->ast_value_factory()->NewConsString(
parser_->ast_value_factory()->set_space_string(), name));
} else {
function->set_raw_name(name);
DCHECK_EQ(ObjectLiteralProperty::COMPUTED, property->kind());
}
}
} else {
DCHECK(value->IsClassLiteral());
DCHECK_EQ(ObjectLiteralProperty::COMPUTED, property->kind());
auto class_literal = value->AsClassLiteral();
if (class_literal->raw_name() == nullptr) {
class_literal->set_raw_name(name);
}
}
}
} // namespace internal
} // namespace v8
......@@ -906,6 +906,9 @@ class ParserTraits {
V8_INLINE void QueueDestructuringAssignmentForRewriting(
Expression* assignment);
void SetFunctionNameFromPropertyName(ObjectLiteralProperty* property,
const AstRawString* name);
private:
Parser* parser_;
};
......
......@@ -1233,10 +1233,11 @@ PreParserExpression PreParser::ParseClassLiteral(
const bool is_static = false;
bool is_computed_name = false; // Classes do not care about computed
// property names here.
Identifier name;
ExpressionClassifier classifier;
ParsePropertyDefinition(&checker, in_class, has_extends, is_static,
&is_computed_name, &has_seen_constructor,
&classifier, CHECK_OK);
&classifier, &name, CHECK_OK);
ValidateExpression(&classifier, CHECK_OK);
}
......
......@@ -923,6 +923,9 @@ class PreParserTraits {
inline void QueueDestructuringAssignmentForRewriting(PreParserExpression) {}
void SetFunctionNameFromPropertyName(PreParserExpression,
PreParserIdentifier) {}
private:
PreParser* pre_parser_;
};
......
......@@ -33,3 +33,90 @@
assertEquals('x', x.name);
assertEquals('NamedClass', y.name);
})();
(function testObjectProperties() {
'use strict';
var obj = {
a: function() {},
b: () => {},
c() { },
get d() { },
set d(val) { },
x: function withName() { },
y: class { },
z: class ClassName { },
42: function() {},
4.2: function() {},
__proto__: function() {},
};
assertEquals('a', obj.a.name);
assertEquals('b', obj.b.name);
assertEquals('c', obj.c.name);
var dDescriptor = Object.getOwnPropertyDescriptor(obj, 'd');
assertEquals('get d', dDescriptor.get.name);
assertEquals('set d', dDescriptor.set.name);
assertEquals('withName', obj.x.name);
assertEquals('y', obj.y.name);
assertEquals('ClassName', obj.z.name);
assertEquals('42', obj[42].name);
assertEquals('4.2', obj[4.2].name);
assertEquals('', obj.__proto__.name);
})();
(function testClassProperties() {
'use strict';
class C {
a() { }
static b() { }
get c() { }
set c(val) { }
42() { }
static 43() { }
get 44() { }
set 44(val) { }
};
assertEquals('a', C.prototype.a.name);
assertEquals('b', C.b.name);
var descriptor = Object.getOwnPropertyDescriptor(C.prototype, 'c');
assertEquals('get c', descriptor.get.name);
assertEquals('set c', descriptor.set.name);
assertEquals('42', C.prototype[42].name);
assertEquals('43', C[43].name);
var descriptor = Object.getOwnPropertyDescriptor(C.prototype, '44');
assertEquals('get 44', descriptor.get.name);
assertEquals('set 44', descriptor.set.name);
})();
// TODO(adamk): Make computed property names work.
(function testComputedProperties() {
'use strict';
var a = 'a';
var sym1 = Symbol('1');
var sym2 = Symbol('2');
var obj = {
[a]: function() {},
[sym1]: function() {},
[sym2]: function withName() {},
};
// Should be 'a'
assertEquals('', obj[a].name);
// Should be '[1]'
assertEquals('', obj[sym1].name);
assertEquals('withName', obj[sym2].name);
class C {
[a]() { }
[sym1]() { }
static [sym2]() { }
}
// Should be 'a'
assertEquals('', C.prototype[a].name);
// Should be '[1]'
assertEquals('', C.prototype[sym1].name);
// Should be '[2]'
assertEquals('', C[sym2].name);
})();
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