From 65aa17b9c30d4400c9fa7468b594dfdc0e46a1e6 Mon Sep 17 00:00:00 2001 From: dslomov Date: Thu, 27 Nov 2014 20:08:48 -0800 Subject: [PATCH] harmony-classes: Implement 'super(...)' call syntactic restriction. R=rossberg@chromium.org,arv@chromium.org BUG=v8:3330 LOG=N Review URL: https://codereview.chromium.org/766663003 Cr-Commit-Position: refs/heads/master@{#25555} --- BUILD.gn | 2 + src/ast-this-access-visitor.cc | 232 ++++++++++++++++++++++++++++++++++++++++ src/ast-this-access-visitor.h | 34 ++++++ src/ast.cc | 11 +- src/ast.h | 5 +- src/compiler.cc | 78 +++++++++++++- src/messages.js | 2 + src/objects-inl.h | 5 +- src/objects.h | 12 ++- src/parser.cc | 6 +- src/preparser.h | 12 ++- src/scopes.cc | 31 ++++-- src/scopes.h | 34 ++++-- test/cctest/test-parsing.cc | 46 ++++---- test/mjsunit/harmony/classes.js | 72 +++++++++++++ test/mjsunit/harmony/super.js | 58 ++++++++++ tools/gyp/v8.gyp | 2 + 17 files changed, 591 insertions(+), 51 deletions(-) create mode 100644 src/ast-this-access-visitor.cc create mode 100644 src/ast-this-access-visitor.h diff --git a/BUILD.gn b/BUILD.gn index 26f4a46..0c7fae6 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -429,6 +429,8 @@ source_set("v8_base") { "src/assert-scope.cc", "src/ast-numbering.cc", "src/ast-numbering.h", + "src/ast-this-access-visitor.cc", + "src/ast-this-access-visitor.h", "src/ast-value-factory.cc", "src/ast-value-factory.h", "src/ast.cc", diff --git a/src/ast-this-access-visitor.cc b/src/ast-this-access-visitor.cc new file mode 100644 index 0000000..e66f961 --- /dev/null +++ b/src/ast-this-access-visitor.cc @@ -0,0 +1,232 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/ast-this-access-visitor.h" +#include "src/parser.h" + +namespace v8 { +namespace internal { + +typedef class AstThisAccessVisitor ATAV; // for code shortitude. + +ATAV::AstThisAccessVisitor(Zone* zone) : uses_this_(false) { + InitializeAstVisitor(zone); +} + + +void ATAV::VisitVariableProxy(VariableProxy* proxy) { + if (proxy->is_this()) { + uses_this_ = true; + } +} + + +// --------------------------------------------------------------------------- +// -- Leaf nodes ------------------------------------------------------------- +// --------------------------------------------------------------------------- + +void ATAV::VisitVariableDeclaration(VariableDeclaration* leaf) {} +void ATAV::VisitFunctionDeclaration(FunctionDeclaration* leaf) {} +void ATAV::VisitModuleDeclaration(ModuleDeclaration* leaf) {} +void ATAV::VisitImportDeclaration(ImportDeclaration* leaf) {} +void ATAV::VisitExportDeclaration(ExportDeclaration* leaf) {} +void ATAV::VisitModuleVariable(ModuleVariable* leaf) {} +void ATAV::VisitModulePath(ModulePath* leaf) {} +void ATAV::VisitModuleUrl(ModuleUrl* leaf) {} +void ATAV::VisitEmptyStatement(EmptyStatement* leaf) {} +void ATAV::VisitContinueStatement(ContinueStatement* leaf) {} +void ATAV::VisitBreakStatement(BreakStatement* leaf) {} +void ATAV::VisitDebuggerStatement(DebuggerStatement* leaf) {} +void ATAV::VisitFunctionLiteral(FunctionLiteral* leaf) {} +void ATAV::VisitNativeFunctionLiteral(NativeFunctionLiteral* leaf) {} +void ATAV::VisitLiteral(Literal* leaf) {} +void ATAV::VisitRegExpLiteral(RegExpLiteral* leaf) {} +void ATAV::VisitThisFunction(ThisFunction* leaf) {} +void ATAV::VisitSuperReference(SuperReference* leaf) {} + + +// --------------------------------------------------------------------------- +// -- Pass-through nodes------------------------------------------------------ +// --------------------------------------------------------------------------- +void ATAV::VisitModuleLiteral(ModuleLiteral* e) { Visit(e->body()); } + + +void ATAV::VisitBlock(Block* stmt) { VisitStatements(stmt->statements()); } + + +void ATAV::VisitExpressionStatement(ExpressionStatement* stmt) { + Visit(stmt->expression()); +} + + +void ATAV::VisitIfStatement(IfStatement* stmt) { + Visit(stmt->condition()); + Visit(stmt->then_statement()); + Visit(stmt->else_statement()); +} + + +void ATAV::VisitReturnStatement(ReturnStatement* stmt) { + Visit(stmt->expression()); +} + + +void ATAV::VisitWithStatement(WithStatement* stmt) { + Visit(stmt->expression()); + Visit(stmt->statement()); +} + + +void ATAV::VisitSwitchStatement(SwitchStatement* stmt) { + Visit(stmt->tag()); + ZoneList* clauses = stmt->cases(); + for (int i = 0; i < clauses->length(); i++) { + Visit(clauses->at(i)); + } +} + + +void ATAV::VisitTryFinallyStatement(TryFinallyStatement* stmt) { + Visit(stmt->try_block()); + Visit(stmt->finally_block()); +} + + +void ATAV::VisitClassLiteral(ClassLiteral* e) { + VisitIfNotNull(e->extends()); + VisitIfNotNull(e->constructor()); + ZoneList* properties = e->properties(); + for (int i = 0; i < properties->length(); i++) { + Visit(properties->at(i)->value()); + } +} + + +void ATAV::VisitConditional(Conditional* e) { + Visit(e->condition()); + Visit(e->then_expression()); + Visit(e->else_expression()); +} + + +void ATAV::VisitObjectLiteral(ObjectLiteral* e) { + ZoneList* properties = e->properties(); + for (int i = 0; i < properties->length(); i++) { + Visit(properties->at(i)->value()); + } +} + + +void ATAV::VisitArrayLiteral(ArrayLiteral* e) { VisitExpressions(e->values()); } + + +void ATAV::VisitYield(Yield* stmt) { + Visit(stmt->generator_object()); + Visit(stmt->expression()); +} + + +void ATAV::VisitThrow(Throw* stmt) { Visit(stmt->exception()); } + + +void ATAV::VisitProperty(Property* e) { + Visit(e->obj()); + Visit(e->key()); +} + + +void ATAV::VisitCall(Call* e) { + Visit(e->expression()); + VisitExpressions(e->arguments()); +} + + +void ATAV::VisitCallNew(CallNew* e) { + Visit(e->expression()); + VisitExpressions(e->arguments()); +} + + +void ATAV::VisitCallRuntime(CallRuntime* e) { + VisitExpressions(e->arguments()); +} + + +void ATAV::VisitUnaryOperation(UnaryOperation* e) { Visit(e->expression()); } + + +void ATAV::VisitBinaryOperation(BinaryOperation* e) { + Visit(e->left()); + Visit(e->right()); +} + + +void ATAV::VisitCompareOperation(CompareOperation* e) { + Visit(e->left()); + Visit(e->right()); +} + + +void ATAV::VisitCaseClause(CaseClause* cc) { + if (!cc->is_default()) Visit(cc->label()); + VisitStatements(cc->statements()); +} + + +void ATAV::VisitModuleStatement(ModuleStatement* stmt) { Visit(stmt->body()); } + + +void ATAV::VisitTryCatchStatement(TryCatchStatement* stmt) { + Visit(stmt->try_block()); + Visit(stmt->catch_block()); +} + + +void ATAV::VisitDoWhileStatement(DoWhileStatement* loop) { + Visit(loop->body()); + Visit(loop->cond()); +} + + +void ATAV::VisitWhileStatement(WhileStatement* loop) { + Visit(loop->cond()); + Visit(loop->body()); +} + + +void ATAV::VisitForStatement(ForStatement* loop) { + VisitIfNotNull(loop->init()); + VisitIfNotNull(loop->cond()); + Visit(loop->body()); + VisitIfNotNull(loop->next()); +} + + +void ATAV::VisitForInStatement(ForInStatement* loop) { + Visit(loop->each()); + Visit(loop->subject()); + Visit(loop->body()); +} + + +void ATAV::VisitForOfStatement(ForOfStatement* loop) { + Visit(loop->each()); + Visit(loop->subject()); + Visit(loop->body()); +} + + +void ATAV::VisitAssignment(Assignment* stmt) { + Expression* l = stmt->target(); + Visit(l); + Visit(stmt->value()); +} + + +void ATAV::VisitCountOperation(CountOperation* e) { + Expression* l = e->expression(); + Visit(l); +} +} +} // namespace v8::internal diff --git a/src/ast-this-access-visitor.h b/src/ast-this-access-visitor.h new file mode 100644 index 0000000..3317922 --- /dev/null +++ b/src/ast-this-access-visitor.h @@ -0,0 +1,34 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_AST_THIS_ACCESS_VISITOR_H_ +#define V8_AST_THIS_ACCESS_VISITOR_H_ +#include "src/ast.h" + +namespace v8 { +namespace internal { + +class AstThisAccessVisitor : public AstVisitor { + public: + explicit AstThisAccessVisitor(Zone* zone); + + bool UsesThis() { return uses_this_; } + +#define DECLARE_VISIT(type) virtual void Visit##type(type* node); + AST_NODE_LIST(DECLARE_VISIT) +#undef DECLARE_VISIT + + private: + bool uses_this_; + + void VisitIfNotNull(AstNode* node) { + if (node != NULL) Visit(node); + } + + DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); + DISALLOW_COPY_AND_ASSIGN(AstThisAccessVisitor); +}; +} +} // namespace v8::intrenal +#endif // V8_AST_THIS_ACCESS_VISITOR_H_ diff --git a/src/ast.cc b/src/ast.cc index 874f984..35347ce 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -151,9 +151,16 @@ StrictMode FunctionLiteral::strict_mode() const { } -bool FunctionLiteral::uses_super() const { +bool FunctionLiteral::uses_super_property() const { DCHECK_NOT_NULL(scope()); - return scope()->uses_super() || scope()->inner_uses_super(); + return scope()->uses_super_property() || scope()->inner_uses_super_property(); +} + + +bool FunctionLiteral::uses_super_constructor_call() const { + DCHECK_NOT_NULL(scope()); + return scope()->uses_super_constructor_call() || + scope()->inner_uses_super_constructor_call(); } diff --git a/src/ast.h b/src/ast.h index c9592e6..d57aaa8 100644 --- a/src/ast.h +++ b/src/ast.h @@ -2513,11 +2513,12 @@ class FunctionLiteral FINAL : public Expression { bool is_expression() const { return IsExpression::decode(bitfield_); } bool is_anonymous() const { return IsAnonymous::decode(bitfield_); } StrictMode strict_mode() const; - bool uses_super() const; + bool uses_super_property() const; + bool uses_super_constructor_call() const; static bool NeedsHomeObject(Expression* literal) { return literal != NULL && literal->IsFunctionLiteral() && - literal->AsFunctionLiteral()->uses_super(); + literal->AsFunctionLiteral()->uses_super_property(); } int materialized_literal_count() { return materialized_literal_count_; } diff --git a/src/compiler.cc b/src/compiler.cc index a3bde5d..546ac88 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -7,6 +7,7 @@ #include "src/compiler.h" #include "src/ast-numbering.h" +#include "src/ast-this-access-visitor.h" #include "src/bootstrapper.h" #include "src/codegen.h" #include "src/compilation-cache.h" @@ -20,6 +21,7 @@ #include "src/isolate-inl.h" #include "src/lithium.h" #include "src/liveedit.h" +#include "src/messages.h" #include "src/parser.h" #include "src/rewriter.h" #include "src/runtime-profiler.h" @@ -615,7 +617,9 @@ static void SetFunctionInfo(Handle function_info, MaybeDisableOptimization(function_info, lit->dont_optimize_reason()); function_info->set_dont_cache(lit->flags()->Contains(kDontCache)); function_info->set_kind(lit->kind()); - function_info->set_uses_super(lit->uses_super()); + function_info->set_uses_super_property(lit->uses_super_property()); + function_info->set_uses_super_constructor_call( + lit->uses_super_constructor_call()); function_info->set_asm_function(lit->scope()->asm_function()); } @@ -758,12 +762,84 @@ static bool Renumber(CompilationInfo* info) { } +static void ThrowSuperConstructorCheckError(CompilationInfo* info) { + MaybeHandle obj = info->isolate()->factory()->NewTypeError( + "super_constructor_call", HandleVector(nullptr, 0)); + Handle exception; + if (!obj.ToHandle(&exception)) return; + + FunctionLiteral* lit = info->function(); + MessageLocation location(info->script(), lit->start_position(), + lit->end_position()); + USE(info->isolate()->Throw(*exception, &location)); +} + + +static bool CheckSuperConstructorCall(CompilationInfo* info) { + FunctionLiteral* function = info->function(); + if (!function->uses_super_constructor_call()) return true; + + if (function->is_default_constructor()) return true; + + ZoneList* body = function->body(); + CHECK(body->length() > 0); + + int super_call_index = 0; + // Allow 'use strict' and similiar and empty statements. + while (true) { + CHECK(super_call_index < body->length()); // We know there is a super call. + Statement* stmt = body->at(super_call_index); + if (stmt->IsExpressionStatement() && + stmt->AsExpressionStatement()->expression()->IsLiteral()) { + super_call_index++; + continue; + } + if (stmt->IsEmptyStatement()) { + super_call_index++; + continue; + } + break; + } + + ExpressionStatement* exprStm = + body->at(super_call_index)->AsExpressionStatement(); + if (exprStm == nullptr) { + ThrowSuperConstructorCheckError(info); + return false; + } + Call* callExpr = exprStm->expression()->AsCall(); + if (callExpr == nullptr) { + ThrowSuperConstructorCheckError(info); + return false; + } + + if (!callExpr->expression()->IsSuperReference()) { + ThrowSuperConstructorCheckError(info); + return false; + } + + ZoneList* arguments = callExpr->arguments(); + + AstThisAccessVisitor this_access_visitor(info->zone()); + this_access_visitor.VisitExpressions(arguments); + + if (this_access_visitor.HasStackOverflow()) return false; + if (this_access_visitor.UsesThis()) { + ThrowSuperConstructorCheckError(info); + return false; + } + + return true; +} + + bool Compiler::Analyze(CompilationInfo* info) { DCHECK(info->function() != NULL); if (!Rewriter::Rewrite(info)) return false; if (!Scope::Analyze(info)) return false; if (!Renumber(info)) return false; DCHECK(info->scope() != NULL); + if (!CheckSuperConstructorCall(info)) return false; return true; } diff --git a/src/messages.js b/src/messages.js index 142e47b..78fac32 100644 --- a/src/messages.js +++ b/src/messages.js @@ -182,6 +182,8 @@ var kMessages = { prototype_parent_not_an_object: ["Class extends value does not have valid prototype property ", "%0"], duplicate_constructor: ["A class may only have one constructor"], sloppy_lexical: ["Block-scoped declarations (let, const, function, class) not yet supported outside strict mode"], + super_constructor_call: ["'super(...)' constructor call is currently only supported if it is the first statement of a constructor and its arguments do not access 'this'"], + super_constructor_call: ["A 'super' constructor call may only appear as the first statement of a function, and its arguments may not access 'this'. Other forms are not yet supported."] }; diff --git a/src/objects-inl.h b/src/objects-inl.h index 98d96a8..34c3f2d 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -5792,7 +5792,10 @@ void SharedFunctionInfo::set_kind(FunctionKind kind) { } -BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, uses_super, kUsesSuper) +BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, uses_super_property, + kUsesSuperProperty) +BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, uses_super_constructor_call, + kUsesSuperConstructorCall) BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, native, kNative) BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, inline_builtin, kInlineBuiltin) diff --git a/src/objects.h b/src/objects.h index ea84ebf..c284a49 100644 --- a/src/objects.h +++ b/src/objects.h @@ -6831,9 +6831,12 @@ class SharedFunctionInfo: public HeapObject { // False if the function definitely does not allocate an arguments object. DECL_BOOLEAN_ACCESSORS(uses_arguments) - // Indicates that this function uses super. This is needed to set up the - // [[HomeObject]] on the function instance. - DECL_BOOLEAN_ACCESSORS(uses_super) + // Indicates that this function uses a super property. + // This is needed to set up the [[HomeObject]] on the function instance. + DECL_BOOLEAN_ACCESSORS(uses_super_property) + + // Indicates that this function uses the super constructor. + DECL_BOOLEAN_ACCESSORS(uses_super_constructor_call) // True if the function has any duplicated parameter names. DECL_BOOLEAN_ACCESSORS(has_duplicate_parameters) @@ -7112,7 +7115,8 @@ class SharedFunctionInfo: public HeapObject { kOptimizationDisabled, kStrictModeFunction, kUsesArguments, - kUsesSuper, + kUsesSuperProperty, + kUsesSuperConstructorCall, kHasDuplicateParameters, kNative, kInlineBuiltin, diff --git a/src/parser.cc b/src/parser.cc index 712fdd4..22c0f5c 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -301,7 +301,7 @@ FunctionLiteral* Parser::DefaultConstructor(bool call_super, Scope* scope, Runtime::FunctionForId(Runtime::kDefaultConstructorSuperCall), args, pos); body->Add(factory()->NewExpressionStatement(call, pos), zone()); - function_scope->RecordSuperUsage(); + function_scope->RecordSuperConstructorCallUsage(); } materialized_literal_count = function_state.materialized_literal_count(); @@ -1053,8 +1053,8 @@ FunctionLiteral* Parser::ParseLazy(Utf16CharacterStream* source) { DCHECK(expression->IsFunctionLiteral()); result = expression->AsFunctionLiteral(); } else if (shared_info->is_default_constructor()) { - result = DefaultConstructor(shared_info->uses_super(), scope, - shared_info->start_position(), + result = DefaultConstructor(shared_info->uses_super_constructor_call(), + scope, shared_info->start_position(), shared_info->end_position()); } else { result = ParseFunctionLiteral(raw_name, Scanner::Location::invalid(), diff --git a/src/preparser.h b/src/preparser.h index bdab177..b50019c 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -979,7 +979,8 @@ class PreParserScope { bool IsDeclared(const PreParserIdentifier& identifier) const { return false; } void DeclareParameter(const PreParserIdentifier& identifier, VariableMode) {} void RecordArgumentsUsage() {} - void RecordSuperUsage() {} + void RecordSuperPropertyUsage() {} + void RecordSuperConstructorCallUsage() {} void RecordThisUsage() {} // Allow scope->Foo() to work. @@ -2563,7 +2564,6 @@ ParserBase::ParseMemberWithNewPrefixesExpression(bool* ok) { int new_pos = position(); ExpressionT result = this->EmptyExpression(); if (Check(Token::SUPER)) { - scope_->RecordSuperUsage(); result = this->SuperReference(scope_, factory()); } else { result = this->ParseMemberWithNewPrefixesExpression(CHECK_OK); @@ -2623,10 +2623,12 @@ ParserBase::ParseMemberExpression(bool* ok) { } else if (peek() == Token::SUPER) { int beg_pos = position(); Consume(Token::SUPER); - scope_->RecordSuperUsage(); Token::Value next = peek(); - if (next == Token::PERIOD || next == Token::LBRACK || - next == Token::LPAREN) { + if (next == Token::PERIOD || next == Token::LBRACK) { + scope_->RecordSuperPropertyUsage(); + result = this->SuperReference(scope_, factory()); + } else if (next == Token::LPAREN) { + scope_->RecordSuperConstructorCallUsage(); result = this->SuperReference(scope_, factory()); } else { ReportMessageAt(Scanner::Location(beg_pos, position()), diff --git a/src/scopes.cc b/src/scopes.cc index 65a91fc..74c3f0b 100644 --- a/src/scopes.cc +++ b/src/scopes.cc @@ -161,7 +161,8 @@ void Scope::SetDefaults(ScopeType scope_type, scope_contains_with_ = false; scope_calls_eval_ = false; scope_uses_arguments_ = false; - scope_uses_super_ = false; + scope_uses_super_property_ = false; + scope_uses_super_constructor_call_ = false; scope_uses_this_ = false; asm_module_ = false; asm_function_ = outer_scope != NULL && outer_scope->asm_module_; @@ -171,7 +172,8 @@ void Scope::SetDefaults(ScopeType scope_type, inner_scope_calls_eval_ = false; inner_scope_uses_arguments_ = false; inner_scope_uses_this_ = false; - inner_scope_uses_super_ = false; + inner_scope_uses_super_property_ = false; + inner_scope_uses_super_constructor_call_ = false; force_eager_compilation_ = false; force_context_allocation_ = (outer_scope != NULL && !is_function_scope()) ? outer_scope->has_forced_context_allocation() : false; @@ -375,7 +377,9 @@ Scope* Scope::FinalizeBlockScope() { // Propagate usage flags to outer scope. if (uses_arguments()) outer_scope_->RecordArgumentsUsage(); - if (uses_super()) outer_scope_->RecordSuperUsage(); + if (uses_super_property()) outer_scope_->RecordSuperPropertyUsage(); + if (uses_super_constructor_call()) + outer_scope_->RecordSuperConstructorCallUsage(); if (uses_this()) outer_scope_->RecordThisUsage(); return NULL; @@ -896,12 +900,20 @@ void Scope::Print(int n) { if (scope_contains_with_) Indent(n1, "// scope contains 'with'\n"); if (scope_calls_eval_) Indent(n1, "// scope calls 'eval'\n"); if (scope_uses_arguments_) Indent(n1, "// scope uses 'arguments'\n"); - if (scope_uses_super_) Indent(n1, "// scope uses 'super'\n"); + if (scope_uses_super_property_) + Indent(n1, "// scope uses 'super' property\n"); + if (scope_uses_super_constructor_call_) { + Indent(n1, "// scope uses 'super' constructor\n"); + } if (scope_uses_this_) Indent(n1, "// scope uses 'this'\n"); if (inner_scope_uses_arguments_) { Indent(n1, "// inner scope uses 'arguments'\n"); } - if (inner_scope_uses_super_) Indent(n1, "// inner scope uses 'super'\n"); + if (inner_scope_uses_super_property_) + Indent(n1, "// inner scope uses 'super' property\n"); + if (inner_scope_uses_super_constructor_call_) { + Indent(n1, "// inner scope uses 'super' constructor\n"); + } if (inner_scope_uses_this_) Indent(n1, "// inner scope uses 'this'\n"); if (outer_scope_calls_sloppy_eval_) { Indent(n1, "// outer scope calls 'eval' in sloppy context\n"); @@ -1174,8 +1186,13 @@ void Scope::PropagateScopeInfo(bool outer_scope_calls_sloppy_eval ) { if (inner->scope_uses_arguments_ || inner->inner_scope_uses_arguments_) { inner_scope_uses_arguments_ = true; } - if (inner->scope_uses_super_ || inner->inner_scope_uses_super_) { - inner_scope_uses_super_ = true; + if (inner->scope_uses_super_property_ || + inner->inner_scope_uses_super_property_) { + inner_scope_uses_super_property_ = true; + } + if (inner->uses_super_constructor_call() || + inner->inner_scope_uses_super_constructor_call_) { + inner_scope_uses_super_constructor_call_ = true; } if (inner->scope_uses_this_ || inner->inner_scope_uses_this_) { inner_scope_uses_this_ = true; diff --git a/src/scopes.h b/src/scopes.h index 4975ab4..8d79006 100644 --- a/src/scopes.h +++ b/src/scopes.h @@ -214,7 +214,12 @@ class Scope: public ZoneObject { void RecordArgumentsUsage() { scope_uses_arguments_ = true; } // Inform the scope that the corresponding code uses "super". - void RecordSuperUsage() { scope_uses_super_ = true; } + void RecordSuperPropertyUsage() { scope_uses_super_property_ = true; } + + // Inform the scope that the corresponding code invokes "super" constructor. + void RecordSuperConstructorCallUsage() { + scope_uses_super_constructor_call_ = true; + } // Inform the scope that the corresponding code uses "this". void RecordThisUsage() { scope_uses_this_ = true; } @@ -307,10 +312,20 @@ class Scope: public ZoneObject { bool uses_arguments() const { return scope_uses_arguments_; } // Does any inner scope access "arguments". bool inner_uses_arguments() const { return inner_scope_uses_arguments_; } - // Does this scope access "super". - bool uses_super() const { return scope_uses_super_; } - // Does any inner scope access "super". - bool inner_uses_super() const { return inner_scope_uses_super_; } + // Does this scope access "super" property (super.foo). + bool uses_super_property() const { return scope_uses_super_property_; } + // Does any inner scope access "super" property. + bool inner_uses_super_property() const { + return inner_scope_uses_super_property_; + } + // Does this scope calls "super" constructor. + bool uses_super_constructor_call() const { + return scope_uses_super_constructor_call_; + } + // Does any inner scope calls "super" constructor. + bool inner_uses_super_constructor_call() const { + return inner_scope_uses_super_constructor_call_; + } // Does this scope access "this". bool uses_this() const { return scope_uses_this_; } // Does any inner scope access "this". @@ -496,8 +511,10 @@ class Scope: public ZoneObject { bool scope_calls_eval_; // This scope uses "arguments". bool scope_uses_arguments_; - // This scope uses "super". - bool scope_uses_super_; + // This scope uses "super" property ('super.foo'). + bool scope_uses_super_property_; + // This scope uses "super" constructor ('super(..)'). + bool scope_uses_super_constructor_call_; // This scope uses "this". bool scope_uses_this_; // This scope contains an "use asm" annotation. @@ -514,7 +531,8 @@ class Scope: public ZoneObject { bool outer_scope_calls_sloppy_eval_; bool inner_scope_calls_eval_; bool inner_scope_uses_arguments_; - bool inner_scope_uses_super_; + bool inner_scope_uses_super_property_; + bool inner_scope_uses_super_constructor_call_; bool inner_scope_uses_this_; bool force_eager_compilation_; bool force_context_allocation_; diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index 80d3406..4e2f828 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -932,11 +932,13 @@ TEST(ScopeUsesArgumentsSuperThis) { enum Expected { NONE = 0, ARGUMENTS = 1, - SUPER = 2, - THIS = 4, - INNER_ARGUMENTS = 8, - INNER_SUPER = 16, - INNER_THIS = 32 + SUPER_PROPERTY = 2, + SUPER_CONSTRUCTOR_CALL = 4, + THIS = 8, + INNER_ARGUMENTS = 16, + INNER_SUPER_PROPERTY = 32, + INNER_SUPER_CONSTRUCTOR_CALL = 64, + INNER_THIS = 128 }; static const struct { @@ -946,27 +948,29 @@ TEST(ScopeUsesArgumentsSuperThis) { {"", NONE}, {"return this", THIS}, {"return arguments", ARGUMENTS}, - {"return super()", SUPER}, - {"return super.x", SUPER}, + {"return super()", SUPER_CONSTRUCTOR_CALL}, + {"return super.x", SUPER_PROPERTY}, {"return arguments[0]", ARGUMENTS}, {"return this + arguments[0]", ARGUMENTS | THIS}, - {"return this + arguments[0] + super.x", ARGUMENTS | SUPER | THIS}, + {"return this + arguments[0] + super.x", + ARGUMENTS | SUPER_PROPERTY | THIS}, {"return x => this + x", INNER_THIS}, - {"return x => super() + x", INNER_SUPER}, + {"return x => super() + x", INNER_SUPER_CONSTRUCTOR_CALL}, {"this.foo = 42;", THIS}, {"this.foo();", THIS}, {"if (foo()) { this.f() }", THIS}, - {"if (foo()) { super.f() }", SUPER}, + {"if (foo()) { super.f() }", SUPER_PROPERTY}, {"if (arguments.length) { this.f() }", ARGUMENTS | THIS}, {"while (true) { this.f() }", THIS}, - {"while (true) { super.f() }", SUPER}, + {"while (true) { super.f() }", SUPER_PROPERTY}, {"if (true) { while (true) this.foo(arguments) }", ARGUMENTS | THIS}, // Multiple nesting levels must work as well. {"while (true) { while (true) { while (true) return this } }", THIS}, {"while (true) { while (true) { while (true) return super() } }", - SUPER}, + SUPER_CONSTRUCTOR_CALL}, {"if (1) { return () => { while (true) new this() } }", INNER_THIS}, - {"if (1) { return () => { while (true) new super() } }", INNER_SUPER}, + {"if (1) { return () => { while (true) new super() } }", NONE}, + {"if (1) { return () => { while (true) new new super() } }", NONE}, // Note that propagation of the inner_uses_this() value does not // cross boundaries of normal functions onto parent scopes. {"return function (x) { return this + x }", NONE}, @@ -981,9 +985,10 @@ TEST(ScopeUsesArgumentsSuperThis) { {"\"use strict\"; while (true) { let x; this, arguments; }", INNER_ARGUMENTS | INNER_THIS}, {"\"use strict\"; while (true) { let x; this, super(), arguments; }", - INNER_ARGUMENTS | INNER_SUPER | INNER_THIS}, + INNER_ARGUMENTS | INNER_SUPER_CONSTRUCTOR_CALL | INNER_THIS}, {"\"use strict\"; if (foo()) { let x; this.f() }", INNER_THIS}, - {"\"use strict\"; if (foo()) { let x; super.f() }", INNER_SUPER}, + {"\"use strict\"; if (foo()) { let x; super.f() }", + INNER_SUPER_PROPERTY}, {"\"use strict\"; if (1) {" " let x; return function () { return this + super() + arguments }" "}", @@ -1033,12 +1038,17 @@ TEST(ScopeUsesArgumentsSuperThis) { i::Scope* scope = script_scope->inner_scopes()->at(0); CHECK_EQ((source_data[i].expected & ARGUMENTS) != 0, scope->uses_arguments()); - CHECK_EQ((source_data[i].expected & SUPER) != 0, scope->uses_super()); + CHECK_EQ((source_data[i].expected & SUPER_PROPERTY) != 0, + scope->uses_super_property()); + CHECK_EQ((source_data[i].expected & SUPER_CONSTRUCTOR_CALL) != 0, + scope->uses_super_constructor_call()); CHECK_EQ((source_data[i].expected & THIS) != 0, scope->uses_this()); CHECK_EQ((source_data[i].expected & INNER_ARGUMENTS) != 0, scope->inner_uses_arguments()); - CHECK_EQ((source_data[i].expected & INNER_SUPER) != 0, - scope->inner_uses_super()); + CHECK_EQ((source_data[i].expected & INNER_SUPER_PROPERTY) != 0, + scope->inner_uses_super_property()); + CHECK_EQ((source_data[i].expected & INNER_SUPER_CONSTRUCTOR_CALL) != 0, + scope->inner_uses_super_constructor_call()); CHECK_EQ((source_data[i].expected & INNER_THIS) != 0, scope->inner_uses_this()); } diff --git a/test/mjsunit/harmony/classes.js b/test/mjsunit/harmony/classes.js index 4291770..5cea2a9 100644 --- a/test/mjsunit/harmony/classes.js +++ b/test/mjsunit/harmony/classes.js @@ -777,3 +777,75 @@ function assertAccessorDescriptor(object, name) { var x = (class x extends x {}); }, ReferenceError); })(); + + +(function TestSuperCallSyntacticRestriction() { + assertThrows(function() { + class C { + constructor() { + var y; + super(); + } + }; new C(); + }, TypeError); + assertThrows(function() { + class C { + constructor() { + super(this.x); + } + }; new C(); + }, TypeError); + assertThrows(function() { + class C { + constructor() { + super(this); + } + }; new C(); + }, TypeError); + assertThrows(function() { + class C { + constructor() { + super(1, 2, Object.getPrototypeOf(this)); + } + }; new C(); + }, TypeError); + assertThrows(function() { + class C { + constructor() { + { super(1, 2); } + } + }; new C(); + }, TypeError); + assertThrows(function() { + class C { + constructor() { + if (1) super(); + } + }; new C(); + }, TypeError); + + class C1 extends Object { + constructor() { + 'use strict'; + super(); + } + }; + new C1(); + + class C2 extends Object { + constructor() { + ; 'use strict';;;;; + super(); + } + }; + new C2(); + + class C3 extends Object { + constructor() { + ; 'use strict';;;;; + // This is a comment. + super(); + } + }; + new C3(); +}()); diff --git a/test/mjsunit/harmony/super.js b/test/mjsunit/harmony/super.js index d972407..6dcc393 100644 --- a/test/mjsunit/harmony/super.js +++ b/test/mjsunit/harmony/super.js @@ -1861,3 +1861,61 @@ function Subclass(base, constructor) { T1.__proto = null; assertThrows(function() { new T1(); }, TypeError); }()); + + +(function TestSuperCallSyntacticRestriction() { + assertThrows(function() { + function C() { + var y; + super(); + } + new C(); + }, TypeError); + assertThrows(function() { + function C() { + super(this.x); + } + new C(); + }, TypeError); + assertThrows(function() { + function C() { + super(this); + } + new C(); + }, TypeError); + assertThrows(function() { + function C() { + super(1, 2, Object.getPrototypeOf(this)); + } + new C(); + }, TypeError); + assertThrows(function() { + function C() { + { super(1, 2); } + }; new C(); + }, TypeError); + assertThrows(function() { + function C() { + if (1) super(); + }; new C(); + }, TypeError); + + function C1() { + 'use strict'; + super(); + }; + new C1(); + + function C2() { + ; 'use strict';;;;; + super(); + }; + new C2(); + + function C3() { + ; 'use strict';;;;; + // This is a comment. + super(); + } + new C3(); +}()); diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 0f79d18..4958528 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -359,6 +359,8 @@ '../../src/assembler.h', '../../src/assert-scope.h', '../../src/assert-scope.cc', + '../../src/ast-this-access-visitor.cc', + '../../src/ast-this-access-visitor.h', '../../src/ast-value-factory.cc', '../../src/ast-value-factory.h', '../../src/ast-numbering.cc', -- 2.7.4