Chromium Code Reviews| Index: src/parsing/parser.cc |
| diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc |
| index 4d4bc8fb89b802ba22c7ef34accb6f2c05e64e2b..9220c251f10d76bdfdde1cf3c7f730bcc016c01f 100644 |
| --- a/src/parsing/parser.cc |
| +++ b/src/parsing/parser.cc |
| @@ -163,7 +163,7 @@ Expression* Parser::CallClassFieldInitializer(Scope* scope, |
| } |
| Expression* Parser::RewriteSuperCall(Expression* super_call) { |
| - if (!allow_harmony_class_fields()) { |
| + if (!(allow_harmony_class_fields() || allow_harmony_private_class_fields())) { |
| return super_call; |
| } |
| // TODO(bakkot) The whole charade with the do expression can be replaced by a |
| @@ -584,6 +584,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; |
| @@ -3365,6 +3366,48 @@ DoExpression* Parser::ParseDoExpression(bool* ok) { |
| return expr; |
| } |
| +const AstRawString* ClassPrivateFieldVariableName( |
| + AstValueFactory* ast_value_factory, const AstRawString* raw_name) { |
| + std::string name(reinterpret_cast<const char*>(raw_name->raw_data()), |
| + raw_name->length()); // TODO(bakkot) check complicated names |
| + name = "#" + name; |
| + return ast_value_factory->GetOneByteString( |
| + name.c_str()); // TODO(bakkot) sometimes two-byte probably, though maybe |
| + // doesn't matter |
|
Dan Ehrenberg
2016/09/15 21:26:58
I'm looking forward to your fix here.
bakkot
2016/09/17 00:11:10
Done.
|
| +} |
| + |
| +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, |
| @@ -4244,6 +4287,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( |
| @@ -4415,6 +4459,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; |
| @@ -4429,12 +4477,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(); |
| @@ -4445,8 +4494,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()); |
| @@ -4463,7 +4543,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 = |
| @@ -4480,16 +4562,18 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name, |
| ->NewVariableDeclaration( // TODO(bakkot) DeclareLocal? not |
| // clear on the difference... |
| name_proxy, scope(), kNoSourcePosition); |
| - Variable* name_var = |
| + Variable* index_var = |
| Declare(name_declaration, DeclarationDescriptor::NORMAL, CONST, |
| kNeedsInitialization, ok, scope()); |
| DCHECK(ok); |
| if (!ok) return nullptr; |
| instance_field_initializers->Add(property->value(), zone()); |
| - property->set_value(factory()->NewVariableProxy(name_var)); |
| + property->set_value(factory()->NewVariableProxy(index_var)); |
| + properties->Add(property, zone()); |
| } |
| + } else { |
| + properties->Add(property, zone()); |
| } |
| - properties->Add(property, zone()); |
| } |
| DCHECK_NOT_NULL(fni_); |
| @@ -4500,7 +4584,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, |
| @@ -4519,6 +4604,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); |
| @@ -4534,7 +4692,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 |