new classes: assert that constructors are not callable and rewrite 'return;'

R=arv@chromium.org
BUG=v8:3834
LOG=N

Review URL: https://codereview.chromium.org/885643004

Cr-Commit-Position: refs/heads/master@{#26483}
This commit is contained in:
dslomov 2015-02-06 02:34:50 -08:00 committed by Commit bot
parent a44511d413
commit 158a87659f
14 changed files with 131 additions and 57 deletions

View File

@ -261,7 +261,8 @@ class AstValue : public ZoneObject {
F(use_asm, "use asm") \
F(use_strong, "use strong") \
F(use_strict, "use strict") \
F(value, "value")
F(value, "value") \
F(is_construct_call, "_IsConstructCall")
#define OTHER_CONSTANTS(F) \
F(true_value) \

View File

@ -2618,7 +2618,7 @@ class FunctionLiteral FINAL : public Expression {
class HasDuplicateParameters : public BitField<ParameterFlag, 3, 1> {};
class IsFunction : public BitField<IsFunctionFlag, 4, 1> {};
class IsParenthesized : public BitField<IsParenthesizedFlag, 5, 1> {};
class FunctionKindBits : public BitField<FunctionKind, 6, 6> {};
class FunctionKindBits : public BitField<FunctionKind, 6, 7> {};
};

View File

@ -591,7 +591,7 @@ class FastNewClosureStub : public HydrogenCodeStub {
private:
STATIC_ASSERT(LANGUAGE_END == 3);
class LanguageModeBits : public BitField<LanguageMode, 0, 2> {};
class FunctionKindBits : public BitField<FunctionKind, 2, 6> {};
class FunctionKindBits : public BitField<FunctionKind, 2, 7> {};
DEFINE_CALL_INTERFACE_DESCRIPTOR(FastNewClosure);
DEFINE_HYDROGEN_CODE_STUB(FastNewClosure, HydrogenCodeStub);

View File

@ -213,6 +213,7 @@ bool Linkage::NeedsFrameState(Runtime::FunctionId function) {
case Runtime::kStringMatch:
case Runtime::kStringReplaceGlobalRegExpWithString:
case Runtime::kThrowConstAssignError:
case Runtime::kThrowConstructorNonCallableError:
case Runtime::kThrowNonMethodError:
case Runtime::kThrowNotDateError:
case Runtime::kThrowReferenceError:

View File

@ -820,7 +820,8 @@ enum FunctionKind {
kConciseGeneratorMethod = kGeneratorFunction | kConciseMethod,
kAccessorFunction = 1 << 3,
kDefaultConstructor = 1 << 4,
kSubclassConstructor = 1 << 5
kSubclassConstructor = 1 << 5,
kBaseConstructor = 1 << 6,
};
@ -832,6 +833,7 @@ inline bool IsValidFunctionKind(FunctionKind kind) {
kind == FunctionKind::kConciseGeneratorMethod ||
kind == FunctionKind::kAccessorFunction ||
kind == FunctionKind::kDefaultConstructor ||
kind == FunctionKind::kBaseConstructor ||
kind == FunctionKind::kSubclassConstructor;
}
@ -866,10 +868,24 @@ inline bool IsDefaultConstructor(FunctionKind kind) {
}
inline bool IsBaseConstructor(FunctionKind kind) {
DCHECK(IsValidFunctionKind(kind));
return kind & FunctionKind::kBaseConstructor;
}
inline bool IsSubclassConstructor(FunctionKind kind) {
DCHECK(IsValidFunctionKind(kind));
return kind & FunctionKind::kSubclassConstructor;
}
inline bool IsConstructor(FunctionKind kind) {
DCHECK(IsValidFunctionKind(kind));
return kind &
(FunctionKind::kBaseConstructor | FunctionKind::kSubclassConstructor |
FunctionKind::kDefaultConstructor);
}
} } // namespace v8::internal
namespace i = v8::internal;

View File

@ -181,7 +181,8 @@ var kMessages = {
sloppy_lexical: ["Block-scoped declarations (let, const, function, class) not yet supported outside strict mode"],
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."],
duplicate_proto: ["Duplicate __proto__ fields are not allowed in object literals"],
param_after_rest: ["Rest parameter must be last formal parameter"]
param_after_rest: ["Rest parameter must be last formal parameter"],
constructor_noncallable: ["Class constructors cannot be invoked without 'new'"]
};

View File

@ -7195,6 +7195,7 @@ class SharedFunctionInfo: public HeapObject {
kIsConciseMethod,
kIsAccessorFunction,
kIsDefaultConstructor,
kIsBaseConstructor,
kIsSubclassConstructor,
kIsAsmFunction,
kDeserialized,
@ -7203,7 +7204,7 @@ class SharedFunctionInfo: public HeapObject {
// Add hints for other modes when they're added.
STATIC_ASSERT(LANGUAGE_END == 3);
class FunctionKindBits : public BitField<FunctionKind, kIsArrow, 6> {};
class FunctionKindBits : public BitField<FunctionKind, kIsArrow, 7> {};
class DeoptCountBits : public BitField<int, 0, 4> {};
class OptReenableTriesBits : public BitField<int, 4, 18> {};

View File

@ -298,7 +298,7 @@ FunctionLiteral* Parser::DefaultConstructor(bool call_super, Scope* scope,
{
AstNodeFactory function_factory(ast_value_factory());
FunctionState function_state(&function_state_, &scope_, function_scope,
&function_factory);
kDefaultConstructor, &function_factory);
body = new (zone()) ZoneList<Statement*>(1, zone());
if (call_super) {
@ -946,7 +946,7 @@ FunctionLiteral* Parser::DoParseProgram(CompilationInfo* info, Scope** scope,
// Enters 'scope'.
AstNodeFactory function_factory(ast_value_factory());
FunctionState function_state(&function_state_, &scope_, *scope,
&function_factory);
kNormalFunction, &function_factory);
scope_->SetLanguageMode(info->language_mode());
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16, zone());
@ -1066,7 +1066,7 @@ FunctionLiteral* Parser::ParseLazy(Utf16CharacterStream* source) {
original_scope_ = scope;
AstNodeFactory function_factory(ast_value_factory());
FunctionState function_state(&function_state_, &scope_, scope,
&function_factory);
shared_info->kind(), &function_factory);
DCHECK(is_sloppy(scope->language_mode()) ||
is_strict(info()->language_mode()));
DCHECK(info()->language_mode() == shared_info->language_mode());
@ -2650,11 +2650,17 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
tok == Token::SEMICOLON ||
tok == Token::RBRACE ||
tok == Token::EOS) {
return_value = GetLiteralUndefined(position());
if (FLAG_experimental_classes &&
IsSubclassConstructor(function_state_->kind())) {
return_value = ThisExpression(scope_, factory(), loc.beg_pos);
} else {
return_value = GetLiteralUndefined(position());
}
} else {
return_value = ParseExpression(true, CHECK_OK);
}
ExpectSemicolon(CHECK_OK);
if (is_generator()) {
Expression* generator = factory()->NewVariableProxy(
function_state_->generator_object_variable());
@ -3671,7 +3677,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
// Parse function body.
{
AstNodeFactory function_factory(ast_value_factory());
FunctionState function_state(&function_state_, &scope_, scope,
FunctionState function_state(&function_state_, &scope_, scope, kind,
&function_factory);
scope_->SetScopeName(function_name);
@ -3825,7 +3831,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
&expected_property_count, CHECK_OK);
} else {
body = ParseEagerFunctionBody(function_name, pos, fvar, fvar_init_op,
is_generator, CHECK_OK);
kind, CHECK_OK);
materialized_literal_count = function_state.materialized_literal_count();
expected_property_count = function_state.expected_property_count();
handler_count = function_state.handler_count();
@ -3935,7 +3941,7 @@ void Parser::SkipLazyFunctionBody(const AstRawString* function_name,
ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
const AstRawString* function_name, int pos, Variable* fvar,
Token::Value fvar_init_op, bool is_generator, bool* ok) {
Token::Value fvar_init_op, FunctionKind kind, bool* ok) {
// Everything inside an eagerly parsed function will be parsed eagerly
// (see comment above).
ParsingModeScope parsing_mode(this, PARSE_EAGERLY);
@ -3952,8 +3958,29 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
RelocInfo::kNoPosition), zone());
}
// For concise constructors, check that they are constructed,
// not called.
if (FLAG_experimental_classes && i::IsConstructor(kind)) {
ZoneList<Expression*>* arguments =
new (zone()) ZoneList<Expression*>(0, zone());
CallRuntime* construct_check = factory()->NewCallRuntime(
ast_value_factory()->is_construct_call_string(),
Runtime::FunctionForId(Runtime::kInlineIsConstructCall), arguments,
pos);
CallRuntime* non_callable_error = factory()->NewCallRuntime(
ast_value_factory()->empty_string(),
Runtime::FunctionForId(Runtime::kThrowConstructorNonCallableError),
arguments, pos);
IfStatement* if_statement = factory()->NewIfStatement(
factory()->NewUnaryOperation(Token::NOT, construct_check, pos),
factory()->NewReturnStatement(non_callable_error, pos),
factory()->NewEmptyStatement(pos), pos);
body->Add(if_statement, zone());
}
// For generators, allocate and yield an iterator on function entry.
if (is_generator) {
if (IsGeneratorFunction(kind)) {
ZoneList<Expression*>* arguments =
new(zone()) ZoneList<Expression*>(0, zone());
CallRuntime* allocation = factory()->NewCallRuntime(
@ -3974,7 +4001,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
ParseStatementList(body, Token::RBRACE, false, NULL, CHECK_OK);
if (is_generator) {
if (IsGeneratorFunction(kind)) {
VariableProxy* get_proxy = factory()->NewVariableProxy(
function_state_->generator_object_variable());
Expression* undefined =
@ -3985,6 +4012,14 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
yield, RelocInfo::kNoPosition), zone());
}
if (FLAG_experimental_classes && IsSubclassConstructor(kind)) {
body->Add(
factory()->NewReturnStatement(
this->ThisExpression(scope_, factory(), RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
zone());
}
Expect(Token::RBRACE, CHECK_OK);
scope_->set_end_position(scanner()->location().end_pos);
@ -4025,7 +4060,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
reusable_preparser_->set_allow_strong_mode(allow_strong_mode());
}
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
language_mode(), is_generator(), logger);
language_mode(), function_state_->kind(), logger);
if (pre_parse_timer_ != NULL) {
pre_parse_timer_->Stop();
}

View File

@ -583,7 +583,7 @@ class ParserTraits {
int* expected_property_count, bool* ok);
V8_INLINE ZoneList<Statement*>* ParseEagerFunctionBody(
const AstRawString* name, int pos, Variable* fvar,
Token::Value fvar_init_op, bool is_generator, bool* ok);
Token::Value fvar_init_op, FunctionKind kind, bool* ok);
ClassLiteral* ParseClassLiteral(const AstRawString* name,
Scanner::Location class_name_location,
@ -871,7 +871,7 @@ class Parser : public ParserBase<ParserTraits> {
// Consumes the ending }.
ZoneList<Statement*>* ParseEagerFunctionBody(
const AstRawString* function_name, int pos, Variable* fvar,
Token::Value fvar_init_op, bool is_generator, bool* ok);
Token::Value fvar_init_op, FunctionKind kind, bool* ok);
void ThrowPendingError();
@ -936,9 +936,9 @@ void ParserTraits::SkipLazyFunctionBody(const AstRawString* function_name,
ZoneList<Statement*>* ParserTraits::ParseEagerFunctionBody(
const AstRawString* name, int pos, Variable* fvar,
Token::Value fvar_init_op, bool is_generator, bool* ok) {
return parser_->ParseEagerFunctionBody(name, pos, fvar, fvar_init_op,
is_generator, ok);
Token::Value fvar_init_op, FunctionKind kind, bool* ok) {
return parser_->ParseEagerFunctionBody(name, pos, fvar, fvar_init_op, kind,
ok);
}
void ParserTraits::CheckConflictingVarDeclarations(v8::internal::Scope* scope,

View File

@ -104,18 +104,18 @@ PreParserExpression PreParserTraits::ParseFunctionLiteral(
PreParser::PreParseResult PreParser::PreParseLazyFunction(
LanguageMode language_mode, bool is_generator, ParserRecorder* log) {
LanguageMode language_mode, FunctionKind kind, ParserRecorder* log) {
log_ = log;
// Lazy functions always have trivial outer scopes (no with/catch scopes).
PreParserScope top_scope(scope_, SCRIPT_SCOPE);
PreParserFactory top_factory(NULL);
FunctionState top_state(&function_state_, &scope_, &top_scope, &top_factory);
FunctionState top_state(&function_state_, &scope_, &top_scope,
kNormalFunction, &top_factory);
scope_->SetLanguageMode(language_mode);
PreParserScope function_scope(scope_, FUNCTION_SCOPE);
PreParserFactory function_factory(NULL);
FunctionState function_state(&function_state_, &scope_, &function_scope,
FunctionState function_state(&function_state_, &scope_, &function_scope, kind,
&function_factory);
function_state.set_is_generator(is_generator);
DCHECK_EQ(Token::LBRACE, scanner()->current_token());
bool ok = true;
int start_position = peek_position();
@ -869,9 +869,8 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
ScopeType outer_scope_type = scope_->type();
PreParserScope function_scope(scope_, FUNCTION_SCOPE);
PreParserFactory factory(NULL);
FunctionState function_state(&function_state_, &scope_, &function_scope,
FunctionState function_state(&function_state_, &scope_, &function_scope, kind,
&factory);
function_state.set_is_generator(IsGeneratorFunction(kind));
// FormalParameterList ::
// '(' (Identifier)*[','] ')'
Expect(Token::LPAREN, CHECK_OK);

View File

@ -200,7 +200,7 @@ class ParserBase : public Traits {
public:
FunctionState(FunctionState** function_state_stack,
typename Traits::Type::Scope** scope_stack,
typename Traits::Type::Scope* scope,
typename Traits::Type::Scope* scope, FunctionKind kind,
typename Traits::Type::Factory* factory);
~FunctionState();
@ -217,15 +217,15 @@ class ParserBase : public Traits {
void AddProperty() { expected_property_count_++; }
int expected_property_count() { return expected_property_count_; }
void set_is_generator(bool is_generator) { is_generator_ = is_generator; }
bool is_generator() const { return is_generator_; }
bool is_generator() const { return IsGeneratorFunction(kind_); }
FunctionKind kind() const { return kind_; }
void set_generator_object_variable(
typename Traits::Type::GeneratorVariable* variable) {
DCHECK(variable != NULL);
DCHECK(!is_generator());
DCHECK(is_generator());
generator_object_variable_ = variable;
is_generator_ = true;
}
typename Traits::Type::GeneratorVariable* generator_object_variable()
const {
@ -246,13 +246,13 @@ class ParserBase : public Traits {
// Properties count estimation.
int expected_property_count_;
// Whether the function is a generator.
bool is_generator_;
FunctionKind kind_;
// For generators, this variable may hold the generator object. It variable
// is used by yield expressions and return statements. It is not necessary
// for generator functions to have this variable set.
Variable* generator_object_variable_;
FunctionState** function_state_stack_;
FunctionState* outer_function_state_;
typename Traits::Type::Scope** scope_stack_;
@ -1451,9 +1451,9 @@ class PreParserTraits {
}
V8_INLINE PreParserStatementList
ParseEagerFunctionBody(PreParserIdentifier function_name, int pos,
Variable* fvar, Token::Value fvar_init_op,
bool is_generator, bool* ok);
ParseEagerFunctionBody(PreParserIdentifier function_name, int pos,
Variable* fvar, Token::Value fvar_init_op,
FunctionKind kind, bool* ok);
// Utility functions
int DeclareArrowParametersFromExpression(PreParserExpression expression,
@ -1545,7 +1545,8 @@ class PreParser : public ParserBase<PreParserTraits> {
PreParseResult PreParseProgram(int* materialized_literals = 0) {
PreParserScope scope(scope_, SCRIPT_SCOPE);
PreParserFactory factory(NULL);
FunctionState top_scope(&function_state_, &scope_, &scope, &factory);
FunctionState top_scope(&function_state_, &scope_, &scope, kNormalFunction,
&factory);
bool ok = true;
int start_position = scanner()->peek_location().beg_pos;
ParseSourceElements(Token::EOS, &ok);
@ -1571,7 +1572,7 @@ class PreParser : public ParserBase<PreParserTraits> {
// At return, unless an error occurred, the scanner is positioned before the
// the final '}'.
PreParseResult PreParseLazyFunction(LanguageMode language_mode,
bool is_generator, ParserRecorder* log);
FunctionKind kind, ParserRecorder* log);
private:
friend class PreParserTraits;
@ -1635,9 +1636,9 @@ class PreParser : public ParserBase<PreParserTraits> {
int* materialized_literal_count,
int* expected_property_count, bool* ok);
V8_INLINE PreParserStatementList
ParseEagerFunctionBody(PreParserIdentifier function_name, int pos,
Variable* fvar, Token::Value fvar_init_op,
bool is_generator, bool* ok);
ParseEagerFunctionBody(PreParserIdentifier function_name, int pos,
Variable* fvar, Token::Value fvar_init_op,
FunctionKind kind, bool* ok);
Expression ParseFunctionLiteral(
Identifier name, Scanner::Location function_name_location,
@ -1663,7 +1664,7 @@ void PreParserTraits::MaterializeTemplateCallsiteLiterals() {
PreParserStatementList PreParser::ParseEagerFunctionBody(
PreParserIdentifier function_name, int pos, Variable* fvar,
Token::Value fvar_init_op, bool is_generator, bool* ok) {
Token::Value fvar_init_op, FunctionKind kind, bool* ok) {
ParsingModeScope parsing_mode(this, PARSE_EAGERLY);
ParseSourceElements(Token::RBRACE, ok);
@ -1676,9 +1677,9 @@ PreParserStatementList PreParser::ParseEagerFunctionBody(
PreParserStatementList PreParserTraits::ParseEagerFunctionBody(
PreParserIdentifier function_name, int pos, Variable* fvar,
Token::Value fvar_init_op, bool is_generator, bool* ok) {
Token::Value fvar_init_op, FunctionKind kind, bool* ok) {
return pre_parser_->ParseEagerFunctionBody(function_name, pos, fvar,
fvar_init_op, is_generator, ok);
fvar_init_op, kind, ok);
}
@ -1686,12 +1687,12 @@ template <class Traits>
ParserBase<Traits>::FunctionState::FunctionState(
FunctionState** function_state_stack,
typename Traits::Type::Scope** scope_stack,
typename Traits::Type::Scope* scope,
typename Traits::Type::Scope* scope, FunctionKind kind,
typename Traits::Type::Factory* factory)
: next_materialized_literal_index_(JSFunction::kLiteralsPrefixSize),
next_handler_index_(0),
expected_property_count_(0),
is_generator_(false),
kind_(kind),
generator_object_variable_(NULL),
function_state_stack_(function_state_stack),
outer_function_state_(*function_state_stack),
@ -2157,7 +2158,7 @@ ParserBase<Traits>::ParsePropertyDefinition(ObjectLiteralCheckerBase* checker,
if (in_class && !is_static && this->IsConstructor(name)) {
*has_seen_constructor = true;
kind = has_extends ? FunctionKind::kSubclassConstructor
: FunctionKind::kNormalFunction;
: FunctionKind::kBaseConstructor;
}
value = this->ParseFunctionLiteral(
@ -2838,7 +2839,7 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(int start_pos,
typename Traits::Type::Factory function_factory(this->ast_value_factory());
FunctionState function_state(&function_state_, &scope_,
Traits::Type::ptr_to_scope(scope),
&function_factory);
kArrowFunction, &function_factory);
Scanner::Location dupe_error_loc = Scanner::Location::invalid();
num_parameters = Traits::DeclareArrowParametersFromExpression(
params_ast, scope_, &dupe_error_loc, ok);
@ -2871,8 +2872,7 @@ ParserBase<Traits>::ParseArrowFunctionLiteral(int start_pos,
} else {
body = this->ParseEagerFunctionBody(
this->EmptyIdentifier(), RelocInfo::kNoPosition, NULL,
Token::INIT_VAR, false, // Not a generator.
CHECK_OK);
Token::INIT_VAR, kArrowFunction, CHECK_OK);
materialized_literal_count =
function_state.materialized_literal_count();
expected_property_count = function_state.expected_property_count();

View File

@ -38,6 +38,15 @@ RUNTIME_FUNCTION(Runtime_ThrowUnsupportedSuperError) {
}
RUNTIME_FUNCTION(Runtime_ThrowConstructorNonCallableError) {
HandleScope scope(isolate);
DCHECK(args.length() == 0);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError("constructor_noncallable", HandleVector<Object>(NULL, 0)));
}
RUNTIME_FUNCTION(Runtime_ToMethod) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);

View File

@ -186,18 +186,19 @@ namespace internal {
/* Classes support */ \
F(ToMethod, 2, 1) \
F(HomeObjectSymbol, 0, 1) \
F(DefaultConstructorSuperCall, 0, 1) \
F(DefineClass, 6, 1) \
F(DefineClassMethod, 3, 1) \
F(ClassGetSourceCode, 1, 1) \
F(ThrowNonMethodError, 0, 1) \
F(ThrowUnsupportedSuperError, 0, 1) \
F(LoadFromSuper, 3, 1) \
F(LoadKeyedFromSuper, 3, 1) \
F(ThrowConstructorNonCallableError, 0, 1) \
F(ThrowNonMethodError, 0, 1) \
F(ThrowUnsupportedSuperError, 0, 1) \
F(StoreToSuper_Strict, 4, 1) \
F(StoreToSuper_Sloppy, 4, 1) \
F(StoreKeyedToSuper_Strict, 4, 1) \
F(StoreKeyedToSuper_Sloppy, 4, 1) \
F(DefaultConstructorSuperCall, 0, 1)
F(StoreKeyedToSuper_Sloppy, 4, 1)
#define RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \

View File

@ -34,15 +34,18 @@ class Subclass extends Base {
let b = new Base(1, 2);
assertSame(3, b.prp);
let s = new Subclass(2, -1);
assertSame(1, s.prp);
assertSame(undefined, s.prp1);
assertFalse(s.hasOwnProperty("prp1"));
class Subclass2 extends Base {
constructor() {
constructor(x) {
super(1,2);
if (x < 0) return;
let called = false;
function tmp() { called = true; return 3; }
var exn = null;
@ -54,4 +57,11 @@ class Subclass2 extends Base {
}
}
new Subclass2();
var s2 = new Subclass2(1);
assertSame(3, s2.prp);
var s3 = new Subclass2(-1);
assertSame(3, s3.prp);
assertThrows(function() { Subclass.call(new Object(), 1, 2); }, TypeError);
assertThrows(function() { Base.call(new Object(), 1, 2); }, TypeError);