Allocate arguments object on-demand instead of at function entry.
This allows Function.prototype.apply to not allocate the objects and copy the arguments directly from the stack. Review URL: http://codereview.chromium.org/147075 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2256 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
69764a5d2a
commit
f66ea38c0b
@ -511,7 +511,10 @@ Object* Accessors::FunctionGetArguments(Object* object, void*) {
|
|||||||
// If there is an arguments variable in the stack, we return that.
|
// If there is an arguments variable in the stack, we return that.
|
||||||
int index = ScopeInfo<>::StackSlotIndex(frame->code(),
|
int index = ScopeInfo<>::StackSlotIndex(frame->code(),
|
||||||
Heap::arguments_symbol());
|
Heap::arguments_symbol());
|
||||||
if (index >= 0) return frame->GetExpression(index);
|
if (index >= 0) {
|
||||||
|
Handle<Object> arguments = Handle<Object>(frame->GetExpression(index));
|
||||||
|
if (!arguments->IsTheHole()) return *arguments;
|
||||||
|
}
|
||||||
|
|
||||||
// If there isn't an arguments variable in the stack, we need to
|
// If there isn't an arguments variable in the stack, we need to
|
||||||
// find the frame that holds the actual arguments passed to the
|
// find the frame that holds the actual arguments passed to the
|
||||||
|
@ -68,7 +68,7 @@ VariableProxy::VariableProxy(Handle<String> name,
|
|||||||
// names must be canonicalized for fast equality checks
|
// names must be canonicalized for fast equality checks
|
||||||
ASSERT(name->IsSymbol());
|
ASSERT(name->IsSymbol());
|
||||||
// at least one access, otherwise no need for a VariableProxy
|
// at least one access, otherwise no need for a VariableProxy
|
||||||
var_uses_.RecordAccess(1);
|
var_uses_.RecordRead(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
16
src/ast.h
16
src/ast.h
@ -802,13 +802,20 @@ class VariableProxy: public Expression {
|
|||||||
Variable* AsVariable() {
|
Variable* AsVariable() {
|
||||||
return this == NULL || var_ == NULL ? NULL : var_->AsVariable();
|
return this == NULL || var_ == NULL ? NULL : var_->AsVariable();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool IsValidLeftHandSide() {
|
virtual bool IsValidLeftHandSide() {
|
||||||
return var_ == NULL ? true : var_->IsValidLeftHandSide();
|
return var_ == NULL ? true : var_->IsValidLeftHandSide();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsVariable(Handle<String> n) {
|
bool IsVariable(Handle<String> n) {
|
||||||
return !is_this() && name().is_identical_to(n);
|
return !is_this() && name().is_identical_to(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsArguments() {
|
||||||
|
Variable* variable = AsVariable();
|
||||||
|
return (variable == NULL) ? false : variable->is_arguments();
|
||||||
|
}
|
||||||
|
|
||||||
// If this assertion fails it means that some code has tried to
|
// If this assertion fails it means that some code has tried to
|
||||||
// treat the special "this" variable as an ordinary variable with
|
// treat the special "this" variable as an ordinary variable with
|
||||||
// the name "this".
|
// the name "this".
|
||||||
@ -890,12 +897,13 @@ class Slot: public Expression {
|
|||||||
virtual void Accept(AstVisitor* v);
|
virtual void Accept(AstVisitor* v);
|
||||||
|
|
||||||
// Type testing & conversion
|
// Type testing & conversion
|
||||||
virtual Slot* AsSlot() { return this; }
|
virtual Slot* AsSlot() { return this; }
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
Variable* var() const { return var_; }
|
Variable* var() const { return var_; }
|
||||||
Type type() const { return type_; }
|
Type type() const { return type_; }
|
||||||
int index() const { return index_; }
|
int index() const { return index_; }
|
||||||
|
bool is_arguments() const { return var_->is_arguments(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Variable* var_;
|
Variable* var_;
|
||||||
|
@ -175,18 +175,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
|
|||||||
function_return_.set_direction(JumpTarget::BIDIRECTIONAL);
|
function_return_.set_direction(JumpTarget::BIDIRECTIONAL);
|
||||||
function_return_is_shadowed_ = false;
|
function_return_is_shadowed_ = false;
|
||||||
|
|
||||||
// Allocate the arguments object and copy the parameters into it.
|
// Allocate the local context if needed.
|
||||||
if (scope_->arguments() != NULL) {
|
|
||||||
ASSERT(scope_->arguments_shadow() != NULL);
|
|
||||||
Comment cmnt(masm_, "[ Allocate arguments object");
|
|
||||||
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
|
|
||||||
frame_->PushFunction();
|
|
||||||
frame_->PushReceiverSlotAddress();
|
|
||||||
frame_->Push(Smi::FromInt(scope_->num_parameters()));
|
|
||||||
Result answer = frame_->CallStub(&stub, 3);
|
|
||||||
frame_->Push(&answer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope_->num_heap_slots() > 0) {
|
if (scope_->num_heap_slots() > 0) {
|
||||||
Comment cmnt(masm_, "[ allocate local context");
|
Comment cmnt(masm_, "[ allocate local context");
|
||||||
// Allocate local context.
|
// Allocate local context.
|
||||||
@ -247,27 +236,11 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This section stores the pointer to the arguments object that
|
|
||||||
// was allocated and copied into above. If the address was not
|
|
||||||
// saved to TOS, we push ecx onto the stack.
|
|
||||||
//
|
|
||||||
// Store the arguments object. This must happen after context
|
// Store the arguments object. This must happen after context
|
||||||
// initialization because the arguments object may be stored in the
|
// initialization because the arguments object may be stored in
|
||||||
// context.
|
// the context.
|
||||||
if (scope_->arguments() != NULL) {
|
if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
|
||||||
Comment cmnt(masm_, "[ store arguments object");
|
StoreArgumentsObject(true);
|
||||||
{ Reference shadow_ref(this, scope_->arguments_shadow());
|
|
||||||
ASSERT(shadow_ref.is_slot());
|
|
||||||
{ Reference arguments_ref(this, scope_->arguments());
|
|
||||||
ASSERT(arguments_ref.is_slot());
|
|
||||||
// Here we rely on the convenient property that references to slot
|
|
||||||
// take up zero space in the frame (ie, it doesn't matter that the
|
|
||||||
// stored value is actually below the reference on the frame).
|
|
||||||
arguments_ref.SetValue(NOT_CONST_INIT);
|
|
||||||
}
|
|
||||||
shadow_ref.SetValue(NOT_CONST_INIT);
|
|
||||||
}
|
|
||||||
frame_->Drop(); // Value is no longer needed.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate code to 'execute' declarations and initialize functions
|
// Generate code to 'execute' declarations and initialize functions
|
||||||
@ -591,6 +564,71 @@ void CodeGenerator::LoadTypeofExpression(Expression* x) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ArgumentsAllocationMode CodeGenerator::ArgumentsMode() const {
|
||||||
|
if (scope_->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
|
||||||
|
ASSERT(scope_->arguments_shadow() != NULL);
|
||||||
|
// We don't want to do lazy arguments allocation for functions that
|
||||||
|
// have heap-allocated contexts, because it interfers with the
|
||||||
|
// uninitialized const tracking in the context objects.
|
||||||
|
return (scope_->num_heap_slots() > 0)
|
||||||
|
? EAGER_ARGUMENTS_ALLOCATION
|
||||||
|
: LAZY_ARGUMENTS_ALLOCATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Result CodeGenerator::StoreArgumentsObject(bool initial) {
|
||||||
|
ArgumentsAllocationMode mode = ArgumentsMode();
|
||||||
|
ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
|
||||||
|
|
||||||
|
Comment cmnt(masm_, "[ store arguments object");
|
||||||
|
if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
|
||||||
|
// When using lazy arguments allocation, we store the hole value
|
||||||
|
// as a sentinel indicating that the arguments object hasn't been
|
||||||
|
// allocated yet.
|
||||||
|
frame_->Push(Factory::the_hole_value());
|
||||||
|
} else {
|
||||||
|
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
|
||||||
|
frame_->PushFunction();
|
||||||
|
frame_->PushReceiverSlotAddress();
|
||||||
|
frame_->Push(Smi::FromInt(scope_->num_parameters()));
|
||||||
|
Result result = frame_->CallStub(&stub, 3);
|
||||||
|
frame_->Push(&result);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ Reference shadow_ref(this, scope_->arguments_shadow());
|
||||||
|
Reference arguments_ref(this, scope_->arguments());
|
||||||
|
ASSERT(shadow_ref.is_slot() && arguments_ref.is_slot());
|
||||||
|
// Here we rely on the convenient property that references to slot
|
||||||
|
// take up zero space in the frame (ie, it doesn't matter that the
|
||||||
|
// stored value is actually below the reference on the frame).
|
||||||
|
JumpTarget done;
|
||||||
|
bool skip_arguments = false;
|
||||||
|
if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
|
||||||
|
// We have to skip storing into the arguments slot if it has
|
||||||
|
// already been written to. This can happen if the a function
|
||||||
|
// has a local variable named 'arguments'.
|
||||||
|
LoadFromSlot(scope_->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
|
||||||
|
Result arguments = frame_->Pop();
|
||||||
|
if (arguments.is_constant()) {
|
||||||
|
// We have to skip updating the arguments object if it has
|
||||||
|
// been assigned a proper value.
|
||||||
|
skip_arguments = !arguments.handle()->IsTheHole();
|
||||||
|
} else {
|
||||||
|
__ cmp(Operand(arguments.reg()), Immediate(Factory::the_hole_value()));
|
||||||
|
arguments.Unuse();
|
||||||
|
done.Branch(not_equal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!skip_arguments) {
|
||||||
|
arguments_ref.SetValue(NOT_CONST_INIT);
|
||||||
|
if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
|
||||||
|
}
|
||||||
|
shadow_ref.SetValue(NOT_CONST_INIT);
|
||||||
|
}
|
||||||
|
return frame_->Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference::Reference(CodeGenerator* cgen, Expression* expression)
|
Reference::Reference(CodeGenerator* cgen, Expression* expression)
|
||||||
: cgen_(cgen), expression_(expression), type_(ILLEGAL) {
|
: cgen_(cgen), expression_(expression), type_(ILLEGAL) {
|
||||||
cgen->LoadReference(this);
|
cgen->LoadReference(this);
|
||||||
@ -2090,6 +2128,172 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CodeGenerator::CallApplyLazy(Property* apply,
|
||||||
|
Expression* receiver,
|
||||||
|
VariableProxy* arguments,
|
||||||
|
int position) {
|
||||||
|
ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
|
||||||
|
ASSERT(arguments->IsArguments());
|
||||||
|
|
||||||
|
JumpTarget slow, done;
|
||||||
|
|
||||||
|
// Load the apply function onto the stack. This will usually
|
||||||
|
// give us a megamorphic load site. Not super, but it works.
|
||||||
|
Reference ref(this, apply);
|
||||||
|
ref.GetValue(NOT_INSIDE_TYPEOF);
|
||||||
|
ASSERT(ref.type() == Reference::NAMED);
|
||||||
|
|
||||||
|
// Load the receiver and the existing arguments object onto the
|
||||||
|
// expression stack. Avoid allocating the arguments object here.
|
||||||
|
Load(receiver);
|
||||||
|
LoadFromSlot(scope_->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
|
||||||
|
|
||||||
|
// Emit the source position information after having loaded the
|
||||||
|
// receiver and the arguments.
|
||||||
|
CodeForSourcePosition(position);
|
||||||
|
|
||||||
|
// Check if the arguments object has been lazily allocated
|
||||||
|
// already. If so, just use that instead of copying the arguments
|
||||||
|
// from the stack. This also deals with cases where a local variable
|
||||||
|
// named 'arguments' has been introduced.
|
||||||
|
frame_->Dup();
|
||||||
|
Result probe = frame_->Pop();
|
||||||
|
bool try_lazy = true;
|
||||||
|
if (probe.is_constant()) {
|
||||||
|
try_lazy = probe.handle()->IsTheHole();
|
||||||
|
} else {
|
||||||
|
__ cmp(Operand(probe.reg()), Immediate(Factory::the_hole_value()));
|
||||||
|
probe.Unuse();
|
||||||
|
slow.Branch(not_equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try_lazy) {
|
||||||
|
JumpTarget build_args;
|
||||||
|
|
||||||
|
// Get rid of the arguments object probe.
|
||||||
|
frame_->Drop();
|
||||||
|
|
||||||
|
// Before messing with the execution stack, we sync all
|
||||||
|
// elements. This is bound to happen anyway because we're
|
||||||
|
// about to call a function.
|
||||||
|
frame_->SyncRange(0, frame_->element_count() - 1);
|
||||||
|
|
||||||
|
// Check that the receiver really is a JavaScript object.
|
||||||
|
{ frame_->PushElementAt(0);
|
||||||
|
Result receiver = frame_->Pop();
|
||||||
|
receiver.ToRegister();
|
||||||
|
__ test(receiver.reg(), Immediate(kSmiTagMask));
|
||||||
|
build_args.Branch(zero);
|
||||||
|
Result tmp = allocator_->Allocate();
|
||||||
|
__ CmpObjectType(receiver.reg(), FIRST_JS_OBJECT_TYPE, tmp.reg());
|
||||||
|
build_args.Branch(less);
|
||||||
|
__ cmp(tmp.reg(), LAST_JS_OBJECT_TYPE);
|
||||||
|
build_args.Branch(greater);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that we're invoking Function.prototype.apply.
|
||||||
|
{ frame_->PushElementAt(1);
|
||||||
|
Result apply = frame_->Pop();
|
||||||
|
apply.ToRegister();
|
||||||
|
__ test(apply.reg(), Immediate(kSmiTagMask));
|
||||||
|
build_args.Branch(zero);
|
||||||
|
Result tmp = allocator_->Allocate();
|
||||||
|
__ CmpObjectType(apply.reg(), JS_FUNCTION_TYPE, tmp.reg());
|
||||||
|
build_args.Branch(not_equal);
|
||||||
|
__ mov(tmp.reg(),
|
||||||
|
FieldOperand(apply.reg(), JSFunction::kSharedFunctionInfoOffset));
|
||||||
|
Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply));
|
||||||
|
__ cmp(FieldOperand(tmp.reg(), SharedFunctionInfo::kCodeOffset),
|
||||||
|
Immediate(apply_code));
|
||||||
|
build_args.Branch(not_equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the function receiver from the stack. Check that it
|
||||||
|
// really is a function.
|
||||||
|
__ mov(edi, Operand(esp, 2 * kPointerSize));
|
||||||
|
__ test(edi, Immediate(kSmiTagMask));
|
||||||
|
build_args.Branch(zero);
|
||||||
|
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
|
||||||
|
build_args.Branch(not_equal);
|
||||||
|
|
||||||
|
// Copy the arguments to this function possibly from the
|
||||||
|
// adaptor frame below it.
|
||||||
|
Label invoke, adapted;
|
||||||
|
__ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
|
||||||
|
__ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
|
||||||
|
__ cmp(ecx, ArgumentsAdaptorFrame::SENTINEL);
|
||||||
|
__ j(equal, &adapted);
|
||||||
|
|
||||||
|
// No arguments adaptor frame. Copy fixed number of arguments.
|
||||||
|
__ mov(eax, Immediate(scope_->num_parameters()));
|
||||||
|
for (int i = 0; i < scope_->num_parameters(); i++) {
|
||||||
|
__ push(frame_->ParameterAt(i));
|
||||||
|
}
|
||||||
|
__ jmp(&invoke);
|
||||||
|
|
||||||
|
// Arguments adaptor frame present. Copy arguments from there, but
|
||||||
|
// avoid copying too many arguments to avoid stack overflows.
|
||||||
|
__ bind(&adapted);
|
||||||
|
static const uint32_t kArgumentsLimit = 1 * KB;
|
||||||
|
__ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
|
||||||
|
__ shr(eax, kSmiTagSize);
|
||||||
|
__ mov(ecx, Operand(eax));
|
||||||
|
__ cmp(eax, kArgumentsLimit);
|
||||||
|
build_args.Branch(above);
|
||||||
|
|
||||||
|
// Loop through the arguments pushing them onto the execution
|
||||||
|
// stack. We don't inform the virtual frame of the push, so we don't
|
||||||
|
// have to worry about getting rid of the elements from the virtual
|
||||||
|
// frame.
|
||||||
|
Label loop;
|
||||||
|
__ bind(&loop);
|
||||||
|
__ test(ecx, Operand(ecx));
|
||||||
|
__ j(zero, &invoke);
|
||||||
|
__ push(Operand(edx, ecx, times_4, 1 * kPointerSize));
|
||||||
|
__ dec(ecx);
|
||||||
|
__ jmp(&loop);
|
||||||
|
|
||||||
|
// Invoke the function. The virtual frame knows about the receiver
|
||||||
|
// so make sure to forget that explicitly.
|
||||||
|
__ bind(&invoke);
|
||||||
|
ParameterCount actual(eax);
|
||||||
|
__ InvokeFunction(edi, actual, CALL_FUNCTION);
|
||||||
|
frame_->Forget(1);
|
||||||
|
Result result = allocator()->Allocate(eax);
|
||||||
|
frame_->SetElementAt(0, &result);
|
||||||
|
done.Jump();
|
||||||
|
|
||||||
|
// Slow-case: Allocate the arguments object since we know it isn't
|
||||||
|
// there, and fall-through to the slow-case where we call
|
||||||
|
// Function.prototype.apply.
|
||||||
|
build_args.Bind();
|
||||||
|
Result arguments_object = StoreArgumentsObject(false);
|
||||||
|
frame_->Push(&arguments_object);
|
||||||
|
slow.Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flip the apply function and the function to call on the stack, so
|
||||||
|
// the function looks like the receiver of the apply call. This way,
|
||||||
|
// the generic Function.prototype.apply implementation can deal with
|
||||||
|
// the call like it usually does.
|
||||||
|
Result a2 = frame_->Pop();
|
||||||
|
Result a1 = frame_->Pop();
|
||||||
|
Result ap = frame_->Pop();
|
||||||
|
Result fn = frame_->Pop();
|
||||||
|
frame_->Push(&ap);
|
||||||
|
frame_->Push(&fn);
|
||||||
|
frame_->Push(&a1);
|
||||||
|
frame_->Push(&a2);
|
||||||
|
CallFunctionStub call_function(2, NOT_IN_LOOP);
|
||||||
|
Result res = frame_->CallStub(&call_function, 3);
|
||||||
|
frame_->Push(&res);
|
||||||
|
|
||||||
|
// All done. Restore context register after call.
|
||||||
|
if (try_lazy) done.Bind();
|
||||||
|
frame_->RestoreContextRegister();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class DeferredStackCheck: public DeferredCode {
|
class DeferredStackCheck: public DeferredCode {
|
||||||
public:
|
public:
|
||||||
DeferredStackCheck() {
|
DeferredStackCheck() {
|
||||||
@ -3615,6 +3819,44 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
|
||||||
|
TypeofState state) {
|
||||||
|
LoadFromSlot(slot, state);
|
||||||
|
|
||||||
|
// Bail out quickly if we're not using lazy arguments allocation.
|
||||||
|
if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
|
||||||
|
|
||||||
|
// ... or if the slot isn't a non-parameter arguments slot.
|
||||||
|
if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
|
||||||
|
|
||||||
|
// Pop the loaded value from the stack.
|
||||||
|
Result value = frame_->Pop();
|
||||||
|
|
||||||
|
// If the loaded value is a constant, we know if the arguments
|
||||||
|
// object has been lazily loaded yet.
|
||||||
|
if (value.is_constant()) {
|
||||||
|
if (value.handle()->IsTheHole()) {
|
||||||
|
Result arguments = StoreArgumentsObject(false);
|
||||||
|
frame_->Push(&arguments);
|
||||||
|
} else {
|
||||||
|
frame_->Push(&value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The loaded value is in a register. If it is the sentinel that
|
||||||
|
// indicates that we haven't loaded the arguments object yet, we
|
||||||
|
// need to do it now.
|
||||||
|
JumpTarget exit;
|
||||||
|
__ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value()));
|
||||||
|
frame_->Push(&value);
|
||||||
|
exit.Branch(not_equal);
|
||||||
|
Result arguments = StoreArgumentsObject(false);
|
||||||
|
frame_->SetElementAt(0, &arguments);
|
||||||
|
exit.Bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
|
Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
|
||||||
Slot* slot,
|
Slot* slot,
|
||||||
TypeofState typeof_state,
|
TypeofState typeof_state,
|
||||||
@ -3787,7 +4029,7 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
|
|||||||
|
|
||||||
void CodeGenerator::VisitSlot(Slot* node) {
|
void CodeGenerator::VisitSlot(Slot* node) {
|
||||||
Comment cmnt(masm_, "[ Slot");
|
Comment cmnt(masm_, "[ Slot");
|
||||||
LoadFromSlot(node, typeof_state());
|
LoadFromSlotCheckForArguments(node, typeof_state());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -4349,24 +4591,41 @@ void CodeGenerator::VisitCall(Call* node) {
|
|||||||
// JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
|
// JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
// Push the name of the function and the receiver onto the stack.
|
Handle<String> name = Handle<String>::cast(literal->handle());
|
||||||
frame_->Push(literal->handle());
|
|
||||||
Load(property->obj());
|
|
||||||
|
|
||||||
// Load the arguments.
|
if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
|
||||||
int arg_count = args->length();
|
name->IsEqualTo(CStrVector("apply")) &&
|
||||||
for (int i = 0; i < arg_count; i++) {
|
args->length() == 2 &&
|
||||||
Load(args->at(i));
|
args->at(1)->AsVariableProxy() != NULL &&
|
||||||
|
args->at(1)->AsVariableProxy()->IsArguments()) {
|
||||||
|
// Use the optimized Function.prototype.apply that avoids
|
||||||
|
// allocating lazily allocated arguments objects.
|
||||||
|
CallApplyLazy(property,
|
||||||
|
args->at(0),
|
||||||
|
args->at(1)->AsVariableProxy(),
|
||||||
|
node->position());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Push the name of the function and the receiver onto the stack.
|
||||||
|
frame_->Push(name);
|
||||||
|
Load(property->obj());
|
||||||
|
|
||||||
|
// Load the arguments.
|
||||||
|
int arg_count = args->length();
|
||||||
|
for (int i = 0; i < arg_count; i++) {
|
||||||
|
Load(args->at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the IC initialization code.
|
||||||
|
CodeForSourcePosition(node->position());
|
||||||
|
Result result =
|
||||||
|
frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count,
|
||||||
|
loop_nesting());
|
||||||
|
frame_->RestoreContextRegister();
|
||||||
|
// Replace the function on the stack with the result.
|
||||||
|
frame_->SetElementAt(0, &result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the IC initialization code.
|
|
||||||
CodeForSourcePosition(node->position());
|
|
||||||
Result result =
|
|
||||||
frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count, loop_nesting());
|
|
||||||
frame_->RestoreContextRegister();
|
|
||||||
// Replace the function on the stack with the result.
|
|
||||||
frame_->SetElementAt(0, &result);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
// JavaScript example: 'array[index](1, 2, 3)'
|
// JavaScript example: 'array[index](1, 2, 3)'
|
||||||
@ -5838,7 +6097,7 @@ void Reference::GetValue(TypeofState typeof_state) {
|
|||||||
Comment cmnt(masm, "[ Load from Slot");
|
Comment cmnt(masm, "[ Load from Slot");
|
||||||
Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
|
Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
|
||||||
ASSERT(slot != NULL);
|
ASSERT(slot != NULL);
|
||||||
cgen_->LoadFromSlot(slot, typeof_state);
|
cgen_->LoadFromSlotCheckForArguments(slot, typeof_state);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,6 +273,14 @@ class CodeGenState BASE_EMBEDDED {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Arguments allocation mode
|
||||||
|
|
||||||
|
enum ArgumentsAllocationMode {
|
||||||
|
NO_ARGUMENTS_ALLOCATION,
|
||||||
|
EAGER_ARGUMENTS_ALLOCATION,
|
||||||
|
LAZY_ARGUMENTS_ALLOCATION
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@ -332,12 +340,11 @@ class CodeGenerator: public AstVisitor {
|
|||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
Scope* scope() const { return scope_; }
|
Scope* scope() const { return scope_; }
|
||||||
|
bool is_eval() { return is_eval_; }
|
||||||
|
|
||||||
// Generating deferred code.
|
// Generating deferred code.
|
||||||
void ProcessDeferred();
|
void ProcessDeferred();
|
||||||
|
|
||||||
bool is_eval() { return is_eval_; }
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
TypeofState typeof_state() const { return state_->typeof_state(); }
|
TypeofState typeof_state() const { return state_->typeof_state(); }
|
||||||
ControlDestination* destination() const { return state_->destination(); }
|
ControlDestination* destination() const { return state_->destination(); }
|
||||||
@ -373,6 +380,12 @@ class CodeGenerator: public AstVisitor {
|
|||||||
// target (which can not be done more than once).
|
// target (which can not be done more than once).
|
||||||
void GenerateReturnSequence(Result* return_value);
|
void GenerateReturnSequence(Result* return_value);
|
||||||
|
|
||||||
|
// Returns the arguments allocation mode.
|
||||||
|
ArgumentsAllocationMode ArgumentsMode() const;
|
||||||
|
|
||||||
|
// Store the arguments object and allocate it if necessary.
|
||||||
|
Result StoreArgumentsObject(bool initial);
|
||||||
|
|
||||||
// The following are used by class Reference.
|
// The following are used by class Reference.
|
||||||
void LoadReference(Reference* ref);
|
void LoadReference(Reference* ref);
|
||||||
void UnloadReference(Reference* ref);
|
void UnloadReference(Reference* ref);
|
||||||
@ -408,6 +421,7 @@ class CodeGenerator: public AstVisitor {
|
|||||||
|
|
||||||
// Read a value from a slot and leave it on top of the expression stack.
|
// Read a value from a slot and leave it on top of the expression stack.
|
||||||
void LoadFromSlot(Slot* slot, TypeofState typeof_state);
|
void LoadFromSlot(Slot* slot, TypeofState typeof_state);
|
||||||
|
void LoadFromSlotCheckForArguments(Slot* slot, TypeofState typeof_state);
|
||||||
Result LoadFromGlobalSlotCheckExtensions(Slot* slot,
|
Result LoadFromGlobalSlotCheckExtensions(Slot* slot,
|
||||||
TypeofState typeof_state,
|
TypeofState typeof_state,
|
||||||
JumpTarget* slow);
|
JumpTarget* slow);
|
||||||
@ -470,6 +484,14 @@ class CodeGenerator: public AstVisitor {
|
|||||||
|
|
||||||
void CallWithArguments(ZoneList<Expression*>* arguments, int position);
|
void CallWithArguments(ZoneList<Expression*>* arguments, int position);
|
||||||
|
|
||||||
|
// Use an optimized version of Function.prototype.apply that avoid
|
||||||
|
// allocating the arguments object and just copies the arguments
|
||||||
|
// from the stack.
|
||||||
|
void CallApplyLazy(Property* apply,
|
||||||
|
Expression* receiver,
|
||||||
|
VariableProxy* arguments,
|
||||||
|
int position);
|
||||||
|
|
||||||
void CheckStack();
|
void CheckStack();
|
||||||
|
|
||||||
struct InlineRuntimeLUT {
|
struct InlineRuntimeLUT {
|
||||||
|
@ -1582,7 +1582,8 @@ VariableProxy* AstBuildingParser::Declare(Handle<String> name,
|
|||||||
// For global const variables we bind the proxy to a variable.
|
// For global const variables we bind the proxy to a variable.
|
||||||
if (mode == Variable::CONST && top_scope_->is_global_scope()) {
|
if (mode == Variable::CONST && top_scope_->is_global_scope()) {
|
||||||
ASSERT(resolve); // should be set by all callers
|
ASSERT(resolve); // should be set by all callers
|
||||||
var = NEW(Variable(top_scope_, name, Variable::CONST, true, false));
|
Variable::Kind kind = Variable::NORMAL;
|
||||||
|
var = NEW(Variable(top_scope_, name, Variable::CONST, true, kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If requested and we have a local variable, bind the proxy to the variable
|
// If requested and we have a local variable, bind the proxy to the variable
|
||||||
|
@ -391,8 +391,9 @@ function CALL_NON_FUNCTION_AS_CONSTRUCTOR() {
|
|||||||
|
|
||||||
function APPLY_PREPARE(args) {
|
function APPLY_PREPARE(args) {
|
||||||
var length;
|
var length;
|
||||||
// First check whether length is a positive Smi and args is an array. This is the
|
// First check whether length is a positive Smi and args is an
|
||||||
// fast case. If this fails, we do the slow case that takes care of more eventualities
|
// array. This is the fast case. If this fails, we do the slow case
|
||||||
|
// that takes care of more eventualities.
|
||||||
if (%_IsArray(args)) {
|
if (%_IsArray(args)) {
|
||||||
length = args.length;
|
length = args.length;
|
||||||
if (%_IsSmi(length) && length >= 0 && length < 0x800000 && IS_FUNCTION(this)) {
|
if (%_IsSmi(length) && length >= 0 && length < 0x800000 && IS_FUNCTION(this)) {
|
||||||
|
@ -81,12 +81,12 @@ Variable* LocalsMap::Declare(Scope* scope,
|
|||||||
Handle<String> name,
|
Handle<String> name,
|
||||||
Variable::Mode mode,
|
Variable::Mode mode,
|
||||||
bool is_valid_LHS,
|
bool is_valid_LHS,
|
||||||
bool is_this) {
|
Variable::Kind kind) {
|
||||||
HashMap::Entry* p = HashMap::Lookup(name.location(), name->Hash(), true);
|
HashMap::Entry* p = HashMap::Lookup(name.location(), name->Hash(), true);
|
||||||
if (p->value == NULL) {
|
if (p->value == NULL) {
|
||||||
// The variable has not been declared yet -> insert it.
|
// The variable has not been declared yet -> insert it.
|
||||||
ASSERT(p->key == name.location());
|
ASSERT(p->key == name.location());
|
||||||
p->value = new Variable(scope, name, mode, is_valid_LHS, is_this);
|
p->value = new Variable(scope, name, mode, is_valid_LHS, kind);
|
||||||
}
|
}
|
||||||
return reinterpret_cast<Variable*>(p->value);
|
return reinterpret_cast<Variable*>(p->value);
|
||||||
}
|
}
|
||||||
@ -169,7 +169,8 @@ void Scope::Initialize(bool inside_with) {
|
|||||||
// such parameter is 'this' which is passed on the stack when
|
// such parameter is 'this' which is passed on the stack when
|
||||||
// invoking scripts
|
// invoking scripts
|
||||||
{ Variable* var =
|
{ Variable* var =
|
||||||
locals_.Declare(this, Factory::this_symbol(), Variable::VAR, false, true);
|
locals_.Declare(this, Factory::this_symbol(), Variable::VAR,
|
||||||
|
false, Variable::THIS);
|
||||||
var->rewrite_ = new Slot(var, Slot::PARAMETER, -1);
|
var->rewrite_ = new Slot(var, Slot::PARAMETER, -1);
|
||||||
receiver_ = new VariableProxy(Factory::this_symbol(), true, false);
|
receiver_ = new VariableProxy(Factory::this_symbol(), true, false);
|
||||||
receiver_->BindTo(var);
|
receiver_->BindTo(var);
|
||||||
@ -179,7 +180,8 @@ void Scope::Initialize(bool inside_with) {
|
|||||||
// Declare 'arguments' variable which exists in all functions.
|
// Declare 'arguments' variable which exists in all functions.
|
||||||
// Note that it may never be accessed, in which case it won't
|
// Note that it may never be accessed, in which case it won't
|
||||||
// be allocated during variable allocation.
|
// be allocated during variable allocation.
|
||||||
Declare(Factory::arguments_symbol(), Variable::VAR);
|
locals_.Declare(this, Factory::arguments_symbol(), Variable::VAR,
|
||||||
|
true, Variable::ARGUMENTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +205,7 @@ Variable* Scope::Lookup(Handle<String> name) {
|
|||||||
|
|
||||||
Variable* Scope::DeclareFunctionVar(Handle<String> name) {
|
Variable* Scope::DeclareFunctionVar(Handle<String> name) {
|
||||||
ASSERT(is_function_scope() && function_ == NULL);
|
ASSERT(is_function_scope() && function_ == NULL);
|
||||||
function_ = new Variable(this, name, Variable::CONST, true, false);
|
function_ = new Variable(this, name, Variable::CONST, true, Variable::NORMAL);
|
||||||
return function_;
|
return function_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +215,7 @@ Variable* Scope::Declare(Handle<String> name, Variable::Mode mode) {
|
|||||||
// INTERNAL variables are allocated explicitly, and TEMPORARY
|
// INTERNAL variables are allocated explicitly, and TEMPORARY
|
||||||
// variables are allocated via NewTemporary().
|
// variables are allocated via NewTemporary().
|
||||||
ASSERT(mode == Variable::VAR || mode == Variable::CONST);
|
ASSERT(mode == Variable::VAR || mode == Variable::CONST);
|
||||||
return locals_.Declare(this, name, mode, true, false);
|
return locals_.Declare(this, name, mode, true, Variable::NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -247,7 +249,8 @@ void Scope::RemoveUnresolved(VariableProxy* var) {
|
|||||||
|
|
||||||
|
|
||||||
VariableProxy* Scope::NewTemporary(Handle<String> name) {
|
VariableProxy* Scope::NewTemporary(Handle<String> name) {
|
||||||
Variable* var = new Variable(this, name, Variable::TEMPORARY, true, false);
|
Variable* var = new Variable(this, name, Variable::TEMPORARY, true,
|
||||||
|
Variable::NORMAL);
|
||||||
VariableProxy* tmp = new VariableProxy(name, false, false);
|
VariableProxy* tmp = new VariableProxy(name, false, false);
|
||||||
tmp->BindTo(var);
|
tmp->BindTo(var);
|
||||||
temps_.Add(var);
|
temps_.Add(var);
|
||||||
@ -503,7 +506,7 @@ Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) {
|
|||||||
Variable* var = map->Lookup(name);
|
Variable* var = map->Lookup(name);
|
||||||
if (var == NULL) {
|
if (var == NULL) {
|
||||||
// Declare a new non-local.
|
// Declare a new non-local.
|
||||||
var = map->Declare(NULL, name, mode, true, false);
|
var = map->Declare(NULL, name, mode, true, Variable::NORMAL);
|
||||||
// Allocate it by giving it a dynamic lookup.
|
// Allocate it by giving it a dynamic lookup.
|
||||||
var->rewrite_ = new Slot(var, Slot::LOOKUP, -1);
|
var->rewrite_ = new Slot(var, Slot::LOOKUP, -1);
|
||||||
}
|
}
|
||||||
@ -619,7 +622,7 @@ void Scope::ResolveVariable(Scope* global_scope,
|
|||||||
// We must have a global variable.
|
// We must have a global variable.
|
||||||
ASSERT(global_scope != NULL);
|
ASSERT(global_scope != NULL);
|
||||||
var = new Variable(global_scope, proxy->name(),
|
var = new Variable(global_scope, proxy->name(),
|
||||||
Variable::DYNAMIC, true, false);
|
Variable::DYNAMIC, true, Variable::NORMAL);
|
||||||
|
|
||||||
} else if (scope_inside_with_) {
|
} else if (scope_inside_with_) {
|
||||||
// If we are inside a with statement we give up and look up
|
// If we are inside a with statement we give up and look up
|
||||||
@ -797,7 +800,7 @@ void Scope::AllocateParameterLocals() {
|
|||||||
// are never allocated in the context).
|
// are never allocated in the context).
|
||||||
Variable* arguments_shadow =
|
Variable* arguments_shadow =
|
||||||
new Variable(this, Factory::arguments_shadow_symbol(),
|
new Variable(this, Factory::arguments_shadow_symbol(),
|
||||||
Variable::INTERNAL, true, false);
|
Variable::INTERNAL, true, Variable::ARGUMENTS);
|
||||||
arguments_shadow_ =
|
arguments_shadow_ =
|
||||||
new VariableProxy(Factory::arguments_shadow_symbol(), false, false);
|
new VariableProxy(Factory::arguments_shadow_symbol(), false, false);
|
||||||
arguments_shadow_->BindTo(arguments_shadow);
|
arguments_shadow_->BindTo(arguments_shadow);
|
||||||
|
@ -47,7 +47,7 @@ class LocalsMap: public HashMap {
|
|||||||
virtual ~LocalsMap();
|
virtual ~LocalsMap();
|
||||||
|
|
||||||
Variable* Declare(Scope* scope, Handle<String> name, Variable::Mode mode,
|
Variable* Declare(Scope* scope, Handle<String> name, Variable::Mode mode,
|
||||||
bool is_valid_LHS, bool is_this);
|
bool is_valid_LHS, Variable::Kind kind);
|
||||||
|
|
||||||
Variable* Lookup(Handle<String> name);
|
Variable* Lookup(Handle<String> name);
|
||||||
};
|
};
|
||||||
|
@ -140,12 +140,12 @@ Variable::Variable(Scope* scope,
|
|||||||
Handle<String> name,
|
Handle<String> name,
|
||||||
Mode mode,
|
Mode mode,
|
||||||
bool is_valid_LHS,
|
bool is_valid_LHS,
|
||||||
bool is_this)
|
Kind kind)
|
||||||
: scope_(scope),
|
: scope_(scope),
|
||||||
name_(name),
|
name_(name),
|
||||||
mode_(mode),
|
mode_(mode),
|
||||||
is_valid_LHS_(is_valid_LHS),
|
is_valid_LHS_(is_valid_LHS),
|
||||||
is_this_(is_this),
|
kind_(kind),
|
||||||
local_if_not_shadowed_(NULL),
|
local_if_not_shadowed_(NULL),
|
||||||
is_accessed_from_inner_scope_(false),
|
is_accessed_from_inner_scope_(false),
|
||||||
rewrite_(NULL) {
|
rewrite_(NULL) {
|
||||||
|
@ -137,6 +137,12 @@ class Variable: public ZoneObject {
|
|||||||
// in a context
|
// in a context
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Kind {
|
||||||
|
NORMAL,
|
||||||
|
THIS,
|
||||||
|
ARGUMENTS
|
||||||
|
};
|
||||||
|
|
||||||
// Printing support
|
// Printing support
|
||||||
static const char* Mode2String(Mode mode);
|
static const char* Mode2String(Mode mode);
|
||||||
|
|
||||||
@ -172,7 +178,8 @@ class Variable: public ZoneObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool is_global() const;
|
bool is_global() const;
|
||||||
bool is_this() const { return is_this_; }
|
bool is_this() const { return kind_ == THIS; }
|
||||||
|
bool is_arguments() const { return kind_ == ARGUMENTS; }
|
||||||
|
|
||||||
Variable* local_if_not_shadowed() const {
|
Variable* local_if_not_shadowed() const {
|
||||||
ASSERT(mode_ == DYNAMIC_LOCAL && local_if_not_shadowed_ != NULL);
|
ASSERT(mode_ == DYNAMIC_LOCAL && local_if_not_shadowed_ != NULL);
|
||||||
@ -190,13 +197,13 @@ class Variable: public ZoneObject {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Variable(Scope* scope, Handle<String> name, Mode mode, bool is_valid_LHS,
|
Variable(Scope* scope, Handle<String> name, Mode mode, bool is_valid_LHS,
|
||||||
bool is_this);
|
Kind kind);
|
||||||
|
|
||||||
Scope* scope_;
|
Scope* scope_;
|
||||||
Handle<String> name_;
|
Handle<String> name_;
|
||||||
Mode mode_;
|
Mode mode_;
|
||||||
bool is_valid_LHS_;
|
bool is_valid_LHS_;
|
||||||
bool is_this_;
|
Kind kind_;
|
||||||
|
|
||||||
Variable* local_if_not_shadowed_;
|
Variable* local_if_not_shadowed_;
|
||||||
|
|
||||||
|
@ -1257,6 +1257,10 @@ void CodeGenerator::VisitCall(Call* node) {
|
|||||||
// JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
|
// JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
// TODO(X64): Consider optimizing Function.prototype.apply calls
|
||||||
|
// with arguments object. Requires lazy arguments allocation;
|
||||||
|
// see http://codereview.chromium.org/147075.
|
||||||
|
|
||||||
// Push the name of the function and the receiver onto the stack.
|
// Push the name of the function and the receiver onto the stack.
|
||||||
frame_->Push(literal->handle());
|
frame_->Push(literal->handle());
|
||||||
Load(property->obj());
|
Load(property->obj());
|
||||||
|
127
test/mjsunit/arguments-apply.js
Normal file
127
test/mjsunit/arguments-apply.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
function ReturnArguments() {
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReturnReceiver() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Global() {
|
||||||
|
return ReturnArguments.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(0, Global().length);
|
||||||
|
assertEquals(1, Global(1).length);
|
||||||
|
assertEquals(2, Global(2)[0]);
|
||||||
|
assertEquals(2, Global(3, 4).length);
|
||||||
|
assertEquals(3, Global(3, 4)[0]);
|
||||||
|
assertEquals(4, Global(3, 4)[1]);
|
||||||
|
|
||||||
|
|
||||||
|
function Local() {
|
||||||
|
var object = { f: ReturnArguments };
|
||||||
|
return object.f.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(0, Local().length);
|
||||||
|
assertEquals(1, Local(1).length);
|
||||||
|
assertEquals(2, Local(2)[0]);
|
||||||
|
assertEquals(2, Local(3, 4).length);
|
||||||
|
assertEquals(3, Local(3, 4)[0]);
|
||||||
|
assertEquals(4, Local(3, 4)[1]);
|
||||||
|
|
||||||
|
|
||||||
|
function ShadowArguments() {
|
||||||
|
var arguments = [3, 4];
|
||||||
|
return ReturnArguments.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(2, ShadowArguments().length);
|
||||||
|
assertEquals(3, ShadowArguments()[0]);
|
||||||
|
assertEquals(4, ShadowArguments()[1]);
|
||||||
|
|
||||||
|
|
||||||
|
function NonObjectReceiver(receiver) {
|
||||||
|
return ReturnReceiver.apply(receiver, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(42, NonObjectReceiver(42));
|
||||||
|
assertEquals("object", typeof NonObjectReceiver(42));
|
||||||
|
assertTrue(NonObjectReceiver(42) instanceof Number);
|
||||||
|
assertTrue(this === NonObjectReceiver(null));
|
||||||
|
assertTrue(this === NonObjectReceiver(void 0));
|
||||||
|
|
||||||
|
|
||||||
|
function ShadowApply() {
|
||||||
|
function f() { return 42; }
|
||||||
|
f.apply = function() { return 87; }
|
||||||
|
return f.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(87, ShadowApply());
|
||||||
|
assertEquals(87, ShadowApply(1));
|
||||||
|
assertEquals(87, ShadowApply(1, 2));
|
||||||
|
|
||||||
|
|
||||||
|
function CallNonFunction() {
|
||||||
|
var object = { apply: Function.prototype.apply };
|
||||||
|
return object.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThrows(CallNonFunction, TypeError);
|
||||||
|
|
||||||
|
|
||||||
|
// Make sure that the stack after the apply optimization is
|
||||||
|
// in a valid state.
|
||||||
|
function SimpleStackCheck() {
|
||||||
|
var sentinel = 42;
|
||||||
|
var result = ReturnArguments.apply(this, arguments);
|
||||||
|
assertTrue(result != null);
|
||||||
|
assertEquals(42, sentinel);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleStackCheck();
|
||||||
|
|
||||||
|
|
||||||
|
function ShadowArgumentsWithConstant() {
|
||||||
|
var arguments = null;
|
||||||
|
return ReturnArguments.apply(this, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(0, ShadowArgumentsWithConstant().length);
|
||||||
|
assertEquals(0, ShadowArgumentsWithConstant(1).length);
|
||||||
|
assertEquals(0, ShadowArgumentsWithConstant(1, 2).length);
|
||||||
|
|
||||||
|
|
||||||
|
// Make sure we can deal with unfolding lots of arguments on the
|
||||||
|
// stack even in the presence of the apply optimizations.
|
||||||
|
var array = new Array(2048);
|
||||||
|
assertEquals(2048, Global.apply(this, array).length);
|
47
test/mjsunit/arguments-lazy.js
Normal file
47
test/mjsunit/arguments-lazy.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Make sure we don't allocate the arguments object over and
|
||||||
|
// over again.
|
||||||
|
function SharedLazyArguments() {
|
||||||
|
return arguments === arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(SharedLazyArguments());
|
||||||
|
|
||||||
|
|
||||||
|
// Make sure that accessing arguments doesn't clobber any
|
||||||
|
// local variables called arguments.
|
||||||
|
function ArgumentsOverride(x) {
|
||||||
|
var arguments = 42;
|
||||||
|
x = x ? x : 0;
|
||||||
|
return x + arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(42, ArgumentsOverride());
|
||||||
|
assertEquals(43, ArgumentsOverride(1));
|
||||||
|
assertEquals(44, ArgumentsOverride(2,3));
|
Loading…
Reference in New Issue
Block a user