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:
parent
8c652127f7
commit
6f97a4948f
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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> {};
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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> {};
|
||||
|
@ -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();
|
||||
|
11
src/parser.h
11
src/parser.h
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user