#include "deoptimizer.h"
#include "full-codegen.h"
#include "runtime.h"
+#include "stub-cache.h"
namespace v8 {
namespace internal {
// Use the global receiver object from the called function as the
// receiver.
__ bind(&use_global_receiver);
- const int kGlobalIndex =
- Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
- __ ldr(r2, FieldMemOperand(cp, kGlobalIndex));
- __ ldr(r2, FieldMemOperand(r2, GlobalObject::kNativeContextOffset));
- __ ldr(r2, FieldMemOperand(r2, kGlobalIndex));
- __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
+ __ ldr(r2, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
+ __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
__ bind(&patch_receiver);
__ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2));
// Use the current global receiver object as the receiver.
__ bind(&use_global_receiver);
- const int kGlobalOffset =
- Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
- __ ldr(r0, FieldMemOperand(cp, kGlobalOffset));
- __ ldr(r0, FieldMemOperand(r0, GlobalObject::kNativeContextOffset));
- __ ldr(r0, FieldMemOperand(r0, kGlobalOffset));
+ __ ldr(r0, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
__ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
// Push the receiver.
// r2 : cache cell for call target
Label slow, non_function;
+ // Check that the function is really a JavaScript function.
+ // r1: pushed function (to be verified)
+ __ JumpIfSmi(r1, &non_function);
+
// The receiver might implicitly be the global object. This is
// indicated by passing the hole as the receiver to the call
// function stub.
- if (ReceiverMightBeImplicit()) {
- Label call;
- // Get the receiver from the stack.
- // function, receiver [, arguments]
- __ ldr(r4, MemOperand(sp, argc_ * kPointerSize));
- // Call as function is indicated with the hole.
- __ CompareRoot(r4, Heap::kTheHoleValueRootIndex);
- __ b(ne, &call);
+ if (ReceiverMightBeImplicit() || ReceiverIsImplicit()) {
+ Label try_call, call, patch_current_context;
+ if (ReceiverMightBeImplicit()) {
+ // Get the receiver from the stack.
+ // function, receiver [, arguments]
+ __ ldr(r4, MemOperand(sp, argc_ * kPointerSize));
+ // Call as function is indicated with the hole.
+ __ CompareRoot(r4, Heap::kTheHoleValueRootIndex);
+ __ b(ne, &try_call);
+ }
// Patch the receiver on the stack with the global receiver object.
- __ ldr(r3,
- MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
- __ ldr(r3, FieldMemOperand(r3, GlobalObject::kGlobalReceiverOffset));
+ // Goto slow case if we do not have a function.
+ __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
+ __ b(ne, &patch_current_context);
+ CallStubCompiler::FetchGlobalProxy(masm, r3, r1);
__ str(r3, MemOperand(sp, argc_ * kPointerSize));
+ __ jmp(&call);
+
+ __ bind(&patch_current_context);
+ __ LoadRoot(r4, Heap::kUndefinedValueRootIndex);
+ __ str(r4, MemOperand(sp, argc_ * kPointerSize));
+ __ jmp(&slow);
+
+ __ bind(&try_call);
+ // Get the map of the function object.
+ __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
+ __ b(ne, &slow);
+
__ bind(&call);
+ } else {
+ // Get the map of the function object.
+ __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
+ __ b(ne, &slow);
}
- // Check that the function is really a JavaScript function.
- // r1: pushed function (to be verified)
- __ JumpIfSmi(r1, &non_function);
- // Get the map of the function object.
- __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
- __ b(ne, &slow);
-
if (RecordCallTarget()) {
GenerateRecordCallTarget(masm);
}
__ mov(r0, Operand(argc_ + 1, RelocInfo::NONE32));
__ mov(r2, Operand::Zero());
__ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY);
- __ SetCallKind(r5, CALL_AS_METHOD);
+ __ SetCallKind(r5, CALL_AS_FUNCTION);
{
Handle<Code> adaptor =
masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(callee);
}
- // Load global receiver object.
- __ ldr(r1, GlobalObjectOperand());
- __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
+ // Push the hole as receiver.
+ // It will be correctly replaced in the call stub.
+ __ LoadRoot(r1, Heap::kTheHoleValueRootIndex);
__ push(r1);
// Emit function call.
- EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
+ EmitCallWithStub(expr, RECEIVER_IS_IMPLICIT);
}
#ifdef DEBUG
// Patch the receiver on the stack.
__ bind(&global);
- __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
+ CallStubCompiler::FetchGlobalProxy(masm, r2, r1);
__ str(r2, MemOperand(sp, argc * kPointerSize));
__ bind(&invoke);
}
__ b(&result_in_receiver);
__ bind(&global_object);
+ CallStubCompiler::FetchGlobalProxy(masm(), receiver, function);
- __ ldr(result, MemOperand(fp, StandardFrameConstants::kContextOffset));
- __ ldr(result, ContextOperand(result, Context::GLOBAL_OBJECT_INDEX));
- __ ldr(result,
- FieldMemOperand(result, JSGlobalObject::kGlobalReceiverOffset));
if (result.is(receiver)) {
__ bind(&result_in_receiver);
} else {
ASSERT(ToRegister(instr->result()).is(r0));
int arity = instr->arity();
- CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
+ CallFunctionFlags flags =
+ instr->hydrogen()->IsContextualCall() ?
+ RECEIVER_IS_IMPLICIT : NO_CALL_FUNCTION_FLAGS;
+ CallFunctionStub stub(arity, flags);
if (instr->hydrogen()->IsTailCall()) {
if (NeedsEagerFrame()) __ mov(sp, fp);
__ Jump(stub.GetCode(isolate()), RelocInfo::CODE_TARGET);
}
-void CallStubCompiler::PatchGlobalProxy(Handle<Object> object) {
+void CallStubCompiler::PatchGlobalProxy(Handle<Object> object,
+ Handle<JSFunction> function) {
if (object->IsGlobalObject()) {
const int argc = arguments().immediate();
const int receiver_offset = argc * kPointerSize;
- __ ldr(r3, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
+ __ Move(r3, handle(function->context()->global_proxy()));
+ __ str(r3, MemOperand(sp, receiver_offset));
+ }
+}
+
+
+void CallStubCompiler::PatchGlobalProxy(Handle<Object> object,
+ Register function) {
+ if (object->IsGlobalObject()) {
+ FetchGlobalProxy(masm(), r3, function);
+ const int argc = arguments().immediate();
+ const int receiver_offset = argc * kPointerSize;
__ str(r3, MemOperand(sp, receiver_offset));
}
}
ASSERT(function.is(r1));
// Check that the function really is a function.
GenerateFunctionCheck(function, r3, miss);
- PatchGlobalProxy(object);
+ PatchGlobalProxy(object, function);
// Invoke the function.
__ InvokeFunction(r1, arguments(), JUMP_FUNCTION,
#define __ ACCESS_MASM(masm)
+void CallStubCompiler::FetchGlobalProxy(MacroAssembler* masm,
+ Register target,
+ Register function) {
+ __ ldr(target, FieldMemOperand(function, JSFunction::kContextOffset));
+ __ ldr(target, ContextOperand(target, Context::GLOBAL_OBJECT_INDEX));
+ __ ldr(target, FieldMemOperand(target, GlobalObject::kGlobalReceiverOffset));
+}
+
+
void StoreStubCompiler::GenerateStoreViaSetter(
MacroAssembler* masm,
Handle<JSFunction> setter) {
builtins->set_native_context(*native_context());
builtins->set_global_context(*native_context());
builtins->set_global_receiver(*builtins);
+ builtins->set_global_receiver(native_context()->global_proxy());
+
// Set up the 'global' properties of the builtins object. The
// 'global' property that refers to the global object is the only
CHECK_NOT_EMPTY_HANDLE(isolate(),
JSObject::SetLocalPropertyIgnoreAttributes(
builtins, global_string, global_obj, attributes));
+ Handle<String> builtins_string =
+ factory()->InternalizeOneByteString(STATIC_ASCII_VECTOR("builtins"));
+ CHECK_NOT_EMPTY_HANDLE(isolate(),
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ builtins, builtins_string, builtins, attributes));
// Set up the reference from the global object to the builtins object.
JSGlobalObject::cast(native_context()->global_object())->
HookUpGlobalProxy(inner_global, global_proxy);
HookUpInnerGlobal(inner_global);
+ native_context()->builtins()->set_global_receiver(
+ native_context()->global_proxy());
if (!ConfigureGlobalObjects(global_template)) return;
} else {
virtual void PrintName(StringStream* stream);
// Minor key encoding in 32 bits with Bitfield <Type, shift, size>.
- class FlagBits: public BitField<CallFunctionFlags, 0, 2> {};
- class ArgcBits: public BitField<unsigned, 2, 32 - 2> {};
+ class FlagBits: public BitField<CallFunctionFlags, 0, 3> {};
+ class ArgcBits: public BitField<unsigned, 3, 32 - 3> {};
Major MajorKey() { return CallFunction; }
int MinorKey() {
return (flags_ & RECEIVER_MIGHT_BE_IMPLICIT) != 0;
}
+ bool ReceiverIsImplicit() {
+ return (flags_ & RECEIVER_IS_IMPLICIT) != 0;
+ }
+
bool RecordCallTarget() {
return (flags_ & RECORD_CALL_TARGET) != 0;
}
enum CallMode {
NORMAL_CALL,
- TAIL_CALL
+ TAIL_CALL,
+ NORMAL_CONTEXTUAL_CALL
};
HCallFunction, HValue*, int, CallMode);
bool IsTailCall() const { return call_mode_ == TAIL_CALL; }
-
+ bool IsContextualCall() const { return call_mode_ == NORMAL_CONTEXTUAL_CALL; }
HValue* context() { return first(); }
HValue* function() { return second(); }
}
+void HOptimizedGraphBuilder::InstallGlobalReceiverInExpressionStack(
+ int receiver_index,
+ Handle<JSFunction> function) {
+ // TODO(dcarney): Fix deserializer to be able to hookup the global receiver
+ // and object during deserialization and embed the global receiver here
+ // directly.
+ // Install global receiver on stack.
+ HValue* function_constant = Add<HConstant>(function);
+ HValue* context = Add<HLoadNamedField>(
+ function_constant,
+ HObjectAccess::ForJSObjectOffset(JSFunction::kContextOffset));
+ HValue* global_object = Add<HLoadNamedField>(
+ context,
+ HObjectAccess::ForContextSlot(Context::GLOBAL_OBJECT_INDEX));
+ HValue* global_receiver = Add<HLoadNamedField>(
+ global_object,
+ HObjectAccess::ForJSObjectOffset(GlobalObject::kGlobalReceiverOffset));
+ environment()->SetExpressionStackAt(receiver_index, global_receiver);
+}
+
+
void HOptimizedGraphBuilder::VisitCall(Call* expr) {
ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL);
HValue* function = Pop();
Add<HCheckValue>(function, expr->target());
- // Replace the global object with the global receiver.
- HGlobalReceiver* global_receiver = Add<HGlobalReceiver>(global_object);
- // Index of the receiver from the top of the expression stack.
+ // Install global receiver on stack.
const int receiver_index = argument_count - 1;
ASSERT(environment()->ExpressionStackAt(receiver_index)->
IsGlobalObject());
- environment()->SetExpressionStackAt(receiver_index, global_receiver);
+ InstallGlobalReceiverInExpressionStack(receiver_index, expr->target());
if (TryInlineBuiltinFunctionCall(expr, false)) { // Nothing to drop.
if (FLAG_trace_inlining) {
}
if (CallStubCompiler::HasCustomCallGenerator(expr->target())) {
+ // We're about to install a contextual IC, which expects the global
+ // object as receiver rather than the global proxy.
+ environment()->SetExpressionStackAt(receiver_index, global_object);
// When the target has a custom call IC generator, use the IC,
// because it is likely to generate better code.
- call = PreProcessCall(New<HCallNamed>(var->name(), argument_count));
+ call = PreProcessCall(New<HCallGlobal>(var->name(), argument_count));
} else {
call = PreProcessCall(New<HCallKnownGlobal>(
expr->target(), argument_count));
CHECK_ALIVE(VisitExpressions(expr->arguments()));
Add<HCheckValue>(function, expr->target());
+ // Install global receiver on stack.
+ const int receiver_index = argument_count - 1;
+ ASSERT(environment()->ExpressionStackAt(receiver_index)->
+ IsGlobalReceiver());
+ InstallGlobalReceiverInExpressionStack(receiver_index, expr->target());
+
if (TryInlineBuiltinFunctionCall(expr, true)) { // Drop the function.
if (FLAG_trace_inlining) {
PrintF("Inlining builtin ");
} else {
CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* function = Top();
- HGlobalObject* global_object = Add<HGlobalObject>();
- HGlobalReceiver* receiver = Add<HGlobalReceiver>(global_object);
+ HValue* receiver = graph()->GetConstantHole();
Push(Add<HPushArgument>(receiver));
CHECK_ALIVE(VisitArgumentList(expr->arguments()));
-
- call = New<HCallFunction>(function, argument_count);
+ call = New<HCallFunction>(
+ function, argument_count, NORMAL_CONTEXTUAL_CALL);
Drop(argument_count + 1);
}
}
HValue* receiver,
Handle<Map> receiver_map);
+ void InstallGlobalReceiverInExpressionStack(int index,
+ Handle<JSFunction> function);
+
// The translation state of the currently-being-translated function.
FunctionState* function_state_;
#include "codegen.h"
#include "deoptimizer.h"
#include "full-codegen.h"
+#include "stub-cache.h"
namespace v8 {
namespace internal {
// Use the global receiver object from the called function as the
// receiver.
__ bind(&use_global_receiver);
- const int kGlobalIndex =
- Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
- __ mov(ebx, FieldOperand(esi, kGlobalIndex));
- __ mov(ebx, FieldOperand(ebx, GlobalObject::kNativeContextOffset));
- __ mov(ebx, FieldOperand(ebx, kGlobalIndex));
- __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
+ CallStubCompiler::FetchGlobalProxy(masm, ebx, edi);
__ bind(&patch_receiver);
__ mov(Operand(esp, eax, times_4, 0), ebx);
// Use the current global receiver object as the receiver.
__ bind(&use_global_receiver);
- const int kGlobalOffset =
- Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
- __ mov(ebx, FieldOperand(esi, kGlobalOffset));
- __ mov(ebx, FieldOperand(ebx, GlobalObject::kNativeContextOffset));
- __ mov(ebx, FieldOperand(ebx, kGlobalOffset));
- __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
+ CallStubCompiler::FetchGlobalProxy(masm, ebx, edi);
// Push the receiver.
__ bind(&push_receiver);
Isolate* isolate = masm->isolate();
Label slow, non_function;
+ // Check that the function really is a JavaScript function.
+ __ JumpIfSmi(edi, &non_function);
+
// The receiver might implicitly be the global object. This is
// indicated by passing the hole as the receiver to the call
// function stub.
- if (ReceiverMightBeImplicit()) {
- Label receiver_ok;
- // Get the receiver from the stack.
- // +1 ~ return address
- __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
- // Call as function is indicated with the hole.
- __ cmp(eax, isolate->factory()->the_hole_value());
- __ j(not_equal, &receiver_ok, Label::kNear);
+ if (ReceiverMightBeImplicit() || ReceiverIsImplicit()) {
+ Label try_call, call, patch_current_context;
+ if (ReceiverMightBeImplicit()) {
+ // Get the receiver from the stack.
+ // +1 ~ return address
+ __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
+ // Call as function is indicated with the hole.
+ __ cmp(eax, isolate->factory()->the_hole_value());
+ __ j(not_equal, &try_call, Label::kNear);
+ }
// Patch the receiver on the stack with the global receiver object.
- __ mov(ecx, GlobalObjectOperand());
- __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
+ // Goto slow case if we do not have a function.
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+ __ j(not_equal, &patch_current_context);
+ CallStubCompiler::FetchGlobalProxy(masm, ecx, edi);
__ mov(Operand(esp, (argc_ + 1) * kPointerSize), ecx);
- __ bind(&receiver_ok);
- }
+ __ jmp(&call, Label::kNear);
- // Check that the function really is a JavaScript function.
- __ JumpIfSmi(edi, &non_function);
- // Goto slow case if we do not have a function.
- __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
- __ j(not_equal, &slow);
+ __ bind(&patch_current_context);
+ __ mov(edx, isolate->factory()->undefined_value());
+ __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edx);
+ __ jmp(&slow);
+
+ __ bind(&try_call);
+ // Goto slow case if we do not have a function.
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+ __ j(not_equal, &slow);
+
+ __ bind(&call);
+ } else {
+ // Goto slow case if we do not have a function.
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+ __ j(not_equal, &slow);
+ }
if (RecordCallTarget()) {
GenerateRecordCallTarget(masm);
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(callee);
}
- // Load global receiver object.
- __ mov(ebx, GlobalObjectOperand());
- __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
+ // Push the hole as receiver.
+ // It will be correctly replaced in the call stub.
+ __ push(Immediate(isolate()->factory()->the_hole_value()));
// Emit function call.
- EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
+ EmitCallWithStub(expr, RECEIVER_IS_IMPLICIT);
}
#ifdef DEBUG
// Patch the receiver on the stack.
__ bind(&global);
- __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
+ CallStubCompiler::FetchGlobalProxy(masm, edx, edi);
__ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
+
__ bind(&invoke);
}
// TODO(kmillikin): We have a hydrogen value for the global object. See
// if it's better to use it than to explicitly fetch it from the context
// here.
- __ mov(receiver, Operand(ebp, StandardFrameConstants::kContextOffset));
- __ mov(receiver, ContextOperand(receiver, Context::GLOBAL_OBJECT_INDEX));
- __ mov(receiver,
- FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset));
+ CallStubCompiler::FetchGlobalProxy(masm(), receiver, function);
__ bind(&receiver_ok);
}
ASSERT(ToRegister(instr->result()).is(eax));
int arity = instr->arity();
- CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
+ CallFunctionFlags flags =
+ instr->hydrogen()->IsContextualCall() ?
+ RECEIVER_IS_IMPLICIT : NO_CALL_FUNCTION_FLAGS;
+ CallFunctionStub stub(arity, flags);
if (instr->hydrogen()->IsTailCall()) {
if (NeedsEagerFrame()) __ leave();
__ jmp(stub.GetCode(isolate()), RelocInfo::CODE_TARGET);
LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) {
LOperand* receiver = UseRegister(instr->receiver());
- LOperand* function = UseRegisterAtStart(instr->function());
+ LOperand* function = UseRegister(instr->function());
LOperand* temp = TempRegister();
LWrapReceiver* result =
new(zone()) LWrapReceiver(receiver, function, temp);
}
-void CallStubCompiler::PatchGlobalProxy(Handle<Object> object) {
+void CallStubCompiler::PatchGlobalProxy(Handle<Object> object,
+ Handle<JSFunction> function) {
if (object->IsGlobalObject()) {
const int argc = arguments().immediate();
const int receiver_offset = (argc + 1) * kPointerSize;
- __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
+ __ LoadHeapObject(edx, handle(function->context()->global_proxy()));
+ __ mov(Operand(esp, receiver_offset), edx);
+ }
+}
+
+
+void CallStubCompiler::PatchGlobalProxy(Handle<Object> object,
+ Register function) {
+ if (object->IsGlobalObject()) {
+ FetchGlobalProxy(masm(), edx, function);
+ const int argc = arguments().immediate();
+ const int receiver_offset = (argc + 1) * kPointerSize;
__ mov(Operand(esp, receiver_offset), edx);
}
}
GenerateFunctionCheck(function, ebx, miss);
if (!function.is(edi)) __ mov(edi, function);
- PatchGlobalProxy(object);
+ PatchGlobalProxy(object, function);
// Invoke the function.
__ InvokeFunction(edi, arguments(), JUMP_FUNCTION,
#define __ ACCESS_MASM(masm)
+void CallStubCompiler::FetchGlobalProxy(MacroAssembler* masm,
+ Register target,
+ Register function) {
+ __ mov(target, FieldOperand(function, JSFunction::kContextOffset));
+ __ mov(target, ContextOperand(target, Context::GLOBAL_OBJECT_INDEX));
+ __ mov(target, FieldOperand(target, GlobalObject::kGlobalReceiverOffset));
+}
+
+
void StoreStubCompiler::GenerateStoreViaSetter(
MacroAssembler* masm,
Handle<JSFunction> setter) {
// GetProperty below can cause GC.
Handle<Object> receiver_handle(
object->IsGlobalObject()
- ? GlobalObject::cast(*object)->global_receiver()
+ ? Object::cast(isolate->heap()->the_hole_value())
: object->IsJSProxy() ? static_cast<Object*>(*object)
: ComputeReceiverForNonGlobal(isolate, JSObject::cast(*object)),
isolate);
var $Function = global.Function;
var $Boolean = global.Boolean;
var $NaN = %GetRootNaN();
-var builtins = this;
// ECMA-262 Section 11.9.3.
function EQUALS(y) {
// upgraded to handle small pages.
size = AreaSize();
} else {
-#if V8_TARGET_ARCH_MIPS
- // TODO(plind): Investigate larger code stubs size on MIPS.
size = 480 * KB;
-#else
- size = 416 * KB;
-#endif
}
break;
default:
void CallStubCompiler::GenerateJumpFunction(Handle<Object> object,
Handle<JSFunction> function) {
- PatchGlobalProxy(object);
+ PatchGlobalProxy(object, function);
GenerateJumpFunctionIgnoreReceiver(function);
}
void CallStubCompiler::GenerateJumpFunction(Handle<Object> object,
Register actual_closure,
Handle<JSFunction> function) {
- PatchGlobalProxy(object);
+ PatchGlobalProxy(object, function);
ParameterCount expected(function);
__ InvokeFunction(actual_closure, expected, arguments(),
JUMP_FUNCTION, NullCallWrapper(), call_kind());
// Patch the global proxy over the global object if the global object is the
// receiver.
- void PatchGlobalProxy(Handle<Object> object);
+ static void FetchGlobalProxy(MacroAssembler* masm,
+ Register target,
+ Register function);
+ void PatchGlobalProxy(Handle<Object> object, Register function);
+ void PatchGlobalProxy(Handle<Object> object, Handle<JSFunction> function);
// Returns the register containing the holder of |name|.
Register HandlerFrontendHeader(Handle<Object> object,
// Receiver might implicitly be the global objects. If it is, the
// hole is passed to the call function stub.
RECEIVER_MIGHT_BE_IMPLICIT = 1 << 0,
+ // Receiver is implicit and the hole has been passed to the stub.
+ RECEIVER_IS_IMPLICIT = 1 << 1,
// The call target is cached in the instruction stream.
- RECORD_CALL_TARGET = 1 << 1
+ RECORD_CALL_TARGET = 1 << 2
};
#include "codegen.h"
#include "deoptimizer.h"
#include "full-codegen.h"
+#include "stub-cache.h"
namespace v8 {
namespace internal {
// Use the global receiver object from the called function as the
// receiver.
__ bind(&use_global_receiver);
- const int kGlobalIndex =
- Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
- __ movq(rbx, FieldOperand(rsi, kGlobalIndex));
- __ movq(rbx, FieldOperand(rbx, GlobalObject::kNativeContextOffset));
- __ movq(rbx, FieldOperand(rbx, kGlobalIndex));
- __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
+ CallStubCompiler::FetchGlobalProxy(masm, rbx, rdi);
__ bind(&patch_receiver);
__ movq(args.GetArgumentOperand(1), rbx);
// Use the current global receiver object as the receiver.
__ bind(&use_global_receiver);
- const int kGlobalOffset =
- Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
- __ movq(rbx, FieldOperand(rsi, kGlobalOffset));
- __ movq(rbx, FieldOperand(rbx, GlobalObject::kNativeContextOffset));
- __ movq(rbx, FieldOperand(rbx, kGlobalOffset));
- __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
-
+ CallStubCompiler::FetchGlobalProxy(masm, rbx, rdi);
// Push the receiver.
__ bind(&push_receiver);
__ push(rbx);
Label slow, non_function;
StackArgumentsAccessor args(rsp, argc_);
+ // Check that the function really is a JavaScript function.
+ __ JumpIfSmi(rdi, &non_function);
+
// The receiver might implicitly be the global object. This is
// indicated by passing the hole as the receiver to the call
// function stub.
- if (ReceiverMightBeImplicit()) {
- Label call;
- // Get the receiver from the stack.
- __ movq(rax, args.GetReceiverOperand());
- // Call as function is indicated with the hole.
- __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
- __ j(not_equal, &call, Label::kNear);
+ if (ReceiverMightBeImplicit() || ReceiverIsImplicit()) {
+ Label try_call, call, patch_current_context;
+ if (ReceiverMightBeImplicit()) {
+ // Get the receiver from the stack.
+ __ movq(rax, args.GetReceiverOperand());
+ // Call as function is indicated with the hole.
+ __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ j(not_equal, &try_call, Label::kNear);
+ }
// Patch the receiver on the stack with the global receiver object.
- __ movq(rcx, GlobalObjectOperand());
- __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalReceiverOffset));
+ // Goto slow case if we do not have a function.
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &patch_current_context);
+ CallStubCompiler::FetchGlobalProxy(masm, rcx, rdi);
__ movq(args.GetReceiverOperand(), rcx);
+ __ jmp(&call, Label::kNear);
+
+ __ bind(&patch_current_context);
+ __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
+ __ movq(args.GetReceiverOperand(), kScratchRegister);
+ __ jmp(&slow);
+
+ __ bind(&try_call);
+ // Goto slow case if we do not have a function.
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &slow);
+
__ bind(&call);
+ } else {
+ // Goto slow case if we do not have a function.
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &slow);
}
- // Check that the function really is a JavaScript function.
- __ JumpIfSmi(rdi, &non_function);
- // Goto slow case if we do not have a function.
- __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
- __ j(not_equal, &slow);
-
if (RecordCallTarget()) {
GenerateRecordCallTarget(masm);
}
__ PushReturnAddressFrom(rcx);
__ Set(rax, argc_ + 1);
__ Set(rbx, 0);
- __ SetCallKind(rcx, CALL_AS_METHOD);
+ __ SetCallKind(rcx, CALL_AS_FUNCTION);
__ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY);
{
Handle<Code> adaptor =
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(callee);
}
- // Load global receiver object.
- __ movq(rbx, GlobalObjectOperand());
- __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
+ // Push the hole as receiver.
+ // It will be correctly replaced in the call stub.
+ __ PushRoot(Heap::kTheHoleValueRootIndex);
// Emit function call.
- EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
+ EmitCallWithStub(expr, RECEIVER_IS_IMPLICIT);
}
#ifdef DEBUG
// Patch the receiver on the stack.
__ bind(&global);
- __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ CallStubCompiler::FetchGlobalProxy(masm, rdx, rdi);
__ movq(args.GetReceiverOperand(), rdx);
__ bind(&invoke);
}
// TODO(kmillikin): We have a hydrogen value for the global object. See
// if it's better to use it than to explicitly fetch it from the context
// here.
- __ movq(receiver, Operand(rbp, StandardFrameConstants::kContextOffset));
- __ movq(receiver, ContextOperand(receiver, Context::GLOBAL_OBJECT_INDEX));
- __ movq(receiver,
- FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset));
+ CallStubCompiler::FetchGlobalProxy(masm(), receiver, function);
__ bind(&receiver_ok);
}
ASSERT(ToRegister(instr->result()).is(rax));
int arity = instr->arity();
- CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
+ CallFunctionFlags flags =
+ instr->hydrogen()->IsContextualCall() ?
+ RECEIVER_IS_IMPLICIT : NO_CALL_FUNCTION_FLAGS;
+ CallFunctionStub stub(arity, flags);
if (instr->hydrogen()->IsTailCall()) {
if (NeedsEagerFrame()) __ leave();
__ jmp(stub.GetCode(isolate()), RelocInfo::CODE_TARGET);
}
-void CallStubCompiler::PatchGlobalProxy(Handle<Object> object) {
+void CallStubCompiler::PatchGlobalProxy(Handle<Object> object,
+ Handle<JSFunction> function) {
if (object->IsGlobalObject()) {
StackArgumentsAccessor args(rsp, arguments());
- __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
+ __ MoveHeapObject(rdx, handle(function->context()->global_proxy()));
+ __ movq(args.GetReceiverOperand(), rdx);
+ }
+}
+
+
+void CallStubCompiler::PatchGlobalProxy(Handle<Object> object,
+ Register function) {
+ if (object->IsGlobalObject()) {
+ FetchGlobalProxy(masm(), rdx, function);
+ StackArgumentsAccessor args(rsp, arguments().immediate());
__ movq(args.GetReceiverOperand(), rdx);
}
}
GenerateFunctionCheck(function, rbx, miss);
if (!function.is(rdi)) __ movq(rdi, function);
- PatchGlobalProxy(object);
+ PatchGlobalProxy(object, function);
// Invoke the function.
__ InvokeFunction(rdi, arguments(), JUMP_FUNCTION,
#define __ ACCESS_MASM(masm)
+void CallStubCompiler::FetchGlobalProxy(MacroAssembler* masm,
+ Register target,
+ Register function) {
+ __ movq(target, FieldOperand(function, JSFunction::kContextOffset));
+ __ movq(target, ContextOperand(target, Context::GLOBAL_OBJECT_INDEX));
+ __ movq(target, FieldOperand(target, GlobalObject::kGlobalReceiverOffset));
+}
+
+
void StoreStubCompiler::GenerateStoreViaSetter(
MacroAssembler* masm,
Handle<JSFunction> setter) {
}
+void GetThisX(const v8::FunctionCallbackInfo<v8::Value>& info) {
+ info.GetReturnValue().Set(
+ info.GetIsolate()->GetCurrentContext()->Global()->Get(v8_str("x")));
+}
+
+
TEST(DetachedAccesses) {
LocalContext env1;
v8::HandleScope scope(env1->GetIsolate());
// Create second environment.
- v8::Handle<Context> env2 = Context::New(env1->GetIsolate());
+ Local<ObjectTemplate> inner_global_template =
+ FunctionTemplate::New(env1->GetIsolate())->InstanceTemplate();
+ inner_global_template ->SetAccessorProperty(
+ v8_str("this_x"), FunctionTemplate::New(env1->GetIsolate(), GetThisX));
+ v8::Local<Context> env2 =
+ Context::New(env1->GetIsolate(), NULL, inner_global_template);
Local<Value> foo = v8_str("foo");
env1->SetSecurityToken(foo);
env2->SetSecurityToken(foo);
+ env1->Global()->Set(v8_str("x"), v8_str("env1_x"));
+
{
v8::Context::Scope scope(env2);
+ env2->Global()->Set(v8_str("x"), v8_str("env2_x"));
CompileRun(
- "var x = 'x';"
- "function get_x() { return this.x; }"
- "function get_x_w() { return get_x(); }"
- "");
+ "function bound_x() { return x; }"
+ "function get_x() { return this.x; }"
+ "function get_x_w() { return (function() {return this.x;})(); }");
+ env1->Global()->Set(v8_str("bound_x"), CompileRun("bound_x"));
env1->Global()->Set(v8_str("get_x"), CompileRun("get_x"));
env1->Global()->Set(v8_str("get_x_w"), CompileRun("get_x_w"));
+ env1->Global()->Set(
+ v8_str("this_x"),
+ CompileRun("Object.getOwnPropertyDescriptor(this, 'this_x').get"));
}
Local<Object> env2_global = env2->Global();
env2->DetachGlobal();
Local<Value> result;
+ result = CompileRun("bound_x()");
+ CHECK_EQ(v8_str("env2_x"), result);
result = CompileRun("get_x()");
CHECK(result->IsUndefined());
result = CompileRun("get_x_w()");
CHECK(result->IsUndefined());
+ result = CompileRun("this_x()");
+ CHECK_EQ(v8_str("env2_x"), result);
// Reattach env2's proxy
env2 = Context::New(env1->GetIsolate(),
env2->SetSecurityToken(foo);
{
v8::Context::Scope scope(env2);
- CompileRun("var x = 'x2';");
+ env2->Global()->Set(v8_str("x"), v8_str("env3_x"));
+ env2->Global()->Set(v8_str("env1"), env1->Global());
+ result = CompileRun(
+ "results = [];"
+ "for (var i = 0; i < 4; i++ ) {"
+ " results.push(env1.bound_x());"
+ " results.push(env1.get_x());"
+ " results.push(env1.get_x_w());"
+ " results.push(env1.this_x());"
+ "}"
+ "results");
+ Local<v8::Array> results = Local<v8::Array>::Cast(result);
+ CHECK_EQ(16, results->Length());
+ for (int i = 0; i < 16; i += 4) {
+ CHECK_EQ(v8_str("env2_x"), results->Get(i + 0));
+ CHECK_EQ(v8_str("env1_x"), results->Get(i + 1));
+ CHECK_EQ(v8_str("env3_x"), results->Get(i + 2));
+ CHECK_EQ(v8_str("env2_x"), results->Get(i + 3));
+ }
}
- result = CompileRun("get_x()");
- CHECK(result->IsUndefined());
- result = CompileRun("get_x_w()");
- CHECK_EQ(v8_str("x2"), result);
+ result = CompileRun(
+ "results = [];"
+ "for (var i = 0; i < 4; i++ ) {"
+ " results.push(bound_x());"
+ " results.push(get_x());"
+ " results.push(get_x_w());"
+ " results.push(this_x());"
+ "}"
+ "results");
+ Local<v8::Array> results = Local<v8::Array>::Cast(result);
+ CHECK_EQ(16, results->Length());
+ for (int i = 0; i < 16; i += 4) {
+ CHECK_EQ(v8_str("env2_x"), results->Get(i + 0));
+ CHECK_EQ(v8_str("env3_x"), results->Get(i + 1));
+ CHECK_EQ(v8_str("env3_x"), results->Get(i + 2));
+ CHECK_EQ(v8_str("env2_x"), results->Get(i + 3));
+ }
+
+ result = CompileRun(
+ "results = [];"
+ "for (var i = 0; i < 4; i++ ) {"
+ " results.push(this.bound_x());"
+ " results.push(this.get_x());"
+ " results.push(this.get_x_w());"
+ " results.push(this.this_x());"
+ "}"
+ "results");
+ results = Local<v8::Array>::Cast(result);
+ CHECK_EQ(16, results->Length());
+ for (int i = 0; i < 16; i += 4) {
+ CHECK_EQ(v8_str("env2_x"), results->Get(i + 0));
+ CHECK_EQ(v8_str("env1_x"), results->Get(i + 1));
+ CHECK_EQ(v8_str("env3_x"), results->Get(i + 2));
+ CHECK_EQ(v8_str("env2_x"), results->Get(i + 3));
+ }
}
CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/g,func)[1]")));
CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/g,func)[3]")));
- // TODO(1547): Make the following also return "i".
// Calling with environment record as base.
- TestReceiver(o, context->Global(), "func()");
+ TestReceiver(i, foreign_context->Global(), "func()");
// Calling with no base.
- TestReceiver(o, context->Global(), "(1,func)()");
+ TestReceiver(i, foreign_context->Global(), "(1,func)()");
}
--- /dev/null
+// Copyright 2013 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.
+
+var realms = [Realm.current(), Realm.create()];
+globals = [Realm.global(0), Realm.global(1)];
+Realm.shared = {}
+
+function install(name, value) {
+ Realm.shared[name] = value;
+ for (i in realms) {
+ Realm.eval(realms[i], name + " = Realm.shared['" + name + "'];");
+ }
+}
+
+install('return_this', function() { return this; });
+install('return_this_strict', function () { 'use strict'; return this; });
+
+// test behaviour of 'with' scope
+for (i in realms) {
+ Realm.shared.results = [];
+ // in the second case, 'this' is found in the with scope,
+ // so the receiver is 'this'
+ Realm.eval(realms[i]," \
+ with('irrelevant') { \
+ Realm.shared.results.push(return_this()); \
+ Realm.shared.results.push(return_this_strict()); \
+ } \
+ with(this) { \
+ Realm.shared.results.push(return_this()); \
+ Realm.shared.results.push(return_this_strict()); \
+ } \
+ ");
+ assertSame(globals[0], Realm.shared.results[0]);
+ assertSame(undefined, Realm.shared.results[1]);
+ assertSame(globals[i], Realm.shared.results[2]);
+ assertSame(globals[i], Realm.shared.results[3]);
+}
+
+// test 'apply' and 'call'
+for (i in realms) {
+ // 'apply' without a receiver is a contextual call
+ assertSame(globals[0], Realm.eval(realms[i],'return_this.apply()')) ;
+ assertSame(undefined, Realm.eval(realms[i],'return_this_strict.apply()'));
+ assertSame(globals[0], Realm.eval(realms[i],'return_this.apply(null)')) ;
+ assertSame(null, Realm.eval(realms[i],'return_this_strict.apply(null)'));
+ // 'call' without a receiver is a contextual call
+ assertSame(globals[0], Realm.eval(realms[i],'return_this.call()')) ;
+ assertSame(undefined, Realm.eval(realms[i],'return_this_strict.call()'));
+ assertSame(globals[0], Realm.eval(realms[i],'return_this.call(null)')) ;
+ assertSame(null, Realm.eval(realms[i],'return_this_strict.call(null)'));
+}
+
+// test ics
+for (var i = 0; i < 4; i++) {
+ assertSame(globals[0], return_this());
+ assertSame(undefined, return_this_strict());
+}
+
+// BUG(1547)
+
+Realm.eval(realms[0], "var name = 'o'");
+Realm.eval(realms[1], "var name = 'i'");
+
+install('f', function() { return this.name; });
+install('g', function() { "use strict"; return this ? this.name : "u"; });
+
+for (i in realms) {
+ result = Realm.eval(realms[i], " \
+ (function(){return f();})() + \
+ (function(){return (1,f)();})() + \
+ (function(){'use strict'; return f();})() + \
+ (function(){'use strict'; return (1,f)();})() + \
+ (function(){return g();})() + \
+ (function(){return (1,g)();})() + \
+ (function(){'use strict'; return g();})() + \
+ (function(){'use strict'; return (1,g)();})(); \
+ ");
+ assertSame("oooouuuu", result);
+}
function TestCall(isStrict, callTrap) {
assertEquals(42, callTrap(5, 37))
- // TODO(rossberg): strict mode seems to be broken on x64...
- // assertSame(isStrict ? undefined : global_object, receiver)
+ assertSame(isStrict ? undefined : global_object, receiver)
var handler = {
get: function(r, k) {
receiver = 333
assertEquals(42, f(11, 31))
- // TODO(rossberg): strict mode seems to be broken on x64...
- // assertSame(isStrict ? undefined : global_object, receiver)
+ assertSame(isStrict ? undefined : global_object, receiver)
receiver = 333
assertEquals(42, o.f(10, 32))
assertSame(o, receiver)
TestCalls()
*/
+
+var realms = [Realm.create(), Realm.create()];
+Realm.shared = {};
+
+Realm.eval(realms[0], "function f() { return this; };");
+Realm.eval(realms[0], "Realm.shared.f = f;");
+Realm.eval(realms[0], "Realm.shared.fg = this;");
+Realm.eval(realms[1], "function g() { return this; };");
+Realm.eval(realms[1], "Realm.shared.g = g;");
+Realm.eval(realms[1], "Realm.shared.gg = this;");
+
+var fp = Proxy.createFunction({}, Realm.shared.f);
+var gp = Proxy.createFunction({}, Realm.shared.g);
+
+for (var i = 0; i < 10; i++) {
+ assertEquals(Realm.shared.fg, fp());
+ assertEquals(Realm.shared.gg, gp());
+
+ with (this) {
+ assertEquals(Realm.shared.fg, fp());
+ assertEquals(Realm.shared.gg, gp());
+ }
+
+ with ({}) {
+ assertEquals(Realm.shared.fg, fp());
+ assertEquals(Realm.shared.gg, gp());
+ }
+}