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:
parent
d0c2c2b32c
commit
8bb2e39774
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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") \
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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) { }
|
||||
|
@ -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)
|
||||
|
@ -424,6 +424,8 @@ class CompilationInfo {
|
||||
void PrintAstForTesting();
|
||||
#endif
|
||||
|
||||
bool is_simple_parameter_list();
|
||||
|
||||
protected:
|
||||
CompilationInfo(Handle<SharedFunctionInfo> shared_info,
|
||||
Zone* zone);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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_; }
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) \
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
17
test/mjsunit/harmony/rest-params-lazy-parsing.js
Normal file
17
test/mjsunit/harmony/rest-params-lazy-parsing.js
Normal 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));
|
182
test/mjsunit/harmony/rest-params.js
Normal file
182
test/mjsunit/harmony/rest-params.js
Normal 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));
|
||||
})();*/
|
Loading…
Reference in New Issue
Block a user