// If there is an arguments variable in the stack, we return that.
int index = ScopeInfo<>::StackSlotIndex(frame->code(),
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
// find the frame that holds the actual arguments passed to the
// names must be canonicalized for fast equality checks
ASSERT(name->IsSymbol());
// at least one access, otherwise no need for a VariableProxy
- var_uses_.RecordAccess(1);
+ var_uses_.RecordRead(1);
}
Variable* AsVariable() {
return this == NULL || var_ == NULL ? NULL : var_->AsVariable();
}
+
virtual bool IsValidLeftHandSide() {
return var_ == NULL ? true : var_->IsValidLeftHandSide();
}
+
bool IsVariable(Handle<String> 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
// treat the special "this" variable as an ordinary variable with
// the name "this".
virtual void Accept(AstVisitor* v);
// Type testing & conversion
- virtual Slot* AsSlot() { return this; }
+ virtual Slot* AsSlot() { return this; }
// Accessors
- Variable* var() const { return var_; }
- Type type() const { return type_; }
- int index() const { return index_; }
+ Variable* var() const { return var_; }
+ Type type() const { return type_; }
+ int index() const { return index_; }
+ bool is_arguments() const { return var_->is_arguments(); }
private:
Variable* var_;
function_return_.set_direction(JumpTarget::BIDIRECTIONAL);
function_return_is_shadowed_ = false;
- // Allocate the arguments object and copy the parameters into it.
- 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);
- }
-
+ // Allocate the local context if needed.
if (scope_->num_heap_slots() > 0) {
Comment cmnt(masm_, "[ allocate local context");
// Allocate local context.
}
}
- // 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
- // initialization because the arguments object may be stored in the
- // context.
- if (scope_->arguments() != NULL) {
- Comment cmnt(masm_, "[ store arguments object");
- { 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.
+ // initialization because the arguments object may be stored in
+ // the context.
+ if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
+ StoreArgumentsObject(true);
}
// Generate code to 'execute' declarations and initialize functions
}
+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)
: cgen_(cgen), expression_(expression), type_(ILLEGAL) {
cgen->LoadReference(this);
}
+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 {
public:
DeferredStackCheck() {
}
+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(
Slot* slot,
TypeofState typeof_state,
void CodeGenerator::VisitSlot(Slot* node) {
Comment cmnt(masm_, "[ Slot");
- LoadFromSlot(node, typeof_state());
+ LoadFromSlotCheckForArguments(node, typeof_state());
}
// 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.
- frame_->Push(literal->handle());
- Load(property->obj());
+ Handle<String> name = Handle<String>::cast(literal->handle());
- // Load the arguments.
- int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- Load(args->at(i));
- }
+ if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
+ name->IsEqualTo(CStrVector("apply")) &&
+ args->length() == 2 &&
+ 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());
- // 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 {
+ // 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);
+ }
} else {
// -------------------------------------------
Comment cmnt(masm, "[ Load from Slot");
Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
ASSERT(slot != NULL);
- cgen_->LoadFromSlot(slot, typeof_state);
+ cgen_->LoadFromSlotCheckForArguments(slot, typeof_state);
break;
}
};
+// -------------------------------------------------------------------------
+// Arguments allocation mode
+
+enum ArgumentsAllocationMode {
+ NO_ARGUMENTS_ALLOCATION,
+ EAGER_ARGUMENTS_ALLOCATION,
+ LAZY_ARGUMENTS_ALLOCATION
+};
// -------------------------------------------------------------------------
// Accessors
Scope* scope() const { return scope_; }
+ bool is_eval() { return is_eval_; }
// Generating deferred code.
void ProcessDeferred();
- bool is_eval() { return is_eval_; }
-
// State
TypeofState typeof_state() const { return state_->typeof_state(); }
ControlDestination* destination() const { return state_->destination(); }
// target (which can not be done more than once).
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.
void LoadReference(Reference* ref);
void UnloadReference(Reference* ref);
// Read a value from a slot and leave it on top of the expression stack.
void LoadFromSlot(Slot* slot, TypeofState typeof_state);
+ void LoadFromSlotCheckForArguments(Slot* slot, TypeofState typeof_state);
Result LoadFromGlobalSlotCheckExtensions(Slot* slot,
TypeofState typeof_state,
JumpTarget* slow);
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();
struct InlineRuntimeLUT {
// For global const variables we bind the proxy to a variable.
if (mode == Variable::CONST && top_scope_->is_global_scope()) {
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
function APPLY_PREPARE(args) {
var length;
- // First check whether length is a positive Smi and args is an array. This is the
- // fast case. If this fails, we do the slow case that takes care of more eventualities
+ // First check whether length is a positive Smi and args is an
+ // array. This is the fast case. If this fails, we do the slow case
+ // that takes care of more eventualities.
if (%_IsArray(args)) {
length = args.length;
if (%_IsSmi(length) && length >= 0 && length < 0x800000 && IS_FUNCTION(this)) {
Handle<String> name,
Variable::Mode mode,
bool is_valid_LHS,
- bool is_this) {
+ Variable::Kind kind) {
HashMap::Entry* p = HashMap::Lookup(name.location(), name->Hash(), true);
if (p->value == NULL) {
// The variable has not been declared yet -> insert it.
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);
}
// such parameter is 'this' which is passed on the stack when
// invoking scripts
{ 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);
receiver_ = new VariableProxy(Factory::this_symbol(), true, false);
receiver_->BindTo(var);
// Declare 'arguments' variable which exists in all functions.
// Note that it may never be accessed, in which case it won't
// be allocated during variable allocation.
- Declare(Factory::arguments_symbol(), Variable::VAR);
+ locals_.Declare(this, Factory::arguments_symbol(), Variable::VAR,
+ true, Variable::ARGUMENTS);
}
}
Variable* Scope::DeclareFunctionVar(Handle<String> name) {
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_;
}
// INTERNAL variables are allocated explicitly, and TEMPORARY
// variables are allocated via NewTemporary().
ASSERT(mode == Variable::VAR || mode == Variable::CONST);
- return locals_.Declare(this, name, mode, true, false);
+ return locals_.Declare(this, name, mode, true, Variable::NORMAL);
}
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);
tmp->BindTo(var);
temps_.Add(var);
Variable* var = map->Lookup(name);
if (var == NULL) {
// 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.
var->rewrite_ = new Slot(var, Slot::LOOKUP, -1);
}
// We must have a global variable.
ASSERT(global_scope != NULL);
var = new Variable(global_scope, proxy->name(),
- Variable::DYNAMIC, true, false);
+ Variable::DYNAMIC, true, Variable::NORMAL);
} else if (scope_inside_with_) {
// If we are inside a with statement we give up and look up
// are never allocated in the context).
Variable* arguments_shadow =
new Variable(this, Factory::arguments_shadow_symbol(),
- Variable::INTERNAL, true, false);
+ Variable::INTERNAL, true, Variable::ARGUMENTS);
arguments_shadow_ =
new VariableProxy(Factory::arguments_shadow_symbol(), false, false);
arguments_shadow_->BindTo(arguments_shadow);
virtual ~LocalsMap();
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);
};
Handle<String> name,
Mode mode,
bool is_valid_LHS,
- bool is_this)
+ Kind kind)
: scope_(scope),
name_(name),
mode_(mode),
is_valid_LHS_(is_valid_LHS),
- is_this_(is_this),
+ kind_(kind),
local_if_not_shadowed_(NULL),
is_accessed_from_inner_scope_(false),
rewrite_(NULL) {
// in a context
};
+ enum Kind {
+ NORMAL,
+ THIS,
+ ARGUMENTS
+ };
+
// Printing support
static const char* Mode2String(Mode mode);
}
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 {
ASSERT(mode_ == DYNAMIC_LOCAL && local_if_not_shadowed_ != NULL);
private:
Variable(Scope* scope, Handle<String> name, Mode mode, bool is_valid_LHS,
- bool is_this);
+ Kind kind);
Scope* scope_;
Handle<String> name_;
Mode mode_;
bool is_valid_LHS_;
- bool is_this_;
+ Kind kind_;
Variable* local_if_not_shadowed_;
// 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.
frame_->Push(literal->handle());
Load(property->obj());
--- /dev/null
+// 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);
--- /dev/null
+// 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));