Commit 65aa17b9 authored by dslomov's avatar dslomov Committed by Commit bot

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}
parent b56afa23
......@@ -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",
......
// 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<CaseClause*>* 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<ObjectLiteralProperty*>* 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<ObjectLiteralProperty*>* 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
// 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_
......@@ -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();
}
......
......@@ -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_; }
......
......@@ -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<SharedFunctionInfo> 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<Object> obj = info->isolate()->factory()->NewTypeError(
"super_constructor_call", HandleVector<Object>(nullptr, 0));
Handle<Object> 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<Statement*>* 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<Expression*>* 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;
}
......
......@@ -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."]
};
......
......@@ -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)
......
......@@ -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,
......
......@@ -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(),
......
......@@ -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<Traits>::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<Traits>::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()),
......
......@@ -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;
......
......@@ -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_;
......
......@@ -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());
}
......
......@@ -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();
}());
......@@ -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();
}());
......@@ -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',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment