Index: src/parsing/parser.cc |
diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc |
index 4d4bc8fb89b802ba22c7ef34accb6f2c05e64e2b..0b6666e6234d682dbb83f9235a69c16ecd7a9a5b 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,37 @@ 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 |
Dan Ehrenberg
2016/09/10 04:30:28
What's the issue here? Non-latin1 names?
bakkot
2016/09/13 01:05:38
Non-one-byte names, in particular, although it loo
|
+ name = "#" + name; |
Dan Ehrenberg
2016/09/10 04:30:28
We don't concatenate strings in the parser like th
bakkot
2016/09/13 01:05:38
I'm not sure it can be avoided; we need an AstRawS
|
+ return ast_value_factory->GetOneByteString( |
+ name.c_str()); // TODO(bakkot) sometimes two-byte probably, though maybe |
+ // doesn't matter |
Dan Ehrenberg
2016/09/10 04:30:28
Yeah, you can in fact have identifier characters w
bakkot
2016/09/13 01:05:38
The reason I think it might not matter is that the
|
+} |
+ |
+Property* Parser::ParsePrivateFieldReference(Expression* base, bool* ok) { |
+ // PrimaryExpression :: |
+ // PrivateName |
+ 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); |
+ |
+ Expression* prop = NewUnresolved(symbol_var_name); |
+ return factory()->NewProperty(call, prop, pos); |
Dan Ehrenberg
2016/09/10 04:30:28
Great logic here, ends up being very elegant, but
bakkot
2016/09/13 01:05:38
Added a TODO.
|
+} |
+ |
void Parser::ParseArrowFunctionFormalParameterList( |
ParserFormalParameters* parameters, Expression* expr, |
const Scanner::Location& params_loc, Scanner::Location* duplicate_loc, |
@@ -4244,6 +4276,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 +4448,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_name_vars = |
+ new (zone()) ZoneList<Variable*>(0, zone()); |
FunctionLiteral* constructor = nullptr; |
bool has_seen_constructor = false; |
Variable* static_initializer_var = nullptr; |
@@ -4429,12 +4466,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 +4483,31 @@ 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); |
+ Variable* symbol_var = scope()->DeclareLocal( |
+ symbol_var_name, CONST, kCreatedInitialized, |
+ Variable::NORMAL); // TODO(bakkot) flags, Declare vs DeclareLocal |
Dan Ehrenberg
2016/09/10 04:30:28
I'm confused, where's the actual declaration AST n
bakkot
2016/09/13 01:05:38
Switched to that. This has the side effect of maki
|
+ private_field_symbol_vars->Add(symbol_var, zone()); |
+ |
+ const AstRawString* name_var_name = ClassFieldVariableName( |
+ true, ast_value_factory(), instance_field_initializers->length()); |
+ Variable* name_var = scope()->DeclareLocal( |
+ name_var_name, CONST, kCreatedInitialized, |
+ Variable::NORMAL); // TODO(bakkot) flags, Declare vs DeclareLocal |
+ private_field_name_vars->Add(name_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 +4524,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 = |
@@ -4487,9 +4550,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_); |
@@ -4500,7 +4565,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 +4585,31 @@ Expression* Parser::ParseClassLiteral(const AstRawString* name, |
proxy->var()->set_initializer_position(end_pos); |
} |
+ DCHECK(private_field_symbol_vars->length() == |
+ private_field_name_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) { |
Dan Ehrenberg
2016/09/10 04:30:28
A comment explaining the desugaring you're doing?
bakkot
2016/09/13 01:05:38
Done.
|
+ VariableProxy* symbol_var_proxy = |
+ factory()->NewVariableProxy(private_field_symbol_vars->at(i)); |
+ VariableProxy* name_var_proxy = |
+ factory()->NewVariableProxy(private_field_name_vars->at(i)); |
+ |
+ ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone()); |
+ args->Add(factory()->NewUndefinedLiteral(kNoSourcePosition), |
+ zone()); // TODO(bakkot) name |
+ Expression* name = |
+ factory()->NewCallRuntime(Runtime::kCreatePrivateSymbol, args, pos); |
+ |
+ Assignment* inner_assignment = factory()->NewAssignment( |
+ Token::INIT, name_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 +4625,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 |