node->set_base_id(ReserveIdRange(ClassLiteral::num_ids()));
if (node->extends()) Visit(node->extends());
if (node->constructor()) Visit(node->constructor());
+ if (node->class_variable_proxy()) {
+ VisitVariableProxy(node->class_variable_proxy());
+ }
for (int i = 0; i < node->properties()->length(); i++) {
VisitObjectLiteralProperty(node->properties()->at(i));
}
Handle<String> name() const { return raw_name_->string(); }
const AstRawString* raw_name() const { return raw_name_; }
+ Scope* scope() const { return scope_; }
+ VariableProxy* class_variable_proxy() const { return class_variable_proxy_; }
Expression* extends() const { return extends_; }
Expression* constructor() const { return constructor_; }
ZoneList<Property*>* properties() const { return properties_; }
int end_position() const { return end_position_; }
protected:
- ClassLiteral(Zone* zone, const AstRawString* name, Expression* extends,
+ ClassLiteral(Zone* zone, const AstRawString* name, Scope* scope,
+ VariableProxy* class_variable_proxy, Expression* extends,
Expression* constructor, ZoneList<Property*>* properties,
int start_position, int end_position)
: Expression(zone, start_position),
raw_name_(name),
+ scope_(scope),
+ class_variable_proxy_(class_variable_proxy),
extends_(extends),
constructor_(constructor),
properties_(properties),
private:
const AstRawString* raw_name_;
+ Scope* scope_;
+ VariableProxy* class_variable_proxy_;
Expression* extends_;
Expression* constructor_;
ZoneList<Property*>* properties_;
position);
}
- ClassLiteral* NewClassLiteral(const AstRawString* name, Expression* extends,
+ ClassLiteral* NewClassLiteral(const AstRawString* name, Scope* scope,
+ VariableProxy* proxy, Expression* extends,
Expression* constructor,
ZoneList<ObjectLiteral::Property*>* properties,
int start_position, int end_position) {
- return new (zone_) ClassLiteral(zone_, name, extends, constructor,
- properties, start_position, end_position);
+ return new (zone_)
+ ClassLiteral(zone_, name, scope, proxy, extends, constructor,
+ properties, start_position, end_position);
}
NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name,
NestedBlock nested_block(this, stmt);
SetStatementPosition(stmt);
- Scope* saved_scope = scope();
- // Push a block context when entering a block with block scoped variables.
- if (stmt->scope() == NULL) {
- PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
- } else {
- scope_ = stmt->scope();
- DCHECK(!scope_->is_module_scope());
- { Comment cmnt(masm_, "[ Extend block context");
- __ Push(scope_->GetScopeInfo());
- PushFunctionArgumentForContextAllocation();
- __ CallRuntime(Runtime::kPushBlockContext, 2);
-
- // Replace the context stored in the frame.
- StoreToFrameField(StandardFrameConstants::kContextOffset,
- context_register());
- PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
- }
- { Comment cmnt(masm_, "[ Declarations");
- VisitDeclarations(scope_->declarations());
- PrepareForBailoutForId(stmt->DeclsId(), NO_REGISTERS);
- }
- }
-
- VisitStatements(stmt->statements());
- scope_ = saved_scope;
- __ bind(nested_block.break_label());
-
- // Pop block context if necessary.
- if (stmt->scope() != NULL) {
- LoadContextField(context_register(), Context::PREVIOUS_INDEX);
- // Update local stack frame context field.
- StoreToFrameField(StandardFrameConstants::kContextOffset,
- context_register());
+ {
+ EnterBlockScopeIfNeeded block_scope_state(
+ this, stmt->scope(), stmt->EntryId(), stmt->DeclsId(), stmt->ExitId());
+ VisitStatements(stmt->statements());
+ __ bind(nested_block.break_label());
}
- PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
}
void FullCodeGenerator::VisitClassLiteral(ClassLiteral* lit) {
Comment cmnt(masm_, "[ ClassLiteral");
- if (lit->raw_name() != NULL) {
- __ Push(lit->name());
- } else {
- __ Push(isolate()->factory()->undefined_value());
- }
+ {
+ EnterBlockScopeIfNeeded block_scope_state(
+ this, lit->scope(), BailoutId::None(), BailoutId::None(),
+ BailoutId::None());
- if (lit->extends() != NULL) {
- VisitForStackValue(lit->extends());
- } else {
- __ Push(isolate()->factory()->the_hole_value());
- }
+ if (lit->raw_name() != NULL) {
+ __ Push(lit->name());
+ } else {
+ __ Push(isolate()->factory()->undefined_value());
+ }
- VisitForStackValue(lit->constructor());
+ if (lit->extends() != NULL) {
+ VisitForStackValue(lit->extends());
+ } else {
+ __ Push(isolate()->factory()->the_hole_value());
+ }
+
+ VisitForStackValue(lit->constructor());
- __ Push(script());
- __ Push(Smi::FromInt(lit->start_position()));
- __ Push(Smi::FromInt(lit->end_position()));
+ __ Push(script());
+ __ Push(Smi::FromInt(lit->start_position()));
+ __ Push(Smi::FromInt(lit->end_position()));
- __ CallRuntime(Runtime::kDefineClass, 6);
- EmitClassDefineProperties(lit);
+ __ CallRuntime(Runtime::kDefineClass, 6);
+ EmitClassDefineProperties(lit);
+
+ if (lit->scope() != NULL) {
+ DCHECK_NOT_NULL(lit->class_variable_proxy());
+ EmitVariableAssignment(lit->class_variable_proxy()->var(),
+ Token::INIT_CONST);
+ }
+ }
context()->Plug(result_register());
}
#endif // DEBUG
+FullCodeGenerator::EnterBlockScopeIfNeeded::EnterBlockScopeIfNeeded(
+ FullCodeGenerator* codegen, Scope* scope, BailoutId entry_id,
+ BailoutId declarations_id, BailoutId exit_id)
+ : codegen_(codegen), scope_(scope), exit_id_(exit_id) {
+ saved_scope_ = codegen_->scope();
+
+ if (scope == NULL) {
+ codegen_->PrepareForBailoutForId(entry_id, NO_REGISTERS);
+ } else {
+ codegen_->scope_ = scope;
+ {
+ Comment cmnt(masm(), "[ Extend block context");
+ __ Push(scope->GetScopeInfo());
+ codegen_->PushFunctionArgumentForContextAllocation();
+ __ CallRuntime(Runtime::kPushBlockContext, 2);
+
+ // Replace the context stored in the frame.
+ codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset,
+ codegen_->context_register());
+ codegen_->PrepareForBailoutForId(entry_id, NO_REGISTERS);
+ }
+ {
+ Comment cmnt(masm(), "[ Declarations");
+ codegen_->VisitDeclarations(scope->declarations());
+ codegen_->PrepareForBailoutForId(declarations_id, NO_REGISTERS);
+ }
+ }
+}
+
+
+FullCodeGenerator::EnterBlockScopeIfNeeded::~EnterBlockScopeIfNeeded() {
+ if (scope_ != NULL) {
+ codegen_->LoadContextField(codegen_->context_register(),
+ Context::PREVIOUS_INDEX);
+ // Update local stack frame context field.
+ codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset,
+ codegen_->context_register());
+ }
+ codegen_->PrepareForBailoutForId(exit_id_, NO_REGISTERS);
+ codegen_->scope_ = saved_scope_;
+}
+
+
#undef __
virtual bool IsEffect() const { return true; }
};
+ class EnterBlockScopeIfNeeded {
+ public:
+ EnterBlockScopeIfNeeded(FullCodeGenerator* codegen, Scope* scope,
+ BailoutId entry_id, BailoutId declarations_id,
+ BailoutId exit_id);
+ ~EnterBlockScopeIfNeeded();
+
+ private:
+ MacroAssembler* masm() const { return codegen_->masm(); }
+
+ FullCodeGenerator* codegen_;
+ Scope* scope_;
+ Scope* saved_scope_;
+ BailoutId exit_id_;
+ };
+
MacroAssembler* masm_;
CompilationInfo* info_;
Scope* scope_;
pos);
}
-Expression* ParserTraits::ClassExpression(
- const AstRawString* name, Expression* extends, Expression* constructor,
- ZoneList<ObjectLiteral::Property*>* properties, int start_position,
- int end_position, AstNodeFactory* factory) {
- return factory->NewClassLiteral(name, extends, constructor, properties,
- start_position, end_position);
-}
Expression* ParserTraits::DefaultConstructor(bool call_super, Scope* scope,
int pos, int end_pos) {
}
+ClassLiteral* ParserTraits::ParseClassLiteral(
+ const AstRawString* name, Scanner::Location class_name_location,
+ bool name_is_strict_reserved, int pos, bool* ok) {
+ return parser_->ParseClassLiteral(name, class_name_location,
+ name_is_strict_reserved, pos, ok);
+}
+
+
Parser::Parser(CompilationInfo* info, ParseInfo* parse_info)
: ParserBase<ParserTraits>(&scanner_, parse_info->stack_limit,
info->extension(), NULL, info->zone(), this),
bool is_strict_reserved = false;
const AstRawString* name =
ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
- Expression* value = ParseClassLiteral(name, scanner()->location(),
- is_strict_reserved, pos, CHECK_OK);
+ ClassLiteral* value = ParseClassLiteral(name, scanner()->location(),
+ is_strict_reserved, pos, CHECK_OK);
VariableProxy* proxy = NewUnresolved(name, LET, Interface::NewValue());
Declaration* declaration =
}
+ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
+ Scanner::Location class_name_location,
+ bool name_is_strict_reserved, int pos,
+ bool* ok) {
+ // All parts of a ClassDeclaration and ClassExpression are strict code.
+ if (name_is_strict_reserved) {
+ ReportMessageAt(class_name_location, "unexpected_strict_reserved");
+ *ok = false;
+ return NULL;
+ }
+ if (IsEvalOrArguments(name)) {
+ ReportMessageAt(class_name_location, "strict_eval_arguments");
+ *ok = false;
+ return NULL;
+ }
+
+ Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
+ BlockState block_state(&scope_, block_scope);
+ scope_->SetStrictMode(STRICT);
+ scope_->SetScopeName(name);
+
+ VariableProxy* proxy = NULL;
+ if (name != NULL) {
+ proxy = NewUnresolved(name, CONST, Interface::NewConst());
+ Declaration* declaration =
+ factory()->NewVariableDeclaration(proxy, CONST, block_scope, pos);
+ Declare(declaration, true, CHECK_OK);
+ }
+
+ Expression* extends = NULL;
+ if (Check(Token::EXTENDS)) {
+ block_scope->set_start_position(scanner()->location().end_pos);
+ extends = ParseLeftHandSideExpression(CHECK_OK);
+ } else {
+ block_scope->set_start_position(scanner()->location().end_pos);
+ }
+
+ ZoneList<ObjectLiteral::Property*>* properties = NewPropertyList(4, zone());
+ Expression* constructor = NULL;
+ bool has_seen_constructor = false;
+
+ Expect(Token::LBRACE, CHECK_OK);
+ while (peek() != Token::RBRACE) {
+ if (Check(Token::SEMICOLON)) continue;
+ if (fni_ != NULL) fni_->Enter();
+ const bool in_class = true;
+ const bool is_static = false;
+ ObjectLiteral::Property* property = ParsePropertyDefinition(
+ NULL, in_class, is_static, &has_seen_constructor, CHECK_OK);
+
+ if (has_seen_constructor && constructor == NULL) {
+ constructor = GetPropertyValue(property);
+ } else {
+ properties->Add(property, zone());
+ }
+
+ if (fni_ != NULL) {
+ fni_->Infer();
+ fni_->Leave();
+ }
+ }
+
+ Expect(Token::RBRACE, CHECK_OK);
+ int end_pos = scanner()->location().end_pos;
+
+ if (constructor == NULL) {
+ constructor =
+ DefaultConstructor(extends != NULL, block_scope, pos, end_pos);
+ }
+
+ block_scope->set_end_position(end_pos);
+ block_scope = block_scope->FinalizeBlockScope();
+
+ if (name != NULL) {
+ DCHECK_NOT_NULL(proxy);
+ DCHECK_NOT_NULL(block_scope);
+ proxy->var()->set_initializer_position(end_pos);
+ }
+
+ return factory()->NewClassLiteral(name, block_scope, proxy, extends,
+ constructor, properties, pos, end_pos);
+}
+
+
Expression* Parser::ParseV8Intrinsic(bool* ok) {
// CallRuntime ::
// '%' Identifier Arguments
int pos = RelocInfo::kNoPosition);
Expression* SuperReference(Scope* scope, AstNodeFactory* factory,
int pos = RelocInfo::kNoPosition);
- Expression* ClassExpression(const AstRawString* name, Expression* extends,
- Expression* constructor,
- ZoneList<ObjectLiteral::Property*>* properties,
- int start_position, int end_position,
- AstNodeFactory* factory);
-
Expression* DefaultConstructor(bool call_super, Scope* scope, int pos,
int end_pos);
Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner,
V8_INLINE ZoneList<Statement*>* ParseEagerFunctionBody(
const AstRawString* name, int pos, Variable* fvar,
Token::Value fvar_init_op, bool is_generator, bool* ok);
+
+ ClassLiteral* ParseClassLiteral(const AstRawString* name,
+ Scanner::Location class_name_location,
+ bool name_is_strict_reserved, int pos,
+ bool* ok);
+
V8_INLINE void CheckConflictingVarDeclarations(v8::internal::Scope* scope,
bool* ok);
int function_token_position, FunctionLiteral::FunctionType type,
FunctionLiteral::ArityRestriction arity_restriction, bool* ok);
+
+ ClassLiteral* ParseClassLiteral(const AstRawString* name,
+ Scanner::Location class_name_location,
+ bool name_is_strict_reserved, int pos,
+ bool* ok);
+
// Magical syntax support.
Expression* ParseV8Intrinsic(bool* ok);
}
+PreParserExpression PreParserTraits::ParseClassLiteral(
+ PreParserIdentifier name, Scanner::Location class_name_location,
+ bool name_is_strict_reserved, int pos, bool* ok) {
+ return pre_parser_->ParseClassLiteral(name, class_name_location,
+ name_is_strict_reserved, pos, ok);
+}
+
+
// Preparsing checks a JavaScript program and emits preparse-data that helps
// a later parsing to be faster.
// See preparser-data.h for the data.
}
+PreParserExpression PreParser::ParseClassLiteral(
+ PreParserIdentifier name, Scanner::Location class_name_location,
+ bool name_is_strict_reserved, int pos, bool* ok) {
+ // All parts of a ClassDeclaration and ClassExpression are strict code.
+ if (name_is_strict_reserved) {
+ ReportMessageAt(class_name_location, "unexpected_strict_reserved");
+ *ok = false;
+ return EmptyExpression();
+ }
+ if (IsEvalOrArguments(name)) {
+ ReportMessageAt(class_name_location, "strict_eval_arguments");
+ *ok = false;
+ return EmptyExpression();
+ }
+
+ PreParserScope scope = NewScope(scope_, BLOCK_SCOPE);
+ BlockState block_state(&scope_, &scope);
+ scope_->SetStrictMode(STRICT);
+ scope_->SetScopeName(name);
+
+ if (Check(Token::EXTENDS)) {
+ ParseLeftHandSideExpression(CHECK_OK);
+ }
+
+ bool has_seen_constructor = false;
+
+ Expect(Token::LBRACE, CHECK_OK);
+ while (peek() != Token::RBRACE) {
+ if (Check(Token::SEMICOLON)) continue;
+ const bool in_class = true;
+ const bool is_static = false;
+ ParsePropertyDefinition(NULL, in_class, is_static, &has_seen_constructor,
+ CHECK_OK);
+ }
+
+ Expect(Token::RBRACE, CHECK_OK);
+
+ return Expression::Default();
+}
+
+
PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) {
// CallRuntime ::
// '%' Identifier Arguments
bool* ok);
ExpressionT ParseArrowFunctionLiteral(int start_pos, ExpressionT params_ast,
bool* ok);
- ExpressionT ParseClassLiteral(IdentifierT name,
- Scanner::Location function_name_location,
- bool name_is_strict_reserved, int pos,
- bool* ok);
// Checks if the expression is a valid reference expression (e.g., on the
// left-hand side of assignments). Although ruled out by ECMA as early errors,
int position) {
return PreParserExpression::Default();
}
- PreParserExpression NewClassLiteral(PreParserIdentifier name,
- PreParserExpression extends,
- PreParserExpression constructor,
- PreParserExpressionList properties,
- int start_position, int end_position) {
- return PreParserExpression::Default();
+
+ // Return the object itself as AstVisitor and implement the needed
+ // dummy method right in this class.
+ PreParserFactory* visitor() { return this; }
+ int* ast_properties() {
+ static int dummy = 42;
+ return &dummy;
}
};
return PreParserExpression::Super();
}
- static PreParserExpression ClassExpression(
- PreParserIdentifier name, PreParserExpression extends,
- PreParserExpression constructor, PreParserExpressionList properties,
- int start_position, int end_position, PreParserFactory* factory) {
- return PreParserExpression::Default();
- }
-
static PreParserExpression DefaultConstructor(bool call_super,
PreParserScope* scope, int pos,
int end_pos) {
int function_token_position, FunctionLiteral::FunctionType type,
FunctionLiteral::ArityRestriction arity_restriction, bool* ok);
+ PreParserExpression ParseClassLiteral(PreParserIdentifier name,
+ Scanner::Location class_name_location,
+ bool name_is_strict_reserved, int pos,
+ bool* ok);
+
private:
PreParser* pre_parser_;
};
FunctionLiteral::ArityRestriction arity_restriction, bool* ok);
void ParseLazyFunctionLiteralBody(bool* ok);
+ PreParserExpression ParseClassLiteral(PreParserIdentifier name,
+ Scanner::Location class_name_location,
+ bool name_is_strict_reserved, int pos,
+ bool* ok);
+
bool CheckInOrOf(bool accept_OF);
};
}
-template <class Traits>
-typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseClassLiteral(
- IdentifierT name, Scanner::Location class_name_location,
- bool name_is_strict_reserved, int pos, bool* ok) {
- // All parts of a ClassDeclaration or a ClassExpression are strict code.
- if (name_is_strict_reserved) {
- ReportMessageAt(class_name_location, "unexpected_strict_reserved");
- *ok = false;
- return this->EmptyExpression();
- }
- if (this->IsEvalOrArguments(name)) {
- ReportMessageAt(class_name_location, "strict_eval_arguments");
- *ok = false;
- return this->EmptyExpression();
- }
-
- bool has_extends = false;
- ExpressionT extends = this->EmptyExpression();
- if (Check(Token::EXTENDS)) {
- typename Traits::Type::ScopePtr scope = this->NewScope(scope_, BLOCK_SCOPE);
- BlockState block_state(&scope_, Traits::Type::ptr_to_scope(scope));
- scope_->SetStrictMode(STRICT);
- extends = this->ParseLeftHandSideExpression(CHECK_OK);
- has_extends = true;
- }
-
- // TODO(arv): Implement scopes and name binding in class body only.
- typename Traits::Type::ScopePtr scope = this->NewScope(scope_, BLOCK_SCOPE);
- BlockState block_state(&scope_, Traits::Type::ptr_to_scope(scope));
- scope_->SetStrictMode(STRICT);
- scope_->SetScopeName(name);
-
- typename Traits::Type::PropertyList properties =
- this->NewPropertyList(4, zone_);
- ExpressionT constructor = this->EmptyExpression();
- bool has_seen_constructor = false;
-
- Expect(Token::LBRACE, CHECK_OK);
- while (peek() != Token::RBRACE) {
- if (Check(Token::SEMICOLON)) continue;
- if (fni_ != NULL) fni_->Enter();
- const bool in_class = true;
- const bool is_static = false;
- bool old_has_seen_constructor = has_seen_constructor;
- ObjectLiteralPropertyT property = this->ParsePropertyDefinition(
- NULL, in_class, is_static, &has_seen_constructor, CHECK_OK);
-
- if (has_seen_constructor != old_has_seen_constructor) {
- constructor = this->GetPropertyValue(property);
- } else {
- properties->Add(property, zone());
- }
-
- if (fni_ != NULL) {
- fni_->Infer();
- fni_->Leave();
- }
- }
-
- int end_pos = peek_position();
- Expect(Token::RBRACE, CHECK_OK);
-
- if (!has_seen_constructor) {
- constructor =
- this->DefaultConstructor(has_extends, scope_, pos, end_pos + 1);
- }
-
- return this->ClassExpression(name, extends, constructor, properties, pos,
- end_pos + 1, factory());
-}
-
-
template <typename Traits>
typename ParserBase<Traits>::ExpressionT
ParserBase<Traits>::CheckAndRewriteReferenceExpression(
})();
-/* TODO(arv): Implement
-(function TestNameBindingInConstructor() {
+(function TestNameBindingConst() {
+ assertThrows('class C { constructor() { C = 42; } }', SyntaxError);
+ assertThrows('(class C { constructor() { C = 42; } })', SyntaxError);
+ assertThrows('class C { m() { C = 42; } }', SyntaxError);
+ assertThrows('(class C { m() { C = 42; } })', SyntaxError);
+ assertThrows('class C { get x() { C = 42; } }', SyntaxError);
+ assertThrows('(class C { get x() { C = 42; } })', SyntaxError);
+ assertThrows('class C { set x(_) { C = 42; } }', SyntaxError);
+ assertThrows('(class C { set x(_) { C = 42; } })', SyntaxError);
+})();
+
+
+(function TestNameBinding() {
+ var C2;
class C {
constructor() {
- assertThrows(function() {
- C = 42;
- }, ReferenceError);
+ C2 = C;
+ }
+ m() {
+ C2 = C;
+ }
+ get x() {
+ C2 = C;
+ }
+ set x(_) {
+ C2 = C;
+ }
+ }
+ new C();
+ assertEquals(C, C2);
+
+ C2 = undefined;
+ new C().m();
+ assertEquals(C, C2);
+
+ C2 = undefined;
+ new C().x;
+ assertEquals(C, C2);
+
+ C2 = undefined;
+ new C().x = 1;
+ assertEquals(C, C2);
+})();
+
+
+(function TestNameBindingExpression() {
+ var C3;
+ var C = class C2 {
+ constructor() {
+ assertEquals(C2, C);
+ C3 = C2;
+ }
+ m() {
+ assertEquals(C2, C);
+ C3 = C2;
+ }
+ get x() {
+ assertEquals(C2, C);
+ C3 = C2;
+ }
+ set x(_) {
+ assertEquals(C2, C);
+ C3 = C2;
}
}
new C();
+ assertEquals(C, C3);
+
+ C3 = undefined;
+ new C().m();
+ assertEquals(C, C3);
+
+ C3 = undefined;
+ new C().x;
+ assertEquals(C, C3);
+
+ C3 = undefined;
+ new C().x = 1;
+ assertEquals(C, C3);
+})();
+
+
+(function TestNameBindingInExtendsExpression() {
+ assertThrows(function() {
+ class x extends x {}
+ }, ReferenceError);
+
+ assertThrows(function() {
+ (class x extends x {});
+ }, ReferenceError);
+
+ assertThrows(function() {
+ var x = (class x extends x {});
+ }, ReferenceError);
})();
-*/