| Index: src/parsing/parser.cc
|
| diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc
|
| index 4d4bc8fb89b802ba22c7ef34accb6f2c05e64e2b..7e5157383cdce22839138f61cc50df2950ebc90b 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
|
| +}
|
| +
|
| +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(
|
| @@ -4329,7 +4373,7 @@ FunctionLiteral* Parser::SynthesizeClassFieldInitializer(int count) {
|
| zone());
|
| body->Add(factory()->NewExpressionStatement(
|
| factory()->NewCallRuntime(
|
| - Runtime::kDefineDataPropertyInLiteral,
|
| + Runtime::kDefineDataProperty,
|
| define_property_args, // TODO(bakkot) verify that this is
|
| // the same as object_define_property
|
| kNoSourcePosition),
|
| @@ -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
|
|
|