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}
This commit is contained in:
dslomov 2015-02-03 09:42:41 -08:00 committed by Commit bot
parent 8c652127f7
commit 6f97a4948f
20 changed files with 402 additions and 55 deletions

View File

@ -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

View File

@ -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<Expression*>* 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);
}

View File

@ -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.

View File

@ -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<Expression*>* 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);
}

View File

@ -2626,7 +2626,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, 4> {};
class FunctionKindBits : public BitField<FunctionKind, 6, 5> {};
};

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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<Expression*>* 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);
}

View File

@ -7216,12 +7216,13 @@ class SharedFunctionInfo: public HeapObject {
kIsGenerator,
kIsConciseMethod,
kIsDefaultConstructor,
kIsSubclassConstructor,
kIsAsmFunction,
kDeserialized,
kCompilerHintsCount // Pseudo entry
};
class FunctionKindBits : public BitField<FunctionKind, kIsArrow, 4> {};
class FunctionKindBits : public BitField<FunctionKind, kIsArrow, 5> {};
class DeoptCountBits : public BitField<int, 0, 4> {};
class OptReenableTriesBits : public BitField<int, 4, 18> {};

View File

@ -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<Statement*>* 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();

View File

@ -563,7 +563,8 @@ class ParserTraits {
ZoneList<v8::internal::Statement*>* NewStatementList(int size, Zone* zone) {
return new(zone) ZoneList<v8::internal::Statement*>(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<ParserTraits> {
// 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);
}

View File

@ -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);

View File

@ -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<Object> 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<Traits>::ExpressionT ParserBase<Traits>::ParsePropertyName(
template <class Traits>
typename ParserBase<Traits>::ObjectLiteralPropertyT
ParserBase<Traits>::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<Traits>::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<Traits>::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<Traits>::ExpressionT ParserBase<Traits>::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;

View File

@ -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<Code> stub(isolate->builtins()->JSConstructStubForDerived());
constructor->shared()->set_construct_stub(*stub);
}
}
JSFunction::SetPrototype(constructor, prototype);
PropertyAttributes attribs =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);

View File

@ -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 {

View File

@ -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

View File

@ -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);

View File

@ -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<Expression*>* 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);
}

View File

@ -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();