From 158a87659f02ad4d5e41d2dc19eec1f0f4ded41f Mon Sep 17 00:00:00 2001 From: dslomov Date: Fri, 6 Feb 2015 02:34:50 -0800 Subject: [PATCH] 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} --- src/ast-value-factory.h | 3 +- src/ast.h | 2 +- src/code-stubs.h | 2 +- src/compiler/linkage.cc | 1 + src/globals.h | 18 ++++++- src/messages.js | 3 +- src/objects.h | 3 +- src/parser.cc | 55 ++++++++++++++++---- src/parser.h | 10 ++-- src/preparser.cc | 11 ++-- src/preparser.h | 48 ++++++++--------- src/runtime/runtime-classes.cc | 9 ++++ src/runtime/runtime.h | 9 ++-- test/mjsunit/harmony/classes-experimental.js | 14 ++++- 14 files changed, 131 insertions(+), 57 deletions(-) diff --git a/src/ast-value-factory.h b/src/ast-value-factory.h index e88819c2fe..70a29f14e3 100644 --- a/src/ast-value-factory.h +++ b/src/ast-value-factory.h @@ -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) \ diff --git a/src/ast.h b/src/ast.h index 4e1fb26c26..e457e66a21 100644 --- a/src/ast.h +++ b/src/ast.h @@ -2618,7 +2618,7 @@ class FunctionLiteral FINAL : public Expression { class HasDuplicateParameters : public BitField {}; class IsFunction : public BitField {}; class IsParenthesized : public BitField {}; - class FunctionKindBits : public BitField {}; + class FunctionKindBits : public BitField {}; }; diff --git a/src/code-stubs.h b/src/code-stubs.h index f284a344c9..a60d9d3c26 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -591,7 +591,7 @@ class FastNewClosureStub : public HydrogenCodeStub { private: STATIC_ASSERT(LANGUAGE_END == 3); class LanguageModeBits : public BitField {}; - class FunctionKindBits : public BitField {}; + class FunctionKindBits : public BitField {}; DEFINE_CALL_INTERFACE_DESCRIPTOR(FastNewClosure); DEFINE_HYDROGEN_CODE_STUB(FastNewClosure, HydrogenCodeStub); diff --git a/src/compiler/linkage.cc b/src/compiler/linkage.cc index 384cf1c09a..4d3187506a 100644 --- a/src/compiler/linkage.cc +++ b/src/compiler/linkage.cc @@ -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: diff --git a/src/globals.h b/src/globals.h index 3f2c33593b..0248bb1600 100644 --- a/src/globals.h +++ b/src/globals.h @@ -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; diff --git a/src/messages.js b/src/messages.js index 3b61e1882d..3bfc1a83c1 100644 --- a/src/messages.js +++ b/src/messages.js @@ -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'"] }; diff --git a/src/objects.h b/src/objects.h index 61daada4b9..d66f839bf7 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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 {}; + class FunctionKindBits : public BitField {}; class DeoptCountBits : public BitField {}; class OptReenableTriesBits : public BitField {}; diff --git a/src/parser.cc b/src/parser.cc index 3c72dd0f72..d3fde9ef8f 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -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(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* body = new(zone()) ZoneList(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* 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* Parser::ParseEagerFunctionBody( RelocInfo::kNoPosition), zone()); } + + // For concise constructors, check that they are constructed, + // not called. + if (FLAG_experimental_classes && i::IsConstructor(kind)) { + ZoneList* arguments = + new (zone()) ZoneList(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* arguments = new(zone()) ZoneList(0, zone()); CallRuntime* allocation = factory()->NewCallRuntime( @@ -3974,7 +4001,7 @@ ZoneList* 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* 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(); } diff --git a/src/parser.h b/src/parser.h index ddb542de35..d1d1d7c3c0 100644 --- a/src/parser.h +++ b/src/parser.h @@ -583,7 +583,7 @@ class ParserTraits { int* expected_property_count, bool* ok); V8_INLINE ZoneList* 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 { // Consumes the ending }. ZoneList* 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* 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, diff --git a/src/preparser.cc b/src/preparser.cc index 170c9e7251..4c96ffb52f 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -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); diff --git a/src/preparser.h b/src/preparser.h index 29a2b55e83..e6ef4ecf1a 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -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 { 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 { // 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 { 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 ParserBase::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::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::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::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(); diff --git a/src/runtime/runtime-classes.cc b/src/runtime/runtime-classes.cc index 8a207a8eab..ffd121e612 100644 --- a/src/runtime/runtime-classes.cc +++ b/src/runtime/runtime-classes.cc @@ -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(NULL, 0))); +} + + RUNTIME_FUNCTION(Runtime_ToMethod) { HandleScope scope(isolate); DCHECK(args.length() == 2); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index e8f8295bfb..3f8aa2101a 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -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) \ diff --git a/test/mjsunit/harmony/classes-experimental.js b/test/mjsunit/harmony/classes-experimental.js index 5a3c3e5558..ed720d97b6 100644 --- a/test/mjsunit/harmony/classes-experimental.js +++ b/test/mjsunit/harmony/classes-experimental.js @@ -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);