From 6f97a4948f1495a9b9d60b7f495d0e0efeaa1006 Mon Sep 17 00:00:00 2001 From: dslomov Date: Tue, 3 Feb 2015 09:42:41 -0800 Subject: [PATCH] new classes: special construct stub for derived classs and TDZ for `this`. R=arv@chromium.org,rossberg@chromium.org BUG=v8:3834 LOG=N Review URL: https://codereview.chromium.org/867153003 Cr-Commit-Position: refs/heads/master@{#26409} --- src/arm/builtins-arm.cc | 64 ++++++++++++++++++++++++ src/arm/full-codegen-arm.cc | 18 ++++++- src/arm64/builtins-arm64.cc | 74 ++++++++++++++++++++++++++++ src/arm64/full-codegen-arm64.cc | 17 ++++++- src/ast.h | 2 +- src/builtins.h | 2 + src/globals.h | 11 +++-- src/ia32/builtins-ia32.cc | 51 +++++++++++++++++++ src/ia32/full-codegen-ia32.cc | 16 +++++- src/objects.h | 3 +- src/parser.cc | 27 ++++++---- src/parser.h | 11 +++-- src/preparser.cc | 7 +-- src/preparser.h | 23 +++++---- src/runtime/runtime-classes.cc | 7 +++ src/scopes.cc | 15 +++--- src/scopes.h | 2 +- src/x64/builtins-x64.cc | 53 ++++++++++++++++++++ src/x64/full-codegen-x64.cc | 18 ++++++- test/mjsunit/harmony/classes-experimental.js | 36 +++++++++++--- 20 files changed, 402 insertions(+), 55 deletions(-) diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 1a98803..2c08b80 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -741,6 +741,70 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { } +void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : number of arguments + // -- r1 : constructor function + // -- r2 : allocation site or undefined + // -- r3 : original constructor + // -- lr : return address + // -- sp[...]: constructor arguments + // ----------------------------------- + + // TODO(dslomov): support pretenuring + CHECK(!FLAG_pretenuring_call_new); + + { + FrameScope frame_scope(masm, StackFrame::CONSTRUCT); + + __ mov(r4, r0); + __ SmiTag(r4); + __ push(r4); // Smi-tagged arguments count. + + // receiver is the hole. + __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); + __ push(ip); + + // Set up pointer to last argument. + __ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset)); + + // Copy arguments and receiver to the expression stack. + // r0: number of arguments + // r1: constructor function + // r2: address of last argument (caller sp) + // r4: number of arguments (smi-tagged) + // sp[0]: receiver + // sp[1]: number of arguments (smi-tagged) + Label loop, entry; + __ b(&entry); + __ bind(&loop); + __ ldr(ip, MemOperand(r2, r4, LSL, kPointerSizeLog2 - 1)); + __ push(ip); + __ bind(&entry); + __ sub(r4, r4, Operand(2), SetCC); + __ b(ge, &loop); + + // Call the function. + // r0: number of arguments + // r1: constructor function + ParameterCount actual(r0); + __ InvokeFunction(r1, actual, CALL_FUNCTION, NullCallWrapper()); + + // Restore context from the frame. + // r0: result + // sp[0]: number of arguments (smi-tagged) + __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + __ ldr(r1, MemOperand(sp, 0)); + + // Leave construct frame. + } + + __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1)); + __ add(sp, sp, Operand(kPointerSize)); + __ Jump(lr); +} + + static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, bool is_construct) { // Called from Generate_JS_Entry diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 6139806..051d3a1 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -1538,6 +1538,10 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { bool skip_init_check; if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { skip_init_check = false; + } else if (var->is_this()) { + CHECK((info_->shared_info()->kind() & kSubclassConstructor) != 0); + // TODO(dslomov): implement 'this' hole check elimination. + skip_init_check = false; } else { // Check that we always have valid source position. DCHECK(var->initializer_position() != RelocInfo::kNoPosition); @@ -3249,6 +3253,17 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) { EmitLoadSuperConstructor(super_ref); __ push(result_register()); + Variable* this_var = super_ref->this_var()->var(); + + GetVar(r0, this_var); + __ CompareRoot(r0, Heap::kTheHoleValueRootIndex); + Label uninitialized_this; + __ b(eq, &uninitialized_this); + __ mov(r0, Operand(this_var->name())); + __ Push(r0); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&uninitialized_this); + // Push the arguments ("left-to-right") on the stack. ZoneList* args = expr->arguments(); int arg_count = args->length(); @@ -3283,8 +3298,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) { RecordJSReturnSite(expr); - // TODO(dslomov): implement TDZ for `this`. - EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN); + EmitVariableAssignment(this_var, Token::INIT_CONST); context()->Plug(r0); } diff --git a/src/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc index 37329ab..726e984 100644 --- a/src/arm64/builtins-arm64.cc +++ b/src/arm64/builtins-arm64.cc @@ -706,6 +706,80 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { } +void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : number of arguments + // -- x1 : constructor function + // -- x2 : allocation site or undefined + // -- x3 : original constructor + // -- lr : return address + // -- sp[...]: constructor arguments + // ----------------------------------- + ASM_LOCATION("Builtins::Generate_JSConstructStubForDerived"); + + // TODO(dslomov): support pretenuring + CHECK(!FLAG_pretenuring_call_new); + + { + FrameScope frame_scope(masm, StackFrame::CONSTRUCT); + __ Mov(x4, x0); + __ SmiTag(x4); + __ LoadRoot(x10, Heap::kTheHoleValueRootIndex); + __ Push(x4, x10); + // sp[0]: number of arguments + // sp[1]: receiver (the hole) + + + // Set up pointer to last argument. + __ Add(x2, fp, StandardFrameConstants::kCallerSPOffset); + + // Copy arguments and receiver to the expression stack. + // Copy 2 values every loop to use ldp/stp. + // x0: number of arguments + // x1: constructor function + // x2: address of last argument (caller sp) + // jssp[0]: receiver + // jssp[1]: number of arguments (smi-tagged) + // Compute the start address of the copy in x4. + __ Add(x4, x2, Operand(x0, LSL, kPointerSizeLog2)); + Label loop, entry, done_copying_arguments; + __ B(&entry); + __ Bind(&loop); + __ Ldp(x10, x11, MemOperand(x4, -2 * kPointerSize, PreIndex)); + __ Push(x11, x10); + __ Bind(&entry); + __ Cmp(x4, x2); + __ B(gt, &loop); + // Because we copied values 2 by 2 we may have copied one extra value. + // Drop it if that is the case. + __ B(eq, &done_copying_arguments); + __ Drop(1); + __ Bind(&done_copying_arguments); + + // Call the function. + // x0: number of arguments + // x1: constructor function + ParameterCount actual(x0); + __ InvokeFunction(x1, actual, CALL_FUNCTION, NullCallWrapper()); + + + // Restore the context from the frame. + // x0: result + // jssp[0]: number of arguments (smi-tagged) + __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + + // Load number of arguments (smi). + __ Peek(x1, 0); + + // Leave construct frame + } + + __ DropBySMI(x1); + __ Drop(1); + __ Ret(); +} + + // Input: // x0: code entry. // x1: function. diff --git a/src/arm64/full-codegen-arm64.cc b/src/arm64/full-codegen-arm64.cc index 491e900..62b5451 100644 --- a/src/arm64/full-codegen-arm64.cc +++ b/src/arm64/full-codegen-arm64.cc @@ -1518,6 +1518,10 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { bool skip_init_check; if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { skip_init_check = false; + } else if (var->is_this()) { + CHECK((info_->shared_info()->kind() & kSubclassConstructor) != 0); + // TODO(dslomov): implement 'this' hole check elimination. + skip_init_check = false; } else { // Check that we always have valid source position. DCHECK(var->initializer_position() != RelocInfo::kNoPosition); @@ -2938,6 +2942,16 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) { EmitLoadSuperConstructor(super_ref); __ push(result_register()); + Variable* this_var = super_ref->this_var()->var(); + + GetVar(x0, this_var); + Label uninitialized_this; + __ JumpIfRoot(x0, Heap::kTheHoleValueRootIndex, &uninitialized_this); + __ Mov(x0, Operand(this_var->name())); + __ Push(x0); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&uninitialized_this); + // Push the arguments ("left-to-right") on the stack. ZoneList* args = expr->arguments(); int arg_count = args->length(); @@ -2972,8 +2986,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) { RecordJSReturnSite(expr); - // TODO(dslomov): implement TDZ for `this`. - EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN); + EmitVariableAssignment(this_var, Token::INIT_CONST); context()->Plug(x0); } diff --git a/src/ast.h b/src/ast.h index 02f7df6..ba1875c 100644 --- a/src/ast.h +++ b/src/ast.h @@ -2626,7 +2626,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/builtins.h b/src/builtins.h index 8e4149f..cfbb77d 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -67,6 +67,7 @@ enum BuiltinExtraArguments { V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \ V(InOptimizationQueue, BUILTIN, UNINITIALIZED, kNoExtraICState) \ V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, kNoExtraICState) \ + V(JSConstructStubForDerived, BUILTIN, UNINITIALIZED, kNoExtraICState) \ V(JSConstructStubApi, BUILTIN, UNINITIALIZED, kNoExtraICState) \ V(JSEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \ V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \ @@ -302,6 +303,7 @@ class Builtins { static void Generate_CompileOptimized(MacroAssembler* masm); static void Generate_CompileOptimizedConcurrent(MacroAssembler* masm); static void Generate_JSConstructStubGeneric(MacroAssembler* masm); + static void Generate_JSConstructStubForDerived(MacroAssembler* masm); static void Generate_JSConstructStubApi(MacroAssembler* masm); static void Generate_JSEntryTrampoline(MacroAssembler* masm); static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm); diff --git a/src/globals.h b/src/globals.h index 6cdd354..5bc0ec2 100644 --- a/src/globals.h +++ b/src/globals.h @@ -778,7 +778,8 @@ enum FunctionKind { kGeneratorFunction = 2, kConciseMethod = 4, kConciseGeneratorMethod = kGeneratorFunction | kConciseMethod, - kDefaultConstructor = 8 + kDefaultConstructor = 8, + kSubclassConstructor = 16 }; @@ -788,7 +789,8 @@ inline bool IsValidFunctionKind(FunctionKind kind) { kind == FunctionKind::kGeneratorFunction || kind == FunctionKind::kConciseMethod || kind == FunctionKind::kConciseGeneratorMethod || - kind == FunctionKind::kDefaultConstructor; + kind == FunctionKind::kDefaultConstructor || + kind == FunctionKind::kSubclassConstructor; } @@ -815,7 +817,10 @@ inline bool IsDefaultConstructor(FunctionKind kind) { return kind & FunctionKind::kDefaultConstructor; } - +inline bool IsSubclassConstructor(FunctionKind kind) { + DCHECK(IsValidFunctionKind(kind)); + return kind & FunctionKind::kSubclassConstructor; +} } } // namespace v8::internal namespace i = v8::internal; diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 675d862..18966c2 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -500,6 +500,57 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { } +void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax: number of arguments + // -- edi: constructor function + // -- ebx: allocation site or undefined + // -- edx: original constructor + // ----------------------------------- + + // TODO(dslomov): support pretenuring + CHECK(!FLAG_pretenuring_call_new); + + { + FrameScope frame_scope(masm, StackFrame::CONSTRUCT); + + // Preserve actual arguments count. + __ SmiTag(eax); + __ push(eax); + __ SmiUntag(eax); + + // receiver is the hole. + __ push(Immediate(masm->isolate()->factory()->the_hole_value())); + + // Set up pointer to last argument. + __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); + + // Copy arguments and receiver to the expression stack. + Label loop, entry; + __ mov(ecx, eax); + __ jmp(&entry); + __ bind(&loop); + __ push(Operand(ebx, ecx, times_4, 0)); + __ bind(&entry); + __ dec(ecx); + __ j(greater_equal, &loop); + + ParameterCount actual(eax); + __ InvokeFunction(edi, actual, CALL_FUNCTION, NullCallWrapper()); + + // Restore context from the frame. + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + + __ mov(ebx, Operand(esp, 0)); + } + + __ pop(ecx); // Return address. + __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); + __ push(ecx); + __ ret(0); +} + + static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, bool is_construct) { ProfileEntryHookStub::MaybeCallEntryHook(masm); diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 5a161e9..178c394 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -1461,6 +1461,10 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { bool skip_init_check; if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { skip_init_check = false; + } else if (var->is_this()) { + CHECK((info_->shared_info()->kind() & kSubclassConstructor) != 0); + // TODO(dslomov): implement 'this' hole check elimination. + skip_init_check = false; } else { // Check that we always have valid source position. DCHECK(var->initializer_position() != RelocInfo::kNoPosition); @@ -3133,6 +3137,15 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) { EmitLoadSuperConstructor(super_ref); __ push(result_register()); + Variable* this_var = super_ref->this_var()->var(); + GetVar(eax, this_var); + __ cmp(eax, isolate()->factory()->the_hole_value()); + Label uninitialized_this; + __ j(equal, &uninitialized_this); + __ push(Immediate(this_var->name())); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&uninitialized_this); + // Push the arguments ("left-to-right") on the stack. ZoneList* args = expr->arguments(); int arg_count = args->length(); @@ -3167,8 +3180,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) { RecordJSReturnSite(expr); - // TODO(dslomov): implement TDZ for `this`. - EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN); + EmitVariableAssignment(this_var, Token::INIT_CONST); context()->Plug(eax); } diff --git a/src/objects.h b/src/objects.h index d1e5acf..e5c55a9 100644 --- a/src/objects.h +++ b/src/objects.h @@ -7216,12 +7216,13 @@ class SharedFunctionInfo: public HeapObject { kIsGenerator, kIsConciseMethod, kIsDefaultConstructor, + kIsSubclassConstructor, kIsAsmFunction, kDeserialized, kCompilerHintsCount // Pseudo entry }; - 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 f16473c..1817944 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -263,12 +263,17 @@ void Parser::SetCachedData() { } -Scope* Parser::NewScope(Scope* parent, ScopeType scope_type) { +Scope* Parser::NewScope(Scope* parent, ScopeType scope_type, + FunctionKind kind) { DCHECK(ast_value_factory()); DCHECK(scope_type != MODULE_SCOPE || allow_harmony_modules()); + DCHECK((scope_type == FUNCTION_SCOPE && IsValidFunctionKind(kind)) || + kind == kNormalFunction); Scope* result = new (zone()) Scope(isolate(), zone(), parent, scope_type, ast_value_factory()); - result->Initialize(); + bool uninitialized_this = + FLAG_experimental_classes && IsSubclassConstructor(kind); + result->Initialize(uninitialized_this); return result; } @@ -281,7 +286,8 @@ FunctionLiteral* Parser::DefaultConstructor(bool call_super, Scope* scope, int parameter_count = 0; const AstRawString* name = ast_value_factory()->empty_string(); - Scope* function_scope = NewScope(scope, FUNCTION_SCOPE); + Scope* function_scope = + NewScope(scope, FUNCTION_SCOPE, FunctionKind::kDefaultConstructor); function_scope->SetStrictMode(STRICT); // Set start and end position to the same value function_scope->set_start_position(pos); @@ -3629,11 +3635,11 @@ FunctionLiteral* Parser::ParseFunctionLiteral( Scope* original_declaration_scope = original_scope_->DeclarationScope(); Scope* scope = function_type == FunctionLiteral::DECLARATION && - (!allow_harmony_scoping() || strict_mode() == SLOPPY) && - (original_scope_ == original_declaration_scope || - declaration_scope != original_declaration_scope) - ? NewScope(declaration_scope, FUNCTION_SCOPE) - : NewScope(scope_, FUNCTION_SCOPE); + (!allow_harmony_scoping() || strict_mode() == SLOPPY) && + (original_scope_ == original_declaration_scope || + declaration_scope != original_declaration_scope) + ? NewScope(declaration_scope, FUNCTION_SCOPE, kind) + : NewScope(scope_, FUNCTION_SCOPE, kind); ZoneList* body = NULL; int materialized_literal_count = -1; int expected_property_count = -1; @@ -4053,6 +4059,7 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name, bool has_seen_constructor = false; Expect(Token::LBRACE, CHECK_OK); + const bool has_extends = extends != nullptr; while (peek() != Token::RBRACE) { if (Check(Token::SEMICOLON)) continue; if (fni_ != NULL) fni_->Enter(); @@ -4061,8 +4068,8 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name, bool is_computed_name = false; // Classes do not care about computed // property names here. ObjectLiteral::Property* property = ParsePropertyDefinition( - &checker, in_class, is_static, &is_computed_name, &has_seen_constructor, - CHECK_OK); + &checker, in_class, has_extends, is_static, &is_computed_name, + &has_seen_constructor, CHECK_OK); if (has_seen_constructor && constructor == NULL) { constructor = GetPropertyValue(property)->AsFunctionLiteral(); diff --git a/src/parser.h b/src/parser.h index 23b12e1..15a9a0e 100644 --- a/src/parser.h +++ b/src/parser.h @@ -563,7 +563,8 @@ class ParserTraits { ZoneList* NewStatementList(int size, Zone* zone) { return new(zone) ZoneList(size, zone); } - V8_INLINE Scope* NewScope(Scope* parent_scope, ScopeType scope_type); + V8_INLINE Scope* NewScope(Scope* parent_scope, ScopeType scope_type, + FunctionKind kind = kNormalFunction); // Utility functions int DeclareArrowParametersFromExpression(Expression* expression, Scope* scope, @@ -852,7 +853,8 @@ class Parser : public ParserBase { // Factory methods. - Scope* NewScope(Scope* parent, ScopeType type); + Scope* NewScope(Scope* parent, ScopeType type, + FunctionKind kind = kNormalFunction); FunctionLiteral* DefaultConstructor(bool call_super, Scope* scope, int pos, int end_pos); @@ -913,8 +915,9 @@ bool ParserTraits::IsFutureStrictReserved( } -Scope* ParserTraits::NewScope(Scope* parent_scope, ScopeType scope_type) { - return parser_->NewScope(parent_scope, scope_type); +Scope* ParserTraits::NewScope(Scope* parent_scope, ScopeType scope_type, + FunctionKind kind) { + return parser_->NewScope(parent_scope, scope_type, kind); } diff --git a/src/preparser.cc b/src/preparser.cc index 6cf0f1d..68d9109 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -1004,7 +1004,8 @@ PreParserExpression PreParser::ParseClassLiteral( scope_->SetStrictMode(STRICT); scope_->SetScopeName(name); - if (Check(Token::EXTENDS)) { + bool has_extends = Check(Token::EXTENDS); + if (has_extends) { ParseLeftHandSideExpression(CHECK_OK); } @@ -1018,8 +1019,8 @@ PreParserExpression PreParser::ParseClassLiteral( const bool is_static = false; bool is_computed_name = false; // Classes do not care about computed // property names here. - ParsePropertyDefinition(&checker, in_class, is_static, &is_computed_name, - &has_seen_constructor, CHECK_OK); + ParsePropertyDefinition(&checker, in_class, has_extends, is_static, + &is_computed_name, &has_seen_constructor, CHECK_OK); } Expect(Token::RBRACE, CHECK_OK); diff --git a/src/preparser.h b/src/preparser.h index 67a559c..9faaca1 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -520,8 +520,9 @@ class ParserBase : public Traits { bool* ok); ExpressionT ParseObjectLiteral(bool* ok); ObjectLiteralPropertyT ParsePropertyDefinition( - ObjectLiteralCheckerBase* checker, bool in_class, bool is_static, - bool* is_computed_name, bool* has_seen_constructor, bool* ok); + ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends, + bool is_static, bool* is_computed_name, bool* has_seen_constructor, + bool* ok); typename Traits::Type::ExpressionList ParseArguments(bool* ok); ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok); ExpressionT ParseYieldExpression(bool* ok); @@ -1315,7 +1316,8 @@ class PreParserTraits { const char* type, Handle arg, int pos) { return PreParserExpression::Default(); } - PreParserScope NewScope(PreParserScope* outer_scope, ScopeType scope_type) { + PreParserScope NewScope(PreParserScope* outer_scope, ScopeType scope_type, + FunctionKind kind = kNormalFunction) { return PreParserScope(outer_scope, scope_type); } @@ -2082,7 +2084,8 @@ typename ParserBase::ExpressionT ParserBase::ParsePropertyName( template typename ParserBase::ObjectLiteralPropertyT ParserBase::ParsePropertyDefinition(ObjectLiteralCheckerBase* checker, - bool in_class, bool is_static, + bool in_class, bool has_extends, + bool is_static, bool* is_computed_name, bool* has_seen_constructor, bool* ok) { @@ -2129,7 +2132,8 @@ ParserBase::ParsePropertyDefinition(ObjectLiteralCheckerBase* checker, if (in_class && !is_static && this->IsConstructor(name)) { *has_seen_constructor = true; - kind = FunctionKind::kNormalFunction; + kind = has_extends ? FunctionKind::kSubclassConstructor + : FunctionKind::kNormalFunction; } value = this->ParseFunctionLiteral( @@ -2145,9 +2149,8 @@ ParserBase::ParsePropertyDefinition(ObjectLiteralCheckerBase* checker, } else if (in_class && name_is_static && !is_static) { // static MethodDefinition - return ParsePropertyDefinition(checker, true, true, is_computed_name, - nullptr, ok); - + return ParsePropertyDefinition(checker, true, has_extends, true, + is_computed_name, nullptr, ok); } else if (is_get || is_set) { // Accessor name = this->EmptyIdentifier(); @@ -2227,9 +2230,11 @@ typename ParserBase::ExpressionT ParserBase::ParseObjectLiteral( const bool in_class = false; const bool is_static = false; + const bool has_extends = false; bool is_computed_name = false; ObjectLiteralPropertyT property = this->ParsePropertyDefinition( - &checker, in_class, is_static, &is_computed_name, nullptr, CHECK_OK); + &checker, in_class, has_extends, is_static, &is_computed_name, NULL, + CHECK_OK); if (is_computed_name) { has_computed_names = true; diff --git a/src/runtime/runtime-classes.cc b/src/runtime/runtime-classes.cc index 00567d4..846fd1a 100644 --- a/src/runtime/runtime-classes.cc +++ b/src/runtime/runtime-classes.cc @@ -107,6 +107,13 @@ RUNTIME_FUNCTION(Runtime_DefineClass) { : isolate->factory()->empty_string(); constructor->shared()->set_name(*name_string); + if (FLAG_experimental_classes) { + if (!super_class->IsTheHole() && !super_class->IsNull()) { + Handle stub(isolate->builtins()->JSConstructStubForDerived()); + constructor->shared()->set_construct_stub(*stub); + } + } + JSFunction::SetPrototype(constructor, prototype); PropertyAttributes attribs = static_cast(DONT_ENUM | DONT_DELETE | READ_ONLY); diff --git a/src/scopes.cc b/src/scopes.cc index 80bf82b..1553431 100644 --- a/src/scopes.cc +++ b/src/scopes.cc @@ -291,7 +291,7 @@ bool Scope::Analyze(CompilationInfo* info) { } -void Scope::Initialize() { +void Scope::Initialize(bool uninitialized_this) { DCHECK(!already_resolved()); // Add this scope as a new inner scope of the outer scope. @@ -311,13 +311,12 @@ void Scope::Initialize() { // such parameter is 'this' which is passed on the stack when // invoking scripts if (is_declaration_scope()) { - Variable* var = - variables_.Declare(this, - ast_value_factory_->this_string(), - VAR, - false, - Variable::THIS, - kCreatedInitialized); + DCHECK(!uninitialized_this || is_function_scope()); + DCHECK(FLAG_experimental_classes || !uninitialized_this); + Variable* var = variables_.Declare( + this, ast_value_factory_->this_string(), + uninitialized_this ? CONST : VAR, false, Variable::THIS, + uninitialized_this ? kNeedsInitialization : kCreatedInitialized); var->AllocateTo(Variable::PARAMETER, -1); receiver_ = var; } else { diff --git a/src/scopes.h b/src/scopes.h index 2e418be..9cb7e65 100644 --- a/src/scopes.h +++ b/src/scopes.h @@ -88,7 +88,7 @@ class Scope: public ZoneObject { scope_name_ = scope_name; } - void Initialize(); + void Initialize(bool uninitialized_this = false); // Checks if the block scope is redundant, i.e. it does not contain any // block scoped declarations. In that case it is removed from the scope diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index ff0a349..80a41dd 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -498,6 +498,59 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { } +void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- rax: number of arguments + // -- rdi: constructor function + // -- rbx: allocation site or undefined + // -- rdx: original constructor + // ----------------------------------- + // TODO(dslomov): support pretenuring + CHECK(!FLAG_pretenuring_call_new); + + { + FrameScope frame_scope(masm, StackFrame::CONSTRUCT); + + // Store a smi-tagged arguments count on the stack. + __ Integer32ToSmi(rax, rax); + __ Push(rax); + __ SmiToInteger32(rax, rax); + + // receiver is the hole. + __ Push(masm->isolate()->factory()->the_hole_value()); + + // Set up pointer to last argument. + __ leap(rbx, Operand(rbp, StandardFrameConstants::kCallerSPOffset)); + + // Copy arguments and receiver to the expression stack. + Label loop, entry; + __ movp(rcx, rax); + __ jmp(&entry); + __ bind(&loop); + __ Push(Operand(rbx, rcx, times_pointer_size, 0)); + __ bind(&entry); + __ decp(rcx); + __ j(greater_equal, &loop); + + // Call the function. + ParameterCount actual(rax); + __ InvokeFunction(rdi, actual, CALL_FUNCTION, NullCallWrapper()); + + // Restore context from the frame. + __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + + __ movp(rbx, Operand(rsp, 0)); // Get arguments count. + } // Leave construct frame. + + // Remove caller arguments from the stack and return. + __ PopReturnAddressTo(rcx); + SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2); + __ leap(rsp, Operand(rsp, index.reg, index.scale, 1 * kPointerSize)); + __ PushReturnAddressFrom(rcx); + __ ret(0); +} + + static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, bool is_construct) { ProfileEntryHookStub::MaybeCallEntryHook(masm); diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 3a93117..6c77274 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -1496,6 +1496,10 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { bool skip_init_check; if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { skip_init_check = false; + } else if (var->is_this()) { + CHECK((info_->shared_info()->kind() & kSubclassConstructor) != 0); + // TODO(dslomov): implement 'this' hole check elimination. + skip_init_check = false; } else { // Check that we always have valid source position. DCHECK(var->initializer_position() != RelocInfo::kNoPosition); @@ -3138,6 +3142,17 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) { EmitLoadSuperConstructor(super_ref); __ Push(result_register()); + Variable* this_var = super_ref->this_var()->var(); + + GetVar(rax, this_var); + __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); + Label uninitialized_this; + __ j(equal, &uninitialized_this); + __ Push(this_var->name()); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&uninitialized_this); + + // Push the arguments ("left-to-right") on the stack. ZoneList* args = expr->arguments(); int arg_count = args->length(); @@ -3172,8 +3187,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) { RecordJSReturnSite(expr); - // TODO(dslomov): implement TDZ for `this`. - EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN); + EmitVariableAssignment(this_var, Token::INIT_CONST); context()->Plug(rax); } diff --git a/test/mjsunit/harmony/classes-experimental.js b/test/mjsunit/harmony/classes-experimental.js index 73944b1..5a3c3e5 100644 --- a/test/mjsunit/harmony/classes-experimental.js +++ b/test/mjsunit/harmony/classes-experimental.js @@ -7,29 +7,51 @@ 'use strict'; class Base { - constructor() { + constructor(a, b) { let o = new Object(); - o.prp = 1; + o.prp = a + b; return o; } } class Subclass extends Base { - constructor() { + constructor(a, b) { + var exn; try { this.prp1 = 3; } catch (e) { - // TODO(dslomov): actually test the exception once TDZ is implemented. + exn = e; } - super(); - assertSame(1, this.prp); + assertTrue(exn instanceof ReferenceError); + super(a, b); + assertSame(a + b, this.prp); assertSame(undefined, this.prp1); assertFalse(this.hasOwnProperty("prp1")); return this; } } -let s = new Subclass(); +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() { + super(1,2); + + let called = false; + function tmp() { called = true; return 3; } + var exn = null; + try { + super(tmp(),4); + } catch(e) { exn = e; } + assertTrue(exn !== null); + assertFalse(called); + } +} + +new Subclass2(); -- 2.7.4