Isolate* isolate = inlined_function->GetIsolate();
Factory* factory = isolate->factory();
SlotRefValueBuilder slot_refs(
- frame,
- inlined_frame_index,
- inlined_function->shared()->formal_parameter_count());
+ frame, inlined_frame_index,
+ inlined_function->shared()->internal_formal_parameter_count());
int args_count = slot_refs.args_length();
Handle<JSObject> arguments =
__ SmiTag(r4);
__ push(r4); // Smi-tagged arguments count.
+ // Push new.target.
+ __ push(r3);
+
// receiver is the hole.
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ push(ip);
// r2: address of last argument (caller sp)
// r4: number of arguments (smi-tagged)
// sp[0]: receiver
- // sp[1]: number of arguments (smi-tagged)
+ // sp[1]: new.target
+ // sp[2]: number of arguments (smi-tagged)
Label loop, entry;
__ b(&entry);
__ bind(&loop);
// Call the function.
// r0: number of arguments
// r1: constructor function
+ __ add(r0, r0, Operand(1));
ParameterCount actual(r0);
__ InvokeFunction(r1, actual, CALL_FUNCTION, NullCallWrapper());
void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+ CHECK(!has_new_target());
// The displacement is the offset of the last parameter (if any)
// relative to the frame pointer.
const int kDisplacement =
// sp[4] : receiver displacement
// sp[8] : function
+ CHECK(!has_new_target());
+
// Check if the calling frame is an arguments adaptor frame.
Label runtime;
__ ldr(r3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
// r6 : allocated object (tagged)
// r9 : mapped parameter count (tagged)
+ CHECK(!has_new_target());
+
__ ldr(r1, MemOperand(sp, 0 * kPointerSize));
// r1 = parameter count (tagged)
// Patch the arguments.length and the parameters pointer.
__ bind(&adaptor_frame);
__ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ if (has_new_target()) {
+ // Subtract 1 from smi-tagged arguments count.
+ __ sub(r1, r1, Operand(2));
+ }
__ str(r1, MemOperand(sp, 0));
__ add(r3, r2, Operand::PointerOffsetFromSmiKey(r1));
__ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
}
// Pass function as original constructor.
- __ mov(r3, r1);
+ if (IsSuperConstructorCall()) {
+ __ mov(r4, Operand(1 * kPointerSize));
+ __ add(r4, r4, Operand(r0, LSL, kPointerSizeLog2));
+ __ ldr(r3, MemOperand(sp, r4));
+ } else {
+ __ mov(r3, r1);
+ }
// Jump to the function-specific construct stub.
Register jmp_reg = r4;
// function, receiver address, parameter count.
// The stub will rewrite receiever and parameter count if the previous
// stack frame was an arguments adapter frame.
+ ArgumentsAccessStub::HasNewTarget has_new_target =
+ IsSubclassConstructor(info->function()->kind())
+ ? ArgumentsAccessStub::HAS_NEW_TARGET
+ : ArgumentsAccessStub::NO_NEW_TARGET;
ArgumentsAccessStub::Type type;
if (is_strict(language_mode())) {
type = ArgumentsAccessStub::NEW_STRICT;
} else {
type = ArgumentsAccessStub::NEW_SLOPPY_FAST;
}
- ArgumentsAccessStub stub(isolate(), type);
+ ArgumentsAccessStub stub(isolate(), type, has_new_target);
__ CallStub(&stub);
SetVar(arguments, r0, r1, r2);
// Make sure that the constant pool is not emitted inside of the return
// sequence.
{ Assembler::BlockConstPoolScope block_const_pool(masm_);
- int32_t sp_delta = (info_->scope()->num_parameters() + 1) * kPointerSize;
+ int32_t arg_count = info_->scope()->num_parameters() + 1;
+ if (FLAG_experimental_classes &&
+ IsSubclassConstructor(info_->function()->kind())) {
+ arg_count++;
+ }
+ int32_t sp_delta = arg_count * kPointerSize;
CodeGenerator::RecordPositions(masm_, function()->end_position() - 1);
// TODO(svenpanne) The code below is sometimes 4 words, sometimes 5!
PredictableCodeSizeScope predictable(masm_, -1);
void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+ Comment cmnt(masm_, "[ SuperConstructorCall");
+ Variable* new_target_var = scope()->DeclarationScope()->new_target_var();
+ GetVar(result_register(), new_target_var);
+ __ Push(result_register());
+
SuperReference* super_ref = expr->expression()->AsSuperReference();
EmitLoadSuperConstructor(super_ref);
__ push(result_register());
__ Move(r2, FeedbackVector());
__ mov(r3, Operand(SmiFromSlot(expr->CallFeedbackSlot())));
- // TODO(dslomov): use a different stub and propagate new.target.
- CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
+ CallConstructStub stub(isolate(), SUPER_CALL_RECORD_TARGET);
__ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
+ __ Drop(1);
+
RecordJSReturnSite(expr);
EmitVariableAssignment(this_var, Token::INIT_CONST);
__ Mov(x4, x0);
__ SmiTag(x4);
__ LoadRoot(x10, Heap::kTheHoleValueRootIndex);
- __ Push(x4, x10);
+ __ Push(x4, x3, x10);
// sp[0]: number of arguments
- // sp[1]: receiver (the hole)
+ // sp[1]: new.target
+ // sp[2]: receiver (the hole)
// Set up pointer to last argument.
// x1: constructor function
// x2: address of last argument (caller sp)
// jssp[0]: receiver
- // jssp[1]: number of arguments (smi-tagged)
+ // jssp[1]: new.target
+ // jssp[2]: 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;
// Call the function.
// x0: number of arguments
// x1: constructor function
+ __ Add(x0, x0, Operand(1)); // new.target
ParameterCount actual(x0);
__ InvokeFunction(x1, actual, CALL_FUNCTION, NullCallWrapper());
void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+ CHECK(!has_new_target());
Register arg_count = ArgumentsAccessReadDescriptor::parameter_count();
Register key = ArgumentsAccessReadDescriptor::index();
DCHECK(arg_count.is(x0));
// jssp[8]: address of receiver argument
// jssp[16]: function
+ CHECK(!has_new_target());
+
// Check if the calling frame is an arguments adaptor frame.
Label runtime;
Register caller_fp = x10;
//
// Returns pointer to result object in x0.
+ CHECK(!has_new_target());
+
// Note: arg_count_smi is an alias of param_count_smi.
Register arg_count_smi = x3;
Register param_count_smi = x3;
MemOperand(caller_fp,
ArgumentsAdaptorFrameConstants::kLengthOffset));
__ SmiUntag(param_count, param_count_smi);
+ if (has_new_target()) {
+ // Skip new.target: it is not a part of arguments.
+ __ Sub(param_count, param_count, Operand(1));
+ __ SmiTag(param_count_smi, param_count);
+ }
__ Add(x10, caller_fp, Operand(param_count, LSL, kPointerSizeLog2));
__ Add(params, x10, StandardFrameConstants::kCallerSPOffset);
__ AssertUndefinedOrAllocationSite(x2, x5);
}
- __ Mov(x3, function);
+ if (IsSuperConstructorCall()) {
+ __ Mov(x4, Operand(1 * kPointerSize));
+ __ Add(x4, x4, Operand(x0, LSL, kPointerSizeLog2));
+ __ Peek(x3, x4);
+ } else {
+ __ Mov(x3, function);
+ }
// Jump to the function-specific construct stub.
Register jump_reg = x4;
// function, receiver address, parameter count.
// The stub will rewrite receiver and parameter count if the previous
// stack frame was an arguments adapter frame.
+ ArgumentsAccessStub::HasNewTarget has_new_target =
+ IsSubclassConstructor(info->function()->kind())
+ ? ArgumentsAccessStub::HAS_NEW_TARGET
+ : ArgumentsAccessStub::NO_NEW_TARGET;
ArgumentsAccessStub::Type type;
if (is_strict(language_mode())) {
type = ArgumentsAccessStub::NEW_STRICT;
} else {
type = ArgumentsAccessStub::NEW_SLOPPY_FAST;
}
- ArgumentsAccessStub stub(isolate(), type);
+ ArgumentsAccessStub stub(isolate(), type, has_new_target);
__ CallStub(&stub);
SetVar(arguments, x0, x1, x2);
__ ldr_pcrel(ip0, (3 * kInstructionSize) >> kLoadLiteralScaleLog2);
__ add(current_sp, current_sp, ip0);
__ ret();
- __ dc64(kXRegSize * (info_->scope()->num_parameters() + 1));
+ int32_t arg_count = info_->scope()->num_parameters() + 1;
+ if (FLAG_experimental_classes &&
+ IsSubclassConstructor(info_->function()->kind())) {
+ arg_count++;
+ }
+ __ dc64(kXRegSize * arg_count);
info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
}
}
void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+ Comment cmnt(masm_, "[ SuperConstructorCall");
+ Variable* new_target_var = scope()->DeclarationScope()->new_target_var();
+ GetVar(result_register(), new_target_var);
+ __ Push(result_register());
+
SuperReference* super_ref = expr->expression()->AsSuperReference();
EmitLoadSuperConstructor(super_ref);
__ push(result_register());
__ LoadObject(x2, FeedbackVector());
__ Mov(x3, SmiFromSlot(expr->CallFeedbackSlot()));
- // TODO(dslomov): use a different stub and propagate new.target.
- CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
+ CallConstructStub stub(isolate(), SUPER_CALL_RECORD_TARGET);
__ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
+ __ Drop(1);
+
RecordJSReturnSite(expr);
EmitVariableAssignment(this_var, Token::INIT_CONST);
F(get_template_callsite, "GetTemplateCallSite") \
F(initialize_const_global, "initializeConstGlobal") \
F(initialize_var_global, "initializeVarGlobal") \
+ F(is_construct_call, "_IsConstructCall") \
F(let, "let") \
F(make_reference_error, "MakeReferenceErrorEmbedded") \
F(make_syntax_error, "MakeSyntaxErrorEmbedded") \
F(make_type_error, "MakeTypeErrorEmbedded") \
F(native, "native") \
+ F(new_target, "new.target") \
F(next, "next") \
F(proto, "__proto__") \
F(prototype, "prototype") \
F(use_asm, "use asm") \
F(use_strong, "use strong") \
F(use_strict, "use strict") \
- F(value, "value") \
- F(is_construct_call, "_IsConstructCall")
+ F(value, "value")
#define OTHER_CONSTANTS(F) \
F(true_value) \
DCHECK(call->is_compiled());
// Set the expected parameters for apply to 2; required by builtin.
- apply->shared()->set_formal_parameter_count(2);
+ apply->shared()->set_internal_formal_parameter_count(2);
// Set the lengths for the functions to satisfy ECMA-262.
call->shared()->set_length(1);
NEW_STRICT
};
- ArgumentsAccessStub(Isolate* isolate, Type type) : PlatformCodeStub(isolate) {
- minor_key_ = TypeBits::encode(type);
+ enum HasNewTarget { NO_NEW_TARGET, HAS_NEW_TARGET };
+
+ ArgumentsAccessStub(Isolate* isolate, Type type,
+ HasNewTarget has_new_target = NO_NEW_TARGET)
+ : PlatformCodeStub(isolate) {
+ minor_key_ =
+ TypeBits::encode(type) | HasNewTargetBits::encode(has_new_target);
}
CallInterfaceDescriptor GetCallInterfaceDescriptor() OVERRIDE {
private:
Type type() const { return TypeBits::decode(minor_key_); }
+ bool has_new_target() const {
+ return HasNewTargetBits::decode(minor_key_) == HAS_NEW_TARGET;
+ }
void GenerateReadElement(MacroAssembler* masm);
void GenerateNewStrict(MacroAssembler* masm);
void PrintName(std::ostream& os) const OVERRIDE; // NOLINT
class TypeBits : public BitField<Type, 0, 2> {};
+ class HasNewTargetBits : public BitField<HasNewTarget, 2, 1> {};
DEFINE_PLATFORM_CODE_STUB(ArgumentsAccess, PlatformCodeStub);
};
return (flags() & RECORD_CONSTRUCTOR_TARGET) != 0;
}
+ bool IsSuperConstructorCall() const {
+ return (flags() & SUPER_CONSTRUCTOR_CALL) != 0;
+ }
+
void PrintName(std::ostream& os) const OVERRIDE; // NOLINT
- class FlagBits : public BitField<CallConstructorFlags, 0, 1> {};
+ class FlagBits : public BitField<CallConstructorFlags, 0, 2> {};
DEFINE_CALL_INTERFACE_DESCRIPTOR(CallConstruct);
DEFINE_PLATFORM_CODE_STUB(CallConstruct, PlatformCodeStub);
explicit ParameterCount(int immediate)
: reg_(no_reg), immediate_(immediate) { }
explicit ParameterCount(Handle<JSFunction> f)
- : reg_(no_reg), immediate_(f->shared()->formal_parameter_count()) { }
+ : reg_(no_reg),
+ immediate_(f->shared()->internal_formal_parameter_count()) {}
bool is_reg() const { return !reg_.is(no_reg); }
bool is_immediate() const { return !is_reg(); }
bool is_toplevel,
Handle<Script> script) {
function_info->set_length(lit->parameter_count());
- function_info->set_formal_parameter_count(lit->parameter_count());
+ if (FLAG_experimental_classes && IsSubclassConstructor(lit->kind())) {
+ function_info->set_internal_formal_parameter_count(lit->parameter_count() +
+ 1);
+ } else {
+ function_info->set_internal_formal_parameter_count(lit->parameter_count());
+ }
function_info->set_script(*script);
function_info->set_function_token_position(lit->function_token_position());
function_info->set_start_position(lit->start_position());
Handle<Object> func = function_const.Value().handle();
if (!func->IsJSFunction()) return false; // not a function.
Handle<JSFunction> function = Handle<JSFunction>::cast(func);
- if (arg_count != function->shared()->formal_parameter_count()) return false;
+ if (arg_count != function->shared()->internal_formal_parameter_count()) {
+ return false;
+ }
// Check the receiver doesn't need to be wrapped.
Node* receiver = node->InputAt(1);
// plus the receiver.
SharedFunctionInfo* shared = info->closure()->shared();
return GetJSCallDescriptor(zone, info->is_osr(),
- 1 + shared->formal_parameter_count(),
+ 1 + shared->internal_formal_parameter_count(),
CallDescriptor::kNoFlags);
}
if (info->code_stub() != NULL) {
return Handle<Code>::null();
}
+ // TODO(dslomov): support turbo optimization of subclass constructors.
+ if (IsSubclassConstructor(shared->kind())) {
+ shared->DisableOptimization(kSuperReference);
+ return Handle<Code>::null();
+ }
+
ZonePool zone_pool;
SmartPointer<PipelineStatistics> pipeline_statistics;
output_frame->SetTop(top_address);
// Compute the incoming parameter translation.
- int parameter_count = function->shared()->formal_parameter_count() + 1;
+ int parameter_count =
+ function->shared()->internal_formal_parameter_count() + 1;
unsigned output_offset = output_frame_size;
unsigned input_offset = input_frame_size;
for (int i = 0; i < parameter_count; ++i) {
CHECK_EQ(Smi::cast(function), Smi::FromInt(StackFrame::STUB));
return 0;
}
- unsigned arguments = function->shared()->formal_parameter_count() + 1;
+ unsigned arguments =
+ function->shared()->internal_formal_parameter_count() + 1;
return arguments * kPointerSize;
}
int FrameDescription::ComputeParametersCount() {
switch (type_) {
case StackFrame::JAVA_SCRIPT:
- return function_->shared()->formal_parameter_count();
+ return function_->shared()->internal_formal_parameter_count();
case StackFrame::ARGUMENTS_ADAPTOR: {
// Last slot contains number of incomming arguments as a smi.
// Can't use GetExpression(0) because it would cause infinite recursion.
// Set integer fields (smi or int, depending on the architecture).
share->set_length(0);
- share->set_formal_parameter_count(0);
+ share->set_internal_formal_parameter_count(0);
share->set_expected_nof_properties(0);
share->set_num_literals(0);
share->set_start_position_and_type(0);
DCHECK(can_access_heap_objects() &&
isolate()->heap()->gc_state() == Heap::NOT_IN_GC);
- return function()->shared()->formal_parameter_count();
+ return function()->shared()->internal_formal_parameter_count();
}
int actual = ComputeParametersCount();
int expected = -1;
JSFunction* function = this->function();
- expected = function->shared()->formal_parameter_count();
+ expected = function->shared()->internal_formal_parameter_count();
PrintIndex(accumulator, mode, index);
accumulator->Add("arguments adaptor frame: %d->%d", actual, expected);
// Copy the function data to the shared function info.
shared->set_function_data(fun->shared()->function_data());
- int parameters = fun->shared()->formal_parameter_count();
- shared->set_formal_parameter_count(parameters);
+ int parameters = fun->shared()->internal_formal_parameter_count();
+ shared->set_internal_formal_parameter_count(parameters);
EmitNewClosure(shared, false);
}
enum CallConstructorFlags {
- NO_CALL_CONSTRUCTOR_FLAGS,
+ NO_CALL_CONSTRUCTOR_FLAGS = 0,
// The call target is cached in the instruction stream.
- RECORD_CONSTRUCTOR_TARGET
+ RECORD_CONSTRUCTOR_TARGET = 1,
+ SUPER_CONSTRUCTOR_CALL = 1 << 1,
+ SUPER_CALL_RECORD_TARGET = SUPER_CONSTRUCTOR_CALL | RECORD_CONSTRUCTOR_TARGET
};
int argument_count)
: HBinaryCall(context, function, argument_count),
known_function_(known_function) {
- formal_parameter_count_ = known_function.is_null()
- ? 0 : known_function->shared()->formal_parameter_count();
+ formal_parameter_count_ =
+ known_function.is_null()
+ ? 0
+ : known_function->shared()->internal_formal_parameter_count();
has_stack_check_ = !known_function.is_null() &&
(known_function->code()->kind() == Code::FUNCTION ||
known_function->code()->kind() == Code::OPTIMIZED_FUNCTION);
bool HOptimizedGraphBuilder::BuildGraph() {
+ if (IsSubclassConstructor(current_info()->function()->kind())) {
+ Bailout(kSuperReference);
+ return false;
+ }
+
Scope* scope = current_info()->scope();
SetUpScope(scope);
HValue* target = Add<HConstant>(jsfun);
// For constant functions, we try to avoid calling the
// argument adaptor and instead call the function directly
- int formal_parameter_count = jsfun->shared()->formal_parameter_count();
+ int formal_parameter_count =
+ jsfun->shared()->internal_formal_parameter_count();
bool dont_adapt_arguments =
(formal_parameter_count ==
SharedFunctionInfo::kDontAdaptArgumentsSentinel);
__ push(eax);
__ SmiUntag(eax);
+ // Push new.target.
+ __ push(edx);
+
// receiver is the hole.
__ push(Immediate(masm->isolate()->factory()->the_hole_value()));
__ dec(ecx);
__ j(greater_equal, &loop);
+ __ inc(eax); // Pushed new.target.
ParameterCount actual(eax);
__ InvokeFunction(edi, actual, CALL_FUNCTION, NullCallWrapper());
void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+ CHECK(!has_new_target());
// The key is in edx and the parameter count is in eax.
DCHECK(edx.is(ArgumentsAccessReadDescriptor::index()));
DCHECK(eax.is(ArgumentsAccessReadDescriptor::parameter_count()));
// esp[8] : receiver displacement
// esp[12] : function
+ CHECK(!has_new_target());
+
// Check if the calling frame is an arguments adaptor frame.
Label runtime;
__ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
// ebx = parameter count (tagged)
__ mov(ebx, Operand(esp, 1 * kPointerSize));
+ CHECK(!has_new_target());
+
// Check if the calling frame is an arguments adaptor frame.
// TODO(rossberg): Factor out some of the bits that are shared with the other
// Generate* functions.
// Patch the arguments.length and the parameters pointer.
__ bind(&adaptor_frame);
__ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
- __ mov(Operand(esp, 1 * kPointerSize), ecx);
+
+ if (has_new_target()) {
+ // Subtract 1 from smi-tagged arguments count.
+ __ sub(ecx, Immediate(2));
+ }
+
__ lea(edx, Operand(edx, ecx, times_2,
StandardFrameConstants::kCallerSPOffset));
+ __ mov(Operand(esp, 1 * kPointerSize), ecx);
__ mov(Operand(esp, 2 * kPointerSize), edx);
// Try the new space allocation. Start out with computing the size of
__ AssertUndefinedOrAllocationSite(ebx);
}
- // Pass original constructor to construct stub.
- __ mov(edx, edi);
+ if (IsSuperConstructorCall()) {
+ __ mov(edx, Operand(esp, eax, times_pointer_size, 2 * kPointerSize));
+ } else {
+ // Pass original constructor to construct stub.
+ __ mov(edx, edi);
+ }
// Jump to the function-specific construct stub.
Register jmp_reg = ecx;
bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
- int parameter_count = function->shared()->formal_parameter_count() + 1;
+ int parameter_count =
+ function->shared()->internal_formal_parameter_count() + 1;
unsigned input_frame_size = input_->GetFrameSize();
unsigned alignment_state_offset =
input_frame_size - parameter_count * kPointerSize -
} else {
type = ArgumentsAccessStub::NEW_SLOPPY_FAST;
}
- ArgumentsAccessStub stub(isolate(), type);
+ ArgumentsAccessStub::HasNewTarget has_new_target =
+ IsSubclassConstructor(info->function()->kind())
+ ? ArgumentsAccessStub::HAS_NEW_TARGET
+ : ArgumentsAccessStub::NO_NEW_TARGET;
+ ArgumentsAccessStub stub(isolate(), type, has_new_target);
__ CallStub(&stub);
SetVar(arguments, eax, ebx, edx);
int no_frame_start = masm_->pc_offset();
__ pop(ebp);
- int arguments_bytes = (info_->scope()->num_parameters() + 1) * kPointerSize;
+ int arg_count = info_->scope()->num_parameters() + 1;
+ if (FLAG_experimental_classes &&
+ IsSubclassConstructor(info_->function()->kind())) {
+ arg_count++;
+ }
+ int arguments_bytes = arg_count * kPointerSize;
__ Ret(arguments_bytes, ecx);
// Check that the size of the code used for returning is large enough
// for the debugger's requirements.
void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+ Variable* new_target_var = scope()->DeclarationScope()->new_target_var();
+ GetVar(eax, new_target_var);
+ __ push(eax);
+
SuperReference* super_ref = expr->expression()->AsSuperReference();
EmitLoadSuperConstructor(super_ref);
__ push(result_register());
__ LoadHeapObject(ebx, FeedbackVector());
__ mov(edx, Immediate(SmiFromSlot(expr->CallFeedbackSlot())));
- // TODO(dslomov): use a different stub and propagate new.target.
- CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
+ CallConstructStub stub(isolate(), SUPER_CALL_RECORD_TARGET);
__ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
+ __ Drop(1);
+
RecordJSReturnSite(expr);
EmitVariableAssignment(this_var, Token::INIT_CONST);
return compiler.CompileLoadCallback(lookup->name(), call_optimization,
lookup->GetAccessorIndex());
}
- int expected_arguments = function->shared()->formal_parameter_count();
+ int expected_arguments =
+ function->shared()->internal_formal_parameter_count();
return compiler.CompileLoadViaGetter(
lookup->name(), lookup->GetAccessorIndex(), expected_arguments);
}
call_optimization,
lookup->GetAccessorIndex());
}
- int expected_arguments = function->shared()->formal_parameter_count();
+ int expected_arguments =
+ function->shared()->internal_formal_parameter_count();
return compiler.CompileStoreViaSetter(receiver, lookup->name(),
lookup->GetAccessorIndex(),
expected_arguments);
#if V8_HOST_ARCH_32_BIT
SMI_ACCESSORS(SharedFunctionInfo, length, kLengthOffset)
-SMI_ACCESSORS(SharedFunctionInfo, formal_parameter_count,
+SMI_ACCESSORS(SharedFunctionInfo, internal_formal_parameter_count,
kFormalParameterCountOffset)
SMI_ACCESSORS(SharedFunctionInfo, expected_nof_properties,
kExpectedNofPropertiesOffset)
PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, length, kLengthOffset)
-PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
- formal_parameter_count,
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, internal_formal_parameter_count,
kFormalParameterCountOffset)
PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
void SharedFunctionInfo::DontAdaptArguments() {
DCHECK(code()->kind() == Code::BUILTIN);
- set_formal_parameter_count(kDontAdaptArgumentsSentinel);
+ set_internal_formal_parameter_count(kDontAdaptArgumentsSentinel);
}
bool JSFunction::NeedsArgumentsAdaption() {
- return shared()->formal_parameter_count() !=
- SharedFunctionInfo::kDontAdaptArgumentsSentinel;
+ return shared()->internal_formal_parameter_count() !=
+ SharedFunctionInfo::kDontAdaptArgumentsSentinel;
}
inline int length() const;
inline void set_length(int value);
- // [formal parameter count]: The declared number of parameters.
- inline int formal_parameter_count() const;
- inline void set_formal_parameter_count(int value);
+ // [internal formal parameter count]: The declared number of parameters.
+ // For subclass constructors, also includes new.target.
+ // The size of function's frame is internal_formal_parameter_count + 1.
+ inline int internal_formal_parameter_count() const;
+ inline void set_internal_formal_parameter_count(int value);
// Set the formal parameter count so the function code will be
// called without using argument adaptor frames.
constructor->shared()->set_name(*name_string);
if (FLAG_experimental_classes) {
- if (!super_class->IsTheHole() && !super_class->IsNull()) {
+ if (!super_class->IsTheHole()) {
Handle<Code> stub(isolate->builtins()->JSConstructStubForDerived());
constructor->shared()->set_construct_stub(*stub);
}
target_shared->set_scope_info(source_shared->scope_info());
target_shared->set_length(source_shared->length());
target_shared->set_feedback_vector(source_shared->feedback_vector());
- target_shared->set_formal_parameter_count(
- source_shared->formal_parameter_count());
+ target_shared->set_internal_formal_parameter_count(
+ source_shared->internal_formal_parameter_count());
target_shared->set_script(source_shared->script());
target_shared->set_start_position_and_type(
source_shared->start_position_and_type());
JSFunction* inlined_function = functions[inlined_jsframe_index];
SlotRefValueBuilder slot_refs(
frame, inlined_jsframe_index,
- inlined_function->shared()->formal_parameter_count());
+ inlined_function->shared()->internal_formal_parameter_count());
int args_count = slot_refs.args_length();
Handle<Object> constructor,
Handle<Object> original_constructor,
Handle<AllocationSite> site) {
- // TODO(dslomov): implement prototype rewiring.
- // The check below is a sanity check.
- CHECK(*constructor == *original_constructor);
-
// If the constructor isn't a proper function we throw a type error.
if (!constructor->IsJSFunction()) {
Vector<Handle<Object> > arguments = HandleVector(&constructor, 1);
Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
+ CHECK(original_constructor->IsJSFunction());
+ Handle<JSFunction> original_function =
+ Handle<JSFunction>::cast(original_constructor);
+
+
// If function should not have prototype, construction is not allowed. In this
// case generated code bailouts here, since function has no initial_map.
if (!function->should_have_prototype() && !function->shared()->bound()) {
result = isolate->factory()->NewJSObjectWithMemento(function, site);
}
+ // Set up the prototoype using original function.
+ // TODO(dslomov): instead of setting the __proto__,
+ // use and cache the correct map.
+ if (*original_function != *function) {
+ if (original_function->has_instance_prototype()) {
+ Handle<Object> prototype =
+ handle(original_function->instance_prototype(), isolate);
+ RETURN_FAILURE_ON_EXCEPTION(
+ isolate, JSObject::SetPrototype(result, prototype, false));
+ }
+ }
+
isolate->counters()->constructed_objects()->Increment();
isolate->counters()->constructed_objects_runtime()->Increment();
Handle<JSFunction> callee,
Object** parameters,
int argument_count) {
+ CHECK(!IsSubclassConstructor(callee->shared()->kind()));
Handle<JSObject> result =
isolate->factory()->NewArgumentsObject(callee, argument_count);
// Allocate the elements if needed.
- int parameter_count = callee->shared()->formal_parameter_count();
+ int parameter_count = callee->shared()->internal_formal_parameter_count();
if (argument_count > 0) {
if (parameter_count > 0) {
int mapped_count = Min(argument_count, parameter_count);
scope_name_ = ast_value_factory_->empty_string();
dynamics_ = NULL;
receiver_ = NULL;
+ new_target_ = nullptr;
function_ = NULL;
arguments_ = NULL;
illegal_redecl_ = NULL;
}
-void Scope::Initialize(bool uninitialized_this) {
+void Scope::Initialize(bool subclass_constructor) {
DCHECK(!already_resolved());
// Add this scope as a new inner scope of the outer scope.
// such parameter is 'this' which is passed on the stack when
// invoking scripts
if (is_declaration_scope()) {
- DCHECK(!uninitialized_this || is_function_scope());
- DCHECK(FLAG_experimental_classes || !uninitialized_this);
+ DCHECK(!subclass_constructor || is_function_scope());
+ DCHECK(FLAG_experimental_classes || !subclass_constructor);
Variable* var = variables_.Declare(
this, ast_value_factory_->this_string(),
- uninitialized_this ? CONST : VAR, false, Variable::THIS,
- uninitialized_this ? kNeedsInitialization : kCreatedInitialized);
+ subclass_constructor ? CONST : VAR, false, Variable::THIS,
+ subclass_constructor ? kNeedsInitialization : kCreatedInitialized);
var->AllocateTo(Variable::PARAMETER, -1);
receiver_ = var;
+
+ if (subclass_constructor) {
+ new_target_ = variables_.Declare(
+ this, ast_value_factory_->new_target_string(), CONST, false,
+ Variable::NEW_TARGET, kCreatedInitialized);
+ new_target_->AllocateTo(Variable::PARAMETER, -2);
+ new_target_->set_is_used();
+ }
} else {
DCHECK(outer_scope() != NULL);
receiver_ = outer_scope()->receiver();
// Give var a read/write use if there is a chance it might be accessed
// via an eval() call. This is only possible if the variable has a
// visible name.
- if ((var->is_this() || !var->raw_name()->IsEmpty()) &&
- (var->has_forced_context_allocation() ||
- scope_calls_eval_ ||
- inner_scope_calls_eval_ ||
- scope_contains_with_ ||
- is_catch_scope() ||
- is_block_scope() ||
- is_module_scope() ||
- is_script_scope())) {
+ if ((var->is_this() || var->is_new_target() || !var->raw_name()->IsEmpty()) &&
+ (var->has_forced_context_allocation() || scope_calls_eval_ ||
+ inner_scope_calls_eval_ || scope_contains_with_ || is_catch_scope() ||
+ is_block_scope() || is_module_scope() || is_script_scope())) {
var->set_is_used();
if (scope_calls_eval_ || inner_scope_calls_eval_) var->set_maybe_assigned();
}
scope_name_ = scope_name;
}
- void Initialize(bool uninitialized_this = false);
+ void Initialize(bool subclass_constructor = 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
// The language mode of this scope.
LanguageMode language_mode() const { return language_mode_; }
- // The variable corresponding the 'this' value.
+ // The variable corresponding to the 'this' value.
Variable* receiver() { return receiver_; }
+ // The variable corresponding to the 'new.target' value.
+ Variable* new_target_var() { return new_target_; }
+
// The variable holding the function literal for named function
// literals, or NULL. Only valid for function scopes.
VariableDeclaration* function() const {
Variable* receiver_;
// Function variable, if any; function scopes only.
VariableDeclaration* function_;
+ // new.target variable, function scopes only.
+ Variable* new_target_;
// Convenience variable; function scopes only.
Variable* arguments_;
// Interface; module scopes only.
class Variable: public ZoneObject {
public:
- enum Kind {
- NORMAL,
- THIS,
- ARGUMENTS
- };
+ enum Kind { NORMAL, THIS, NEW_TARGET, ARGUMENTS };
enum Location {
// Before and during variable allocation, a variable whose location is
}
bool is_this() const { return kind_ == THIS; }
+ bool is_new_target() const { return kind_ == NEW_TARGET; }
bool is_arguments() const { return kind_ == ARGUMENTS; }
// True if the variable is named eval and not known to be shadowed.
__ Push(rax);
__ SmiToInteger32(rax, rax);
+ // Push new.target
+ __ Push(rdx);
+
// receiver is the hole.
__ Push(masm->isolate()->factory()->the_hole_value());
__ j(greater_equal, &loop);
// Call the function.
+ __ incp(rax); // Pushed new.target.
ParameterCount actual(rax);
__ InvokeFunction(rdi, actual, CALL_FUNCTION, NullCallWrapper());
void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+ CHECK(!has_new_target());
// The key is in rdx and the parameter count is in rax.
DCHECK(rdx.is(ArgumentsAccessReadDescriptor::index()));
DCHECK(rax.is(ArgumentsAccessReadDescriptor::parameter_count()));
// rbx: the mapped parameter count (untagged)
// rax: the allocated object (tagged).
+ CHECK(!has_new_target());
+
Factory* factory = isolate()->factory();
StackArgumentsAccessor args(rsp, 3, ARGUMENTS_DONT_CONTAIN_RECEIVER);
// rsp[8] : number of parameters
// rsp[16] : receiver displacement
// rsp[24] : function
+ CHECK(!has_new_target());
// Check if the calling frame is an arguments adaptor frame.
Label runtime;
// Patch the arguments.length and the parameters pointer.
__ bind(&adaptor_frame);
__ movp(rcx, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ if (has_new_target()) {
+ // Subtract 1 from smi-tagged arguments count.
+ __ SmiToInteger32(rcx, rcx);
+ __ decp(rcx);
+ __ Integer32ToSmi(rcx, rcx);
+ }
__ movp(args.GetArgumentOperand(2), rcx);
__ SmiToInteger64(rcx, rcx);
__ leap(rdx, Operand(rdx, rcx, times_pointer_size,
}
// Pass original constructor to construct stub.
- __ movp(rdx, rdi);
+ if (IsSuperConstructorCall()) {
+ __ movp(rdx, Operand(rsp, rax, times_pointer_size, 2 * kPointerSize));
+ } else {
+ __ movp(rdx, rdi);
+ }
// Jump to the function-specific construct stub.
Register jmp_reg = rcx;
// function, receiver address, parameter count.
// The stub will rewrite receiver and parameter count if the previous
// stack frame was an arguments adapter frame.
+
+ ArgumentsAccessStub::HasNewTarget has_new_target =
+ IsSubclassConstructor(info->function()->kind())
+ ? ArgumentsAccessStub::HAS_NEW_TARGET
+ : ArgumentsAccessStub::NO_NEW_TARGET;
ArgumentsAccessStub::Type type;
if (is_strict(language_mode())) {
type = ArgumentsAccessStub::NEW_STRICT;
} else {
type = ArgumentsAccessStub::NEW_SLOPPY_FAST;
}
- ArgumentsAccessStub stub(isolate(), type);
+ ArgumentsAccessStub stub(isolate(), type, has_new_target);
__ CallStub(&stub);
SetVar(arguments, rax, rbx, rdx);
__ popq(rbp);
int no_frame_start = masm_->pc_offset();
- int arguments_bytes = (info_->scope()->num_parameters() + 1) * kPointerSize;
+ int arg_count = info_->scope()->num_parameters() + 1;
+ if (FLAG_experimental_classes &&
+ IsSubclassConstructor(info_->function()->kind())) {
+ arg_count++;
+ }
+ int arguments_bytes = arg_count * kPointerSize;
__ Ret(arguments_bytes, rcx);
// Add padding that will be overwritten by a debugger breakpoint. We
void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+ Variable* new_target_var = scope()->DeclarationScope()->new_target_var();
+ GetVar(result_register(), new_target_var);
+ __ Push(result_register());
+
SuperReference* super_ref = expr->expression()->AsSuperReference();
EmitLoadSuperConstructor(super_ref);
__ Push(result_register());
__ Move(rdx, SmiFromSlot(expr->CallFeedbackSlot()));
// TODO(dslomov): use a different stub and propagate new.target.
- CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
+ CallConstructStub stub(isolate(), SUPER_CALL_RECORD_TARGET);
__ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
+ __ Drop(1);
+
RecordJSReturnSite(expr);
+
EmitVariableAssignment(this_var, Token::INIT_CONST);
context()->Plug(rax);
}
// Flags: --experimental-classes --harmony-classes
'use strict';
+(function TestArgumentsAccess() {
+ class Base {
+ constructor() {
+ assertEquals(2, arguments.length);
+ assertEquals(1, arguments[0]);
+ assertEquals(2, arguments[1]);
+ }
+ }
+
+ let b = new Base(1,2);
+
+ class Subclass extends Base {
+ constructor() {
+ assertEquals(2, arguments.length);
+ assertEquals(3, arguments[0]);
+ assertEquals(4, arguments[1]);
+ super(1,2);
+ }
+ }
+
+ let s = new Subclass(3,4);
+ assertEquals(0, Subclass.length);
+
+ class Subclass2 extends Base {
+ constructor(x,y) {
+ assertEquals(2, arguments.length);
+ assertEquals(3, arguments[0]);
+ assertEquals(4, arguments[1]);
+ super(1,2);
+ }
+ }
+
+ let s2 = new Subclass2(3,4);
+ assertEquals(2, Subclass2.length);
+}());
+
+(function TestThisAccessRestriction() {
+ class Base {
+ constructor(a, b) {
+ let o = new Object();
+ o.prp = a + b;
+ return o;
+ }
+ }
+
+ class Subclass extends Base {
+ constructor(a, b) {
+ var exn;
+ try {
+ this.prp1 = 3;
+ } catch (e) {
+ exn = e;
+ }
+ assertTrue(exn instanceof ReferenceError);
+ super(a, b);
+ assertSame(a + b, this.prp);
+ assertSame(undefined, this.prp1);
+ assertFalse(this.hasOwnProperty("prp1"));
+ return this;
+ }
+ }
+
+ 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(x) {
+ super(1,2);
+
+ if (x < 0) return;
+
+ let called = false;
+ function tmp() { called = true; return 3; }
+ var exn = null;
+ try {
+ super(tmp(),4);
+ } catch (e) { exn = e; }
+ assertTrue(exn instanceof ReferenceError);
+ // TODO(dslomov): should be 'true'.
+ assertFalse(called);
+ }
+ }
+
+ var s2 = new Subclass2(1);
+ assertSame(3, s2.prp);
-class Base {
- constructor(a, b) {
- let o = new Object();
- o.prp = a + b;
- return o;
+ 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);
+
+ class BadSubclass extends Base {
+ constructor() {}
+ }
+
+ assertThrows(function() { new BadSubclass(); }, ReferenceError);
+}());
+
+(function TestPrototypeWiring() {
+ class Base {
+ constructor(x) {
+ this.foobar = x;
+ }
+ }
+
+ class Subclass extends Base {
+ constructor(x) {
+ super(x);
+ }
+ }
+
+ let s = new Subclass(1);
+ assertSame(1, s.foobar);
+ assertSame(Subclass.prototype, s.__proto__);
+
+ let s1 = new Subclass(1, 2);
+ assertSame(1, s1.foobar);
+ assertTrue(s1.__proto__ === Subclass.prototype);
+
+ let s2 = new Subclass();
+ assertSame(undefined, s2.foobar);
+ assertSame(Subclass.prototype, s2.__proto__);
+ assertThrows(function() { Subclass(1); }, TypeError);
+ assertThrows(function() { Subclass(1,2,3,4); }, TypeError);
+
+ class Subclass2 extends Subclass {
+ constructor() {
+ super(5, 6, 7);
+ }
}
-}
-
-class Subclass extends Base {
- constructor(a, b) {
- var exn;
- try {
- this.prp1 = 3;
- } catch (e) {
- exn = e;
+
+ let ss2 = new Subclass2();
+ assertSame(5, ss2.foobar);
+ assertSame(Subclass2.prototype, ss2.__proto__);
+
+ class Subclass3 extends Base {
+ constructor(x,y) {
+ super(x + y);
}
- assertTrue(exn instanceof ReferenceError);
- super(a, b);
- assertSame(a + b, this.prp);
- assertSame(undefined, this.prp1);
- assertFalse(this.hasOwnProperty("prp1"));
- return this;
}
-}
-let b = new Base(1, 2);
-assertSame(3, b.prp);
+ let ss3 = new Subclass3(27,42-27);
+ assertSame(42, ss3.foobar);
+ assertSame(Subclass3.prototype, ss3.__proto__);
+}());
+(function TestSublclassingBuiltins() {
+ class ExtendedUint8Array extends Uint8Array {
+ constructor() {
+ super(10);
+ this[0] = 255;
+ this[1] = 0xFFA;
+ }
+ }
-let s = new Subclass(2, -1);
-assertSame(1, s.prp);
-assertSame(undefined, s.prp1);
-assertFalse(s.hasOwnProperty("prp1"));
+ var eua = new ExtendedUint8Array();
+ assertEquals(10, eua.length);
+ assertEquals(10, eua.byteLength);
+ assertEquals(0xFF, eua[0]);
+ assertEquals(0xFA, eua[1]);
+ assertTrue(eua.__proto__ === ExtendedUint8Array.prototype);
+ assertEquals("[object Uint8Array]", Object.prototype.toString.call(eua));
+}());
-class Subclass2 extends Base {
- constructor(x) {
- super(1,2);
+(function TestSubclassingNull() {
+ let N = null;
- if (x < 0) return;
+ class Foo extends N {
+ constructor(x,y) {
+ assertSame(1, x);
+ assertSame(2, y);
+ return {};
+ }
+ }
+
+ new Foo(1,2);
+}());
+
+(function TestSubclassBinding() {
+ class Base {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ }
+ }
- 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);
+ let obj = {};
+ class Subclass extends Base {
+ constructor(x,y) {
+ super(x,y);
+ assertTrue(this !== obj);
+ }
}
-}
-var s2 = new Subclass2(1);
-assertSame(3, s2.prp);
+ let f = Subclass.bind(obj);
+ assertThrows(function () { f(1, 2); }, TypeError);
+ let s = new f(1, 2);
+ assertSame(1, s.x);
+ assertSame(2, s.y);
+ assertSame(Subclass.prototype, s.__proto__);
-var s3 = new Subclass2(-1);
-assertSame(3, s3.prp);
+ let s1 = new f(1);
+ assertSame(1, s1.x);
+ assertSame(undefined, s1.y);
+ assertSame(Subclass.prototype, s1.__proto__);
-assertThrows(function() { Subclass.call(new Object(), 1, 2); }, TypeError);
-assertThrows(function() { Base.call(new Object(), 1, 2); }, TypeError);
+ let g = Subclass.bind(obj, 1);
+ assertThrows(function () { g(8); }, TypeError);
+ let s2 = new g(8);
+ assertSame(1, s2.x);
+ assertSame(8, s2.y);
+ assertSame(Subclass.prototype, s.__proto__);
+}());