Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Unified Diff: src/parsing/parser.cc

Issue 2329703002: Private fields
Patch Set: some comments Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/parsing/parser.h ('k') | src/parsing/parser-base.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/parsing/parser.cc
diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc
index 8d1cac2bdd44e35b8057b23559284cabc83cb1e2..8bcd592cf5bdf563c88d295c7b17055002905e71 100644
--- a/src/parsing/parser.cc
+++ b/src/parsing/parser.cc
@@ -170,7 +170,7 @@ Expression* Parser::CallClassFieldInitializer(Scope* scope,
Expression* Parser::RewriteSuperCall(Expression* super_call) {
// TODO(bakkot) find a way to avoid this for classes without fields.
- if (!allow_harmony_class_fields()) {
+ if (!(allow_harmony_class_fields() || allow_harmony_private_class_fields())) {
return super_call;
}
// This turns a super call `super()` into a do expression of the form
@@ -596,6 +596,7 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_restrictive_generators(FLAG_harmony_restrictive_generators);
set_allow_harmony_trailing_commas(FLAG_harmony_trailing_commas);
set_allow_harmony_class_fields(FLAG_harmony_class_fields);
+ set_allow_harmony_private_class_fields(FLAG_harmony_private_class_fields);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
use_counts_[feature] = 0;
@@ -3377,6 +3378,45 @@ DoExpression* Parser::ParseDoExpression(bool* ok) {
return expr;
}
+const AstRawString* ClassPrivateFieldVariableName(
+ AstValueFactory* ast_value_factory, const AstRawString* raw_name) {
+ // Prepends '#'
+ return ast_value_factory->ConcatStrings(
+ ast_value_factory->number_sign_string(), raw_name);
+}
+
+Property* Parser::ParsePrivateFieldReference(Expression* base, bool* ok) {
+ // PrimaryExpression ::
+ // PrivateName
+
+ // This desugars a private field reference like `#a` or `obj.#a` into
+ // %ThrowIfMissingPrivateField(obj, #a)[#a]
+ // Here #a is a variable which will resolve to a private symbol when inside of
+ // a class which defines a private field named #a (otherwise attempting to
+ // resolve it will throw a reference error).
+
+ int pos = peek_position();
+
+ Expect(Token::HASH, CHECK_OK);
+ const AstRawString* property_name =
+ ParseIdentifierName(CHECK_OK); // TODO(bakkot) more complex names
+ const AstRawString* symbol_var_name =
+ ClassPrivateFieldVariableName(ast_value_factory(), property_name);
+
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
+ args->Add(base, zone());
+ args->Add(NewUnresolved(symbol_var_name), zone());
+ Expression* call = factory()->NewCallRuntime(
+ Runtime::kThrowIfMissingPrivateField, args, pos);
+ // TODO(bakkot) ideally we'd avoid this runtime function, either by adding a
+ // flag to the load/store IC or by adding a code stub for it. Currently it
+ // means that every private field access requires this runtime call, which is
+ // a slowdown we'd prefer to avoid.
+
+ Expression* prop = NewUnresolved(symbol_var_name);
+ return factory()->NewProperty(call, prop, pos);
+}
+
void Parser::ParseArrowFunctionFormalParameterList(
ParserFormalParameters* parameters, Expression* expr,
const Scanner::Location& params_loc, Scanner::Location* duplicate_loc,
@@ -4256,6 +4296,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
SET_ALLOW(harmony_async_await);
SET_ALLOW(harmony_trailing_commas);
SET_ALLOW(harmony_class_fields);
+ SET_ALLOW(harmony_private_class_fields);
#undef SET_ALLOW
}
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
@@ -4441,6 +4482,10 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name,
ZoneList<ClassLiteral::Property*>* properties = NewClassPropertyList(4);
ZoneList<Expression*>* instance_field_initializers =
new (zone()) ZoneList<Expression*>(0, zone());
+ ZoneList<Variable*>* private_field_symbol_vars =
+ new (zone()) ZoneList<Variable*>(0, zone());
+ ZoneList<Variable*>* private_field_index_vars =
+ new (zone()) ZoneList<Variable*>(0, zone());
FunctionLiteral* constructor = nullptr;
bool has_seen_constructor = false;
Variable* static_initializer_var = nullptr;
@@ -4455,12 +4500,13 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name,
while (peek() != Token::RBRACE) {
if (Check(Token::SEMICOLON)) continue;
FuncNameInferrer::State fni_state(fni_);
+ const AstRawString* private_name = nullptr;
bool is_computed_name = false; // Classes do not care about computed
// property names here.
ExpressionClassifier property_classifier(this);
- ClassLiteral::Property* property =
- ParseClassPropertyDefinition(&checker, has_extends, &is_computed_name,
- &has_seen_constructor, CHECK_OK);
+ ClassLiteral::Property* property = ParseClassPropertyDefinition(
+ &checker, has_extends, &private_name, &is_computed_name,
+ &has_seen_constructor, CHECK_OK);
RewriteNonPattern(CHECK_OK);
impl()->AccumulateFormalParameterContainmentErrors();
@@ -4471,8 +4517,39 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name,
name != nullptr ? name : ast_value_factory()->empty_string());
} else {
if (property->kind() == ClassLiteralProperty::FIELD) {
- DCHECK(allow_harmony_class_fields());
- if (property->is_static()) {
+ if (private_name != nullptr) {
+ DCHECK(allow_harmony_private_class_fields());
+ DCHECK(property->key() == nullptr);
+ DCHECK(!property->is_static());
+
+ const AstRawString* symbol_var_name =
+ ClassPrivateFieldVariableName(ast_value_factory(), private_name);
+ VariableProxy* symbol_var_proxy = NewUnresolved(symbol_var_name);
+ Declaration* symbol_var_declaration =
+ factory()->NewVariableDeclaration(symbol_var_proxy, scope(),
+ kNoSourcePosition);
+ Variable* symbol_var =
+ Declare(symbol_var_declaration, DeclarationDescriptor::NORMAL,
+ CONST, kCreatedInitialized, ok, scope());
+ private_field_symbol_vars->Add(symbol_var, zone());
+
+ const AstRawString* index_var_name = ClassFieldVariableName(
+ true, ast_value_factory(), instance_field_initializers->length());
+ VariableProxy* index_var_proxy = NewUnresolved(index_var_name);
+ Declaration* index_var_declaration =
+ factory()->NewVariableDeclaration(index_var_proxy, scope(),
+ kNoSourcePosition);
+ Variable* index_var =
+ Declare(index_var_declaration, DeclarationDescriptor::NORMAL,
+ CONST, kCreatedInitialized, ok, scope());
+ private_field_index_vars->Add(index_var, zone());
+
+ instance_field_initializers->Add(property->value(), zone());
+
+ // Does not add to properties because no action is required from the
+ // backend during class definition.
+ } else if (property->is_static()) {
+ DCHECK(allow_harmony_class_fields());
if (static_initializer_var == nullptr) {
static_initializer_var =
NewTemporary(ast_value_factory()->empty_string());
@@ -4489,7 +4566,9 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name,
Expression* call = factory()->NewCallRuntime(Runtime::kInlineCall,
args, kNoSourcePosition);
property->set_value(call);
+ properties->Add(property, zone());
} else {
+ DCHECK(allow_harmony_class_fields());
// if (is_computed_name) { // TODO(bakkot) figure out why this is
// necessary for non-computed names in full-codegen
ZoneList<Expression*>* to_name_args =
@@ -4510,9 +4589,11 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name,
if (!ok) return nullptr;
instance_field_initializers->Add(property->value(), zone());
property->set_value(factory()->NewVariableProxy(name_var));
+ properties->Add(property, zone());
}
+ } else {
+ properties->Add(property, zone());
}
- properties->Add(property, zone());
}
DCHECK_NOT_NULL(fni_);
@@ -4523,7 +4604,8 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name,
int end_pos = scanner()->location().end_pos;
bool has_instance_fields = instance_field_initializers->length() > 0;
- DCHECK(!has_instance_fields || allow_harmony_class_fields());
+ DCHECK(!has_instance_fields || allow_harmony_class_fields() ||
+ allow_harmony_private_class_fields());
bool has_default_constructor = constructor == nullptr;
if (has_default_constructor) {
constructor = DefaultConstructor(name, has_extends, has_instance_fields,
@@ -4542,6 +4624,79 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name,
proxy->var()->set_initializer_position(end_pos);
}
+ // The parser creates two variables for every private field, both of which
+ // store the private symbol which serves as the key for that field. One of
+ // them, 'symbol_var', is used when referencing private fields with '#a' in
+ // normal code. The other, 'index_var', is used when initializing the field.
+ // The later variable is necessary because the synthetic field initialization
+ // function has available to it *only* the number of fields in the class.
+ // The desugaring looks something like this:
+ //
+ // class C {
+ // #foo = 0;
+ // baz;
+ // m(){
+ // this.#bar = 'a';
+ // #foo = 1;
+ // }
+ // #bar;
+ // }
+ // -- becomes --
+ // do {
+ // class C {
+ // m(){
+ // %ThrowIfMissingPrivateField(this, .bar-symbol)[.bar-symbol] = 'a';
+ // %ThrowIfMissingPrivateField(this, .foo-symbol)[.foo-symbol] = 1;
+ // }
+ // constructor(){
+ // .initialize.call(this);
+ // }
+ // }
+ //
+ // let .foo-symbol, .name-0, .init-0;
+ // .foo-symbol = .name-0 = PrivateSymbol();
+ // .init-0 = ()=>0;
+ //
+ // let .name-1, .init-1;
+ // // .name-1 is given value 'baz' by the backends during class definition
+ // .init-1 = ()=>undefined;
+ //
+ // let .bar-symbol, .name-2, .init-2;
+ // .bar-symbol = .name-2 = PrivateSymbol();
+ // .init-2 = ()=>undefined;
+ //
+ // .initialize() {
+ // this[.name-0] = .init-0(); // Actually this is defineOwnProperty not =
+ // this[.name-1] = .init-1();
+ // this[.name-2] = .init-2();
+ // }
+ //
+ // C;
+ // }
+ DCHECK(private_field_symbol_vars->length() ==
+ private_field_index_vars->length());
+ DCHECK(allow_harmony_private_class_fields() ||
+ private_field_symbol_vars->length() == 0);
+ for (int i = 0; i < private_field_symbol_vars->length(); ++i) {
+ VariableProxy* symbol_var_proxy =
+ factory()->NewVariableProxy(private_field_symbol_vars->at(i));
+ VariableProxy* index_var_proxy =
+ factory()->NewVariableProxy(private_field_index_vars->at(i));
+
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
+ args->Add(factory()->NewUndefinedLiteral(kNoSourcePosition), zone());
+ Expression* name =
+ factory()->NewCallRuntime(Runtime::kCreatePrivateSymbol, args, pos);
+
+ Assignment* inner_assignment = factory()->NewAssignment(
+ Token::INIT, index_var_proxy, name, kNoSourcePosition);
+ Assignment* outer_assignment = factory()->NewAssignment(
+ Token::INIT, symbol_var_proxy, inner_assignment, kNoSourcePosition);
+ do_block->statements()->Add(
+ factory()->NewExpressionStatement(outer_assignment, kNoSourcePosition),
+ zone());
+ }
+
ClassLiteral* class_literal = factory()->NewClassLiteral(
proxy, extends, constructor, properties, pos, end_pos);
@@ -4557,7 +4712,7 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name,
class_literal, kNoSourcePosition),
pos),
zone());
- if (allow_harmony_class_fields() &&
+ if ((allow_harmony_class_fields() || allow_harmony_private_class_fields()) &&
(has_instance_fields ||
(extends != nullptr && !has_default_constructor))) {
// Default constructors for derived classes without fields will not try to
« no previous file with comments | « src/parsing/parser.h ('k') | src/parsing/parser-base.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698