Implement ES6 rest parameters

LOG=Y
BUG=v8:2159
R=dslomov@chromium.org, arv@chromium.org, marja@chromium.org

Review URL: https://codereview.chromium.org/816913003

Cr-Commit-Position: refs/heads/master@{#26645}
This commit is contained in:
caitpotter88 2015-02-13 16:14:46 -08:00 committed by Commit bot
parent d0c2c2b32c
commit 8bb2e39774
33 changed files with 663 additions and 11 deletions

View File

@ -1901,6 +1901,30 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
}
void RestParamAccessStub::GenerateNew(MacroAssembler* masm) {
// Stack layout on entry.
// sp[0] : index of rest parameter
// sp[4] : number of parameters
// sp[8] : receiver displacement
Label runtime;
__ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
__ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
__ b(ne, &runtime);
// Patch the arguments.length and the parameters pointer.
__ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ str(r1, MemOperand(sp, 1 * kPointerSize));
__ add(r3, r2, Operand::PointerOffsetFromSmiKey(r1));
__ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
__ str(r3, MemOperand(sp, 2 * kPointerSize));
__ bind(&runtime);
__ TailCallRuntime(Runtime::kNewRestParam, 3, 1);
}
void RegExpExecStub::Generate(MacroAssembler* masm) {
// Just jump directly to runtime if native RegExp is not selected at compile
// time or if regexp entry in generated code is turned off runtime switch or

View File

@ -240,6 +240,25 @@ void FullCodeGenerator::Generate() {
}
}
// Possibly allocate RestParameters
int rest_index;
Variable* rest_param = scope()->rest_parameter(&rest_index);
if (rest_param) {
Comment cmnt(masm_, "[ Allocate rest parameter array");
int num_parameters = info->scope()->num_parameters();
int offset = num_parameters * kPointerSize;
__ add(r3, fp, Operand(StandardFrameConstants::kCallerSPOffset + offset));
__ mov(r2, Operand(Smi::FromInt(num_parameters)));
__ mov(r1, Operand(Smi::FromInt(rest_index)));
__ Push(r3, r2, r1);
RestParamAccessStub stub(isolate());
__ CallStub(&stub);
SetVar(rest_param, r0, r1, r2);
}
Variable* arguments = scope()->arguments();
if (arguments != NULL) {
// Function uses arguments object.
@ -267,7 +286,7 @@ void FullCodeGenerator::Generate() {
? ArgumentsAccessStub::HAS_NEW_TARGET
: ArgumentsAccessStub::NO_NEW_TARGET;
ArgumentsAccessStub::Type type;
if (is_strict(language_mode())) {
if (is_strict(language_mode()) || !is_simple_parameter_list()) {
type = ArgumentsAccessStub::NEW_STRICT;
} else if (function()->has_duplicate_parameters()) {
type = ArgumentsAccessStub::NEW_SLOPPY_SLOW;

View File

@ -2160,6 +2160,53 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
}
void RestParamAccessStub::GenerateNew(MacroAssembler* masm) {
// Stack layout on entry.
// jssp[0]: index of rest parameter (tagged)
// jssp[8]: number of parameters (tagged)
// jssp[16]: address of receiver argument
//
// Returns pointer to result object in x0.
// Get the stub arguments from the frame, and make an untagged copy of the
// parameter count.
Register rest_index_smi = x1;
Register param_count_smi = x2;
Register params = x3;
Register param_count = x13;
__ Pop(rest_index_smi, param_count_smi, params);
__ SmiUntag(param_count, param_count_smi);
// Test if arguments adaptor needed.
Register caller_fp = x11;
Register caller_ctx = x12;
Label runtime;
__ Ldr(caller_fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ Ldr(caller_ctx, MemOperand(caller_fp,
StandardFrameConstants::kContextOffset));
__ Cmp(caller_ctx, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
__ B(ne, &runtime);
// x1 rest_index_smi index of rest parameter
// x2 param_count_smi number of parameters passed to function (smi)
// x3 params pointer to parameters
// x11 caller_fp caller's frame pointer
// x13 param_count number of parameters passed to function
// Patch the argument length and parameters pointer.
__ Ldr(param_count_smi,
MemOperand(caller_fp,
ArgumentsAdaptorFrameConstants::kLengthOffset));
__ SmiUntag(param_count, param_count_smi);
__ Add(x10, caller_fp, Operand(param_count, LSL, kPointerSizeLog2));
__ Add(params, x10, StandardFrameConstants::kCallerSPOffset);
__ Bind(&runtime);
__ Push(params, param_count_smi, rest_index_smi);
__ TailCallRuntime(Runtime::kNewRestParam, 3, 1);
}
void RegExpExecStub::Generate(MacroAssembler* masm) {
#ifdef V8_INTERPRETED_REGEXP
__ TailCallRuntime(Runtime::kRegExpExecRT, 4, 1);

View File

@ -241,6 +241,25 @@ void FullCodeGenerator::Generate() {
}
}
// Possibly allocate RestParameters
int rest_index;
Variable* rest_param = scope()->rest_parameter(&rest_index);
if (rest_param) {
Comment cmnt(masm_, "[ Allocate rest parameter array");
int num_parameters = info->scope()->num_parameters();
int offset = num_parameters * kPointerSize;
__ Add(x3, fp, StandardFrameConstants::kCallerSPOffset + offset);
__ Mov(x2, Smi::FromInt(num_parameters));
__ Mov(x1, Smi::FromInt(rest_index));
__ Push(x3, x2, x1);
RestParamAccessStub stub(isolate());
__ CallStub(&stub);
SetVar(rest_param, x0, x1, x2);
}
Variable* arguments = scope()->arguments();
if (arguments != NULL) {
// Function uses arguments object.
@ -267,7 +286,7 @@ void FullCodeGenerator::Generate() {
? ArgumentsAccessStub::HAS_NEW_TARGET
: ArgumentsAccessStub::NO_NEW_TARGET;
ArgumentsAccessStub::Type type;
if (is_strict(language_mode())) {
if (is_strict(language_mode()) || !is_simple_parameter_list()) {
type = ArgumentsAccessStub::NEW_STRICT;
} else if (function()->has_duplicate_parameters()) {
type = ArgumentsAccessStub::NEW_SLOPPY_SLOW;

View File

@ -220,6 +220,7 @@ namespace internal {
V(kRegisterDidNotMatchExpectedRoot, "Register did not match expected root") \
V(kRegisterWasClobbered, "Register was clobbered") \
V(kRememberedSetPointerInNewSpace, "Remembered set pointer is in new space") \
V(kRestParameter, "Rest parameters") \
V(kReturnAddressNotFoundInFrame, "Return address not found in frame") \
V(kRhsHasBeenClobbered, "Rhs has been clobbered") \
V(kScopedBlock, "ScopedBlock") \

View File

@ -790,6 +790,11 @@ void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
}
void RestParamAccessStub::Generate(MacroAssembler* masm) {
GenerateNew(masm);
}
void ArgumentsAccessStub::PrintName(std::ostream& os) const { // NOLINT
os << "ArgumentsAccessStub_";
switch (type()) {
@ -810,6 +815,11 @@ void ArgumentsAccessStub::PrintName(std::ostream& os) const { // NOLINT
}
void RestParamAccessStub::PrintName(std::ostream& os) const { // NOLINT
os << "RestParamAccessStub_";
}
void CallFunctionStub::PrintName(std::ostream& os) const { // NOLINT
os << "CallFunctionStub_Args" << argc();
}

View File

@ -94,7 +94,8 @@ namespace internal {
V(StoreField) \
V(StoreGlobal) \
V(StoreTransition) \
V(StringLength)
V(StringLength) \
V(RestParamAccess)
// List of code stubs only used on ARM 32 bits platforms.
#if V8_TARGET_ARCH_ARM
@ -1650,6 +1651,23 @@ class ArgumentsAccessStub: public PlatformCodeStub {
};
class RestParamAccessStub: public PlatformCodeStub {
public:
explicit RestParamAccessStub(Isolate* isolate) : PlatformCodeStub(isolate) { }
CallInterfaceDescriptor GetCallInterfaceDescriptor() OVERRIDE {
return ContextOnlyDescriptor(isolate());
}
private:
void GenerateNew(MacroAssembler* masm);
virtual void PrintName(std::ostream& os) const OVERRIDE; // NOLINT
DEFINE_PLATFORM_CODE_STUB(RestParamAccess, PlatformCodeStub);
};
class RegExpExecStub: public PlatformCodeStub {
public:
explicit RegExpExecStub(Isolate* isolate) : PlatformCodeStub(isolate) { }

View File

@ -308,6 +308,11 @@ void CompilationInfo::EnsureFeedbackVector() {
}
bool CompilationInfo::is_simple_parameter_list() {
return scope_->is_simple_parameter_list();
}
class HOptimizedGraphBuilderWithPositions: public HOptimizedGraphBuilder {
public:
explicit HOptimizedGraphBuilderWithPositions(CompilationInfo* info)

View File

@ -424,6 +424,8 @@ class CompilationInfo {
void PrintAstForTesting();
#endif
bool is_simple_parameter_list();
protected:
CompilationInfo(Handle<SharedFunctionInfo> shared_info,
Zone* zone);

View File

@ -449,6 +449,11 @@ bool AstGraphBuilder::CreateGraph() {
// Build the arguments object if it is used.
BuildArgumentsObject(scope->arguments());
// Build rest arguments array if it is used.
int rest_index;
Variable* rest_parameter = scope->rest_parameter(&rest_index);
BuildRestArgumentsArray(rest_parameter, rest_index);
// Emit tracing call if requested to do so.
if (FLAG_trace) {
NewNode(javascript()->CallRuntime(Runtime::kTraceEnter, 0));
@ -2479,6 +2484,22 @@ Node* AstGraphBuilder::BuildArgumentsObject(Variable* arguments) {
}
Node* AstGraphBuilder::BuildRestArgumentsArray(Variable* rest, int index) {
if (rest == NULL) return NULL;
DCHECK(index >= 0);
const Operator* op = javascript()->CallRuntime(Runtime::kNewRestParamSlow, 1);
Node* object = NewNode(op, jsgraph()->SmiConstant(index));
// Assign the object to the rest array
DCHECK(rest->IsContextSlot() || rest->IsStackAllocated());
// This should never lazy deopt, so it is fine to send invalid bailout id.
BuildVariableAssignment(rest, object, Token::ASSIGN, BailoutId::None());
return object;
}
Node* AstGraphBuilder::BuildHoleCheckSilent(Node* value, Node* for_hole,
Node* not_hole) {
IfBuilder hole_check(this);

View File

@ -205,6 +205,9 @@ class AstGraphBuilder : public AstVisitor {
// Builder to create an arguments object if it is used.
Node* BuildArgumentsObject(Variable* arguments);
// Builder to create an array of rest parameters if used
Node* BuildRestArgumentsArray(Variable* rest, int index);
// Builders for variable load and assignment.
Node* BuildVariableAssignment(Variable* var, Node* value, Token::Value op,
BailoutId bailout_id,

View File

@ -125,6 +125,7 @@ bool Linkage::NeedsFrameState(Runtime::FunctionId function) {
case Runtime::kTraceEnter:
case Runtime::kTraceExit:
case Runtime::kTypeof:
case Runtime::kNewRestParamSlow:
return false;
case Runtime::kInlineArguments:
case Runtime::kInlineCallFunction:

View File

@ -2225,7 +2225,8 @@ Handle<DebugInfo> Factory::NewDebugInfo(Handle<SharedFunctionInfo> shared) {
Handle<JSObject> Factory::NewArgumentsObject(Handle<JSFunction> callee,
int length) {
bool strict_mode_callee = is_strict(callee->shared()->language_mode());
bool strict_mode_callee = is_strict(callee->shared()->language_mode()) ||
!callee->is_simple_parameter_list();
Handle<Map> map = strict_mode_callee ? isolate()->strict_arguments_map()
: isolate()->sloppy_arguments_map();

View File

@ -685,6 +685,7 @@ class FullCodeGenerator: public AstVisitor {
bool is_eval() { return info_->is_eval(); }
bool is_native() { return info_->is_native(); }
LanguageMode language_mode() { return function()->language_mode(); }
bool is_simple_parameter_list() { return info_->is_simple_parameter_list(); }
FunctionLiteral* function() { return info_->function(); }
Scope* scope() { return scope_; }

View File

@ -4525,6 +4525,12 @@ void HOptimizedGraphBuilder::SetUpScope(Scope* scope) {
environment()->Bind(scope->arguments(),
graph()->GetArgumentsObject());
}
int rest_index;
Variable* rest = scope->rest_parameter(&rest_index);
if (rest) {
return Bailout(kRestParameter);
}
}

View File

@ -1159,6 +1159,31 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
}
void RestParamAccessStub::GenerateNew(MacroAssembler* masm) {
// esp[0] : return address
// esp[4] : index of rest parameter
// esp[8] : number of parameters
// esp[12] : receiver displacement
// Check if the calling frame is an arguments adaptor frame.
Label runtime;
__ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
__ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
__ cmp(ecx, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
__ j(not_equal, &runtime);
// Patch the arguments.length and the parameters pointer.
__ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ mov(Operand(esp, 2 * kPointerSize), ecx);
__ lea(edx, Operand(edx, ecx, times_2,
StandardFrameConstants::kCallerSPOffset));
__ mov(Operand(esp, 3 * kPointerSize), edx);
__ bind(&runtime);
__ TailCallRuntime(Runtime::kNewRestParam, 3, 1);
}
void RegExpExecStub::Generate(MacroAssembler* masm) {
// Just jump directly to runtime if native RegExp is not selected at compile
// time or if regexp entry in generated code is turned off runtime switch or

View File

@ -236,6 +236,26 @@ void FullCodeGenerator::Generate() {
}
}
// Possibly allocate RestParameters
int rest_index;
Variable* rest_param = scope()->rest_parameter(&rest_index);
if (rest_param) {
Comment cmnt(masm_, "[ Allocate rest parameter array");
int num_parameters = info->scope()->num_parameters();
int offset = num_parameters * kPointerSize;
__ lea(edx,
Operand(ebp, StandardFrameConstants::kCallerSPOffset + offset));
__ push(edx);
__ push(Immediate(Smi::FromInt(num_parameters)));
__ push(Immediate(Smi::FromInt(rest_index)));
RestParamAccessStub stub(isolate());
__ CallStub(&stub);
SetVar(rest_param, eax, ebx, edx);
}
Variable* arguments = scope()->arguments();
if (arguments != NULL) {
// Function uses arguments object.
@ -257,7 +277,7 @@ void FullCodeGenerator::Generate() {
// The stub will rewrite receiver and parameter count if the previous
// stack frame was an arguments adapter frame.
ArgumentsAccessStub::Type type;
if (is_strict(language_mode())) {
if (is_strict(language_mode()) || !is_simple_parameter_list()) {
type = ArgumentsAccessStub::NEW_STRICT;
} else if (function()->has_duplicate_parameters()) {
type = ArgumentsAccessStub::NEW_SLOPPY_SLOW;

View File

@ -2022,6 +2022,33 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
}
void RestParamAccessStub::GenerateNew(MacroAssembler* masm) {
// sp[0] : index of rest parameter
// sp[4] : number of parameters
// sp[8] : receiver displacement
// Check if the calling frame is an arguments adaptor frame.
Label runtime;
__ lw(a2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ lw(a3, MemOperand(a2, StandardFrameConstants::kContextOffset));
__ Branch(&runtime, ne, a3,
Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
// Patch the arguments.length and the parameters pointer.
__ lw(a1, MemOperand(a2, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ sw(a1, MemOperand(sp, 1 * kPointerSize));
__ sll(at, a1, kPointerSizeLog2 - kSmiTagSize);
__ Addu(a3, a2, Operand(at));
__ Addu(a3, a3, Operand(StandardFrameConstants::kCallerSPOffset));
__ sw(a3, MemOperand(sp, 2 * kPointerSize));
// Do the runtime call to allocate the arguments object.
__ bind(&runtime);
__ TailCallRuntime(Runtime::kNewRestParam, 3, 1);
}
void RegExpExecStub::Generate(MacroAssembler* masm) {
// Just jump directly to runtime if native RegExp is not selected at compile
// time or if regexp entry in generated code is turned off runtime switch or

View File

@ -249,6 +249,26 @@ void FullCodeGenerator::Generate() {
}
}
// Possibly allocate RestParameters
int rest_index;
Variable* rest_param = scope()->rest_parameter(&rest_index);
if (rest_param) {
Comment cmnt(masm_, "[ Allocate rest parameter array");
int num_parameters = info->scope()->num_parameters();
int offset = num_parameters * kPointerSize;
__ Addu(a3, fp,
Operand(StandardFrameConstants::kCallerSPOffset + offset));
__ li(a2, Operand(Smi::FromInt(num_parameters)));
__ li(a1, Operand(Smi::FromInt(rest_index)));
__ Push(a3, a2, a1);
RestParamAccessStub stub(isolate());
__ CallStub(&stub);
SetVar(rest_param, v0, a1, a2);
}
Variable* arguments = scope()->arguments();
if (arguments != NULL) {
// Function uses arguments object.
@ -276,7 +296,7 @@ void FullCodeGenerator::Generate() {
? ArgumentsAccessStub::HAS_NEW_TARGET
: ArgumentsAccessStub::NO_NEW_TARGET;
ArgumentsAccessStub::Type type;
if (is_strict(language_mode())) {
if (is_strict(language_mode()) || !is_simple_parameter_list()) {
type = ArgumentsAccessStub::NEW_STRICT;
} else if (function()->has_duplicate_parameters()) {
type = ArgumentsAccessStub::NEW_SLOPPY_SLOW;

View File

@ -2025,6 +2025,34 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
}
void RestParamAccessStub::GenerateNew(MacroAssembler* masm) {
// sp[0] : index of rest parameter
// sp[4] : number of parameters
// sp[8] : receiver displacement
// Check if the calling frame is an arguments adaptor frame.
Label runtime;
__ ld(a2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ ld(a3, MemOperand(a2, StandardFrameConstants::kContextOffset));
__ Branch(&runtime, ne, a3,
Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
// Patch the arguments.length and the parameters pointer.
__ ld(a1, MemOperand(a2, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ sd(a1, MemOperand(sp, 1 * kPointerSize));
__ SmiScale(at, a1, kPointerSizeLog2);
__ Daddu(a3, a2, Operand(at));
__ Daddu(a3, a3, Operand(StandardFrameConstants::kCallerSPOffset));
__ sd(a3, MemOperand(sp, 2 * kPointerSize));
// Do the runtime call to allocate the arguments object.
__ bind(&runtime);
__ TailCallRuntime(Runtime::kNewRestParam, 3, 1);
}
void RegExpExecStub::Generate(MacroAssembler* masm) {
// Just jump directly to runtime if native RegExp is not selected at compile
// time or if regexp entry in generated code is turned off runtime switch or

View File

@ -245,6 +245,27 @@ void FullCodeGenerator::Generate() {
}
}
}
// Possibly allocate RestParameters
int rest_index;
Variable* rest_param = scope()->rest_parameter(&rest_index);
if (rest_param) {
Comment cmnt(masm_, "[ Allocate rest parameter array");
int num_parameters = info->scope()->num_parameters();
int offset = num_parameters * kPointerSize;
__ Daddu(a3, fp,
Operand(StandardFrameConstants::kCallerSPOffset + offset));
__ li(a2, Operand(Smi::FromInt(num_parameters)));
__ li(a1, Operand(Smi::FromInt(rest_index)));
__ Push(a3, a2, a1);
RestParamAccessStub stub(isolate());
__ CallStub(&stub);
SetVar(rest_param, v0, a1, a2);
}
Variable* arguments = scope()->arguments();
if (arguments != NULL) {
// Function uses arguments object.
@ -272,7 +293,7 @@ void FullCodeGenerator::Generate() {
? ArgumentsAccessStub::HAS_NEW_TARGET
: ArgumentsAccessStub::NO_NEW_TARGET;
ArgumentsAccessStub::Type type;
if (is_strict(language_mode())) {
if (is_strict(language_mode()) || !is_simple_parameter_list()) {
type = ArgumentsAccessStub::NEW_STRICT;
} else if (function()->has_duplicate_parameters()) {
type = ArgumentsAccessStub::NEW_SLOPPY_SLOW;

View File

@ -5957,6 +5957,11 @@ bool SharedFunctionInfo::is_compiled() {
}
bool SharedFunctionInfo::is_simple_parameter_list() {
return scope_info()->IsSimpleParameterList();
}
bool SharedFunctionInfo::IsApiFunction() {
return function_data()->IsFunctionTemplateInfo();
}
@ -6230,6 +6235,11 @@ bool JSFunction::is_compiled() {
}
bool JSFunction::is_simple_parameter_list() {
return shared()->is_simple_parameter_list();
}
FixedArray* JSFunction::literals() {
DCHECK(!shared()->bound());
return literals_or_bindings();

View File

@ -10387,7 +10387,7 @@ void SharedFunctionInfo::DisableOptimization(BailoutReason reason) {
void SharedFunctionInfo::InitFromFunctionLiteral(
Handle<SharedFunctionInfo> shared_info, FunctionLiteral* lit) {
shared_info->set_length(lit->parameter_count());
shared_info->set_length(lit->scope()->default_function_length());
if (IsSubclassConstructor(lit->kind())) {
shared_info->set_internal_formal_parameter_count(lit->parameter_count() +
1);

View File

@ -4174,6 +4174,10 @@ class ScopeInfo : public FixedArray {
// Return if this is a nested function within an asm module scope.
bool IsAsmFunction() { return AsmFunctionField::decode(Flags()); }
bool IsSimpleParameterList() {
return IsSimpleParameterListField::decode(Flags());
}
// Return the function_name if present.
String* FunctionName();
@ -4331,6 +4335,8 @@ class ScopeInfo : public FixedArray {
class FunctionVariableMode : public BitField<VariableMode, 9, 3> {};
class AsmModuleField : public BitField<bool, 12, 1> {};
class AsmFunctionField : public BitField<bool, 13, 1> {};
class IsSimpleParameterListField
: public BitField<bool, AsmFunctionField::kNext, 1> {};
// BitFields representing the encoded information for context locals in the
// ContextLocalInfoEntries part.
@ -7035,6 +7041,8 @@ class SharedFunctionInfo: public HeapObject {
// Calculate the number of in-object properties.
int CalculateInObjectProperties();
inline bool is_simple_parameter_list();
// Dispatched behavior.
DECLARE_PRINTER(SharedFunctionInfo)
DECLARE_VERIFIER(SharedFunctionInfo)
@ -7541,6 +7549,11 @@ class JSFunction: public JSObject {
// Returns if this function has been compiled to native code yet.
inline bool is_compiled();
// Returns `false` if formal parameters include rest parameters, optional
// parameters, or destructuring parameters.
// TODO(caitp): make this a flag set during parsing
inline bool is_simple_parameter_list();
// [next_function_link]: Links functions into various lists, e.g. the list
// of optimized functions hanging off the native_context. The CodeFlusher
// uses this link to chain together flushing candidates. Treated weakly

View File

@ -3837,6 +3837,11 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
FunctionLiteral::kIsFunction, parenthesized, kind, pos);
function_literal->set_function_token_position(function_token_pos);
if (scope->has_rest_parameter()) {
// TODO(caitp): enable optimization of functions with rest params
function_literal->set_dont_optimize_reason(kRestParameter);
}
if (fni_ != NULL && should_infer_name) fni_->AddFunction(function_literal);
return function_literal;
}

View File

@ -357,6 +357,7 @@ static Handle<JSObject> NewSloppyArguments(Isolate* isolate,
Object** parameters,
int argument_count) {
CHECK(!IsSubclassConstructor(callee->shared()->kind()));
DCHECK(callee->is_simple_parameter_list());
Handle<JSObject> result =
isolate->factory()->NewArgumentsObject(callee, argument_count);
@ -420,6 +421,7 @@ static Handle<JSObject> NewSloppyArguments(Isolate* isolate,
break;
}
}
DCHECK(context_index >= 0);
arguments->set_the_hole(index);
parameter_map->set(
@ -478,7 +480,9 @@ RUNTIME_FUNCTION(Runtime_NewArguments) {
// Determine parameter location on the stack and dispatch on language mode.
int argument_count = frame->GetArgumentsLength();
Object** parameters = reinterpret_cast<Object**>(frame->GetParameterSlot(-1));
return is_strict(callee->shared()->language_mode())
return (is_strict(callee->shared()->language_mode()) ||
!callee->is_simple_parameter_list())
? *NewStrictArguments(isolate, callee, parameters, argument_count)
: *NewSloppyArguments(isolate, callee, parameters, argument_count);
}
@ -504,6 +508,51 @@ RUNTIME_FUNCTION(Runtime_NewStrictArguments) {
}
static Handle<JSArray> NewRestParam(Isolate* isolate,
Object** parameters,
int num_params,
int rest_index) {
parameters -= rest_index;
int num_elements = std::max(0, num_params - rest_index);
Handle<FixedArray> elements =
isolate->factory()->NewUninitializedFixedArray(num_elements);
for (int i = 0; i < num_elements; ++i) {
elements->set(i, *--parameters);
}
return isolate->factory()->NewJSArrayWithElements(elements, FAST_ELEMENTS,
num_elements);
}
RUNTIME_FUNCTION(Runtime_NewRestParam) {
HandleScope scope(isolate);
DCHECK(args.length() == 3);
Object** parameters = reinterpret_cast<Object**>(args[0]);
CONVERT_SMI_ARG_CHECKED(num_params, 1);
CONVERT_SMI_ARG_CHECKED(rest_index, 2);
return *NewRestParam(isolate, parameters, num_params, rest_index);
}
RUNTIME_FUNCTION(Runtime_NewRestParamSlow) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_SMI_ARG_CHECKED(rest_index, 0);
JavaScriptFrameIterator it(isolate);
// Find the frame that holds the actual arguments passed to the function.
it.AdvanceToArgumentsFrame();
JavaScriptFrame* frame = it.frame();
int argument_count = frame->GetArgumentsLength();
Object** parameters = reinterpret_cast<Object**>(frame->GetParameterSlot(-1));
return *NewRestParam(isolate, parameters, argument_count, rest_index);
}
RUNTIME_FUNCTION(Runtime_NewClosureFromStubFailure) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);

View File

@ -454,6 +454,8 @@ namespace internal {
F(NewArguments, 1, 1) /* TODO(turbofan): Only temporary */ \
F(NewSloppyArguments, 3, 1) \
F(NewStrictArguments, 3, 1) \
F(NewRestParam, 3, 1) \
F(NewRestParamSlow, 1, 1) \
\
/* Harmony generators */ \
F(CreateJSGeneratorObject, 0, 1) \

View File

@ -25,6 +25,9 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
DCHECK(scope->StackLocalCount() == stack_local_count);
DCHECK(scope->ContextLocalCount() == context_local_count);
bool simple_parameter_list =
scope->is_function_scope() ? scope->is_simple_parameter_list() : true;
// Determine use and location of the function variable if it is present.
FunctionVariableInfo function_name_info;
VariableMode function_variable_mode;
@ -60,7 +63,8 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
FunctionVariableField::encode(function_name_info) |
FunctionVariableMode::encode(function_variable_mode) |
AsmModuleField::encode(scope->asm_module()) |
AsmFunctionField::encode(scope->asm_function());
AsmFunctionField::encode(scope->asm_function()) |
IsSimpleParameterListField::encode(simple_parameter_list);
scope_info->SetFlags(flags);
scope_info->SetParameterCount(parameter_count);
scope_info->SetStackLocalCount(stack_local_count);

View File

@ -370,6 +370,10 @@ class Scope: public ZoneObject {
return rest_parameter_;
}
bool has_rest_parameter() const {
return rest_index_ >= 0;
}
bool is_simple_parameter_list() const {
DCHECK(is_function_scope());
if (rest_index_ >= 0) return false;

View File

@ -845,6 +845,33 @@ void ArgumentsAccessStub::GenerateNewSloppySlow(MacroAssembler* masm) {
}
void RestParamAccessStub::GenerateNew(MacroAssembler* masm) {
// rsp[0] : return address
// rsp[8] : index of rest parameter
// rsp[16] : number of parameters
// rsp[24] : receiver displacement
// Check if the calling frame is an arguments adaptor frame.
Label runtime;
__ movp(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
__ movp(rcx, Operand(rdx, StandardFrameConstants::kContextOffset));
__ Cmp(rcx, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
__ j(not_equal, &runtime);
// Patch the arguments.length and the parameters pointer.
StackArgumentsAccessor args(rsp, 3, ARGUMENTS_DONT_CONTAIN_RECEIVER);
__ movp(rcx, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ movp(args.GetArgumentOperand(1), rcx);
__ SmiToInteger64(rcx, rcx);
__ leap(rdx, Operand(rdx, rcx, times_pointer_size,
StandardFrameConstants::kCallerSPOffset));
__ movp(args.GetArgumentOperand(0), rdx);
__ bind(&runtime);
__ TailCallRuntime(Runtime::kNewRestParam, 3, 1);
}
void LoadIndexedInterceptorStub::Generate(MacroAssembler* masm) {
// Return address is on the stack.
Label slow;

View File

@ -232,6 +232,26 @@ void FullCodeGenerator::Generate() {
}
}
// Possibly allocate RestParameters
int rest_index;
Variable* rest_param = scope()->rest_parameter(&rest_index);
if (rest_param) {
Comment cmnt(masm_, "[ Allocate rest parameter array");
int num_parameters = info->scope()->num_parameters();
int offset = num_parameters * kPointerSize;
__ leap(rdx,
Operand(rbp, StandardFrameConstants::kCallerSPOffset + offset));
__ Push(rdx);
__ Push(Smi::FromInt(num_parameters));
__ Push(Smi::FromInt(rest_index));
RestParamAccessStub stub(isolate());
__ CallStub(&stub);
SetVar(rest_param, rax, rbx, rdx);
}
// Possibly allocate an arguments object.
Variable* arguments = scope()->arguments();
if (arguments != NULL) {
@ -260,7 +280,7 @@ void FullCodeGenerator::Generate() {
? ArgumentsAccessStub::HAS_NEW_TARGET
: ArgumentsAccessStub::NO_NEW_TARGET;
ArgumentsAccessStub::Type type;
if (is_strict(language_mode())) {
if (is_strict(language_mode()) || !is_simple_parameter_list()) {
type = ArgumentsAccessStub::NEW_STRICT;
} else if (function()->has_duplicate_parameters()) {
type = ArgumentsAccessStub::NEW_SLOPPY_SLOW;

View File

@ -0,0 +1,17 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-rest-parameters --min-preparse-length=0
function variadic(co, ...values) {
var sum = 0;
while (values.length) {
sum += co * values.pop();
}
return sum;
}
assertEquals(90, variadic(2, 1, 2, 3, 4, 5, 6, 7, 8, 9));
assertEquals(74, variadic(2, 1, 2, 3, 4, 5, 6, 7, 9));
assertEquals(110, variadic(2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

View File

@ -0,0 +1,182 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-rest-parameters
(function testRestIndex() {
assertEquals(5, (function(...args) { return args.length; })(1,2,3,4,5));
assertEquals(4, (function(a, ...args) { return args.length; })(1,2,3,4,5));
assertEquals(3, (function(a, b, ...args) { return args.length; })(1,2,3,4,5));
assertEquals(2, (function(a, b, c, ...args) {
return args.length; })(1,2,3,4,5));
assertEquals(1, (function(a, b, c, d, ...args) {
return args.length; })(1,2,3,4,5));
assertEquals(0, (function(a, b, c, d, e, ...args) {
return args.length; })(1,2,3,4,5));
})();
function strictTest(a, b, ...c) {
"use strict";
assertEquals(Array, c.constructor);
assertTrue(Array.isArray(c));
var expectedLength = arguments.length >= 3 ? arguments.length - 2 : 0;
assertEquals(expectedLength, c.length);
for (var i = 2, j = 0; i < arguments.length; ++i) {
assertEquals(c[j++], arguments[i]);
}
}
function sloppyTest(a, b, ...c) {
assertEquals(Array, c.constructor);
assertTrue(Array.isArray(c));
var expectedLength = arguments.length >= 3 ? arguments.length - 2 : 0;
assertEquals(expectedLength, c.length);
for (var i = 2, j = 0; i < arguments.length; ++i) {
assertEquals(c[j++], arguments[i]);
}
}
var O = {
strict: strictTest,
sloppy: sloppyTest
};
(function testStrictRestParamArity() {
assertEquals(2, strictTest.length);
assertEquals(2, O.strict.length);
})();
(function testRestParamsStrictMode() {
strictTest();
strictTest(1, 2);
strictTest(1, 2, 3, 4, 5, 6);
strictTest(1, 2, 3);
O.strict();
O.strict(1, 2);
O.strict(1, 2, 3, 4, 5, 6);
O.strict(1, 2, 3);
})();
(function testRestParamsStrictModeApply() {
strictTest.apply(null, []);
strictTest.apply(null, [1, 2]);
strictTest.apply(null, [1, 2, 3, 4, 5, 6]);
strictTest.apply(null, [1, 2, 3]);
O.strict.apply(O, []);
O.strict.apply(O, [1, 2]);
O.strict.apply(O, [1, 2, 3, 4, 5, 6]);
O.strict.apply(O, [1, 2, 3]);
})();
(function testRestParamsStrictModeCall() {
strictTest.call(null);
strictTest.call(null, 1, 2);
strictTest.call(null, 1, 2, 3, 4, 5, 6);
strictTest.call(null, 1, 2, 3);
O.strict.call(O);
O.strict.call(O, 1, 2);
O.strict.call(O, 1, 2, 3, 4, 5, 6);
O.strict.call(O, 1, 2, 3);
})();
(function testsloppyRestParamArity() {
assertEquals(2, sloppyTest.length);
assertEquals(2, O.sloppy.length);
})();
(function testRestParamssloppyMode() {
sloppyTest();
sloppyTest(1, 2);
sloppyTest(1, 2, 3, 4, 5, 6);
sloppyTest(1, 2, 3);
O.sloppy();
O.sloppy(1, 2);
O.sloppy(1, 2, 3, 4, 5, 6);
O.sloppy(1, 2, 3);
})();
(function testRestParamssloppyModeApply() {
sloppyTest.apply(null, []);
sloppyTest.apply(null, [1, 2]);
sloppyTest.apply(null, [1, 2, 3, 4, 5, 6]);
sloppyTest.apply(null, [1, 2, 3]);
O.sloppy.apply(O, []);
O.sloppy.apply(O, [1, 2]);
O.sloppy.apply(O, [1, 2, 3, 4, 5, 6]);
O.sloppy.apply(O, [1, 2, 3]);
})();
(function testRestParamssloppyModeCall() {
sloppyTest.call(null);
sloppyTest.call(null, 1, 2);
sloppyTest.call(null, 1, 2, 3, 4, 5, 6);
sloppyTest.call(null, 1, 2, 3);
O.sloppy.call(O);
O.sloppy.call(O, 1, 2);
O.sloppy.call(O, 1, 2, 3, 4, 5, 6);
O.sloppy.call(O, 1, 2, 3);
})();
(function testUnmappedArguments() {
// Strict/Unmapped arguments should always be used for functions with rest
// parameters
assertThrows(function(...rest) { return arguments.caller; }, TypeError);
assertThrows(function(...rest) { return arguments.callee; }, TypeError);
// TODO(caitp): figure out why this doesn't throw sometimes, even though the
// getter always does =)
// assertThrows(function(...rest) { arguments.caller = 1; }, TypeError);
// assertThrows(function(...rest) { arguments.callee = 1; }, TypeError);
})();
(function testNoAliasArgumentsStrict() {
function strictF(a, ...rest) {
"use strict";
arguments[0] = 1;
assertEquals(3, a);
arguments[1] = 2;
assertArrayEquals([4, 5], rest);
}
strictF(3, 4, 5);
})();
(function testNoAliasArgumentsSloppy() {
function sloppyF(a, ...rest) {
arguments[0] = 1;
assertEquals(3, a);
arguments[1] = 2;
assertArrayEquals([4, 5], rest);
}
sloppyF(3, 4, 5);
})();
/* TODO(caitp): support arrow functions (blocked on spread operator support)
(function testRestParamsArrowFunctions() {
"use strict";
var fn = (a, b, ...c) => c;
assertEquals([], fn());
assertEquals([], fn(1, 2));
assertEquals([3], fn(1, 2, 3));
assertEquals([3, 4], fn(1, 2, 3, 4));
assertEquals([3, 4, 5], fn(1, 2, 3, 4, 5));
assertThrows("var x = ...y => y;", SyntaxError);
assertEquals([], ((...args) => args)());
assertEquals([1,2,3], ((...args) => args)(1,2,3));
})();*/