From: verwaest@chromium.org Date: Tue, 7 Jan 2014 08:21:17 +0000 (+0000) Subject: Load the global proxy from the context of the target function. X-Git-Tag: upstream/4.7.83~11259 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5b40c3867943fe8ed2bca53ece5c47b2a8b63766;p=platform%2Fupstream%2Fv8.git Load the global proxy from the context of the target function. BUG= R=dcarney@chromium.org Review URL: https://codereview.chromium.org/111613003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18458 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 5a47ef4..6c374b3 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -34,6 +34,7 @@ #include "deoptimizer.h" #include "full-codegen.h" #include "runtime.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -1091,12 +1092,8 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 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)); @@ -1287,11 +1284,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { // 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. diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index f70271b..dfa9113 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -3182,32 +3182,48 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // 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(ip, Heap::kUndefinedValueRootIndex); + __ str(ip, 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); } @@ -3251,7 +3267,7 @@ void CallFunctionStub::Generate(MacroAssembler* 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 adaptor = masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(); diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index b362f12..da7eb99 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -2815,12 +2815,12 @@ void FullCodeGenerator::VisitCall(Call* expr) { { 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 diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index ea247b3..a8943e1 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -491,7 +491,7 @@ void CallICBase::GenerateMiss(MacroAssembler* masm, // 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); } diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 0463ce4..c58fdb1 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -3491,11 +3491,8 @@ void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) { __ 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 { @@ -3993,7 +3990,10 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) { 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); diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 4d9fc0d..5350b6b 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -2341,11 +2341,23 @@ void StubCompiler::GenerateBooleanCheck(Register object, Label* miss) { } -void CallStubCompiler::PatchGlobalProxy(Handle object) { +void CallStubCompiler::PatchGlobalProxy(Handle object, + Handle 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, + 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)); } } @@ -2444,7 +2456,7 @@ void CallStubCompiler::GenerateJumpFunction(Handle object, 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, @@ -2562,6 +2574,15 @@ Handle StoreStubCompiler::CompileStoreCallback( #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 setter) { diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index f27ca7a..7b8af06 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1669,6 +1669,8 @@ bool Genesis::InstallNatives() { 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 @@ -1682,6 +1684,11 @@ bool Genesis::InstallNatives() { CHECK_NOT_EMPTY_HANDLE(isolate(), JSObject::SetLocalPropertyIgnoreAttributes( builtins, global_string, global_obj, attributes)); + Handle 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())-> @@ -2581,6 +2588,8 @@ Genesis::Genesis(Isolate* isolate, HookUpGlobalProxy(inner_global, global_proxy); HookUpInnerGlobal(inner_global); + native_context()->builtins()->set_global_receiver( + native_context()->global_proxy()); if (!ConfigureGlobalObjects(global_template)) return; } else { diff --git a/src/code-stubs.h b/src/code-stubs.h index bb9ff78..3c3c194 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -1550,8 +1550,8 @@ class CallFunctionStub: public PlatformCodeStub { virtual void PrintName(StringStream* stream); // Minor key encoding in 32 bits with Bitfield . - class FlagBits: public BitField {}; - class ArgcBits: public BitField {}; + class FlagBits: public BitField {}; + class ArgcBits: public BitField {}; Major MajorKey() { return CallFunction; } int MinorKey() { @@ -1563,6 +1563,10 @@ class CallFunctionStub: public PlatformCodeStub { return (flags_ & RECEIVER_MIGHT_BE_IMPLICIT) != 0; } + bool ReceiverIsImplicit() { + return (flags_ & RECEIVER_IS_IMPLICIT) != 0; + } + bool RecordCallTarget() { return (flags_ & RECORD_CALL_TARGET) != 0; } diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index a32a418..71ae304 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -2414,7 +2414,8 @@ class HCallNamed V8_FINAL : public HUnaryCall { enum CallMode { NORMAL_CALL, - TAIL_CALL + TAIL_CALL, + NORMAL_CONTEXTUAL_CALL }; @@ -2425,7 +2426,7 @@ class HCallFunction V8_FINAL : public HBinaryCall { 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(); } diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 7bdc02f..03e666c 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -7618,6 +7618,27 @@ bool HOptimizedGraphBuilder::TryCallApply(Call* expr) { } +void HOptimizedGraphBuilder::InstallGlobalReceiverInExpressionStack( + int receiver_index, + Handle 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(function); + HValue* context = Add( + function_constant, + HObjectAccess::ForJSObjectOffset(JSFunction::kContextOffset)); + HValue* global_object = Add( + context, + HObjectAccess::ForContextSlot(Context::GLOBAL_OBJECT_INDEX)); + HValue* global_receiver = Add( + global_object, + HObjectAccess::ForJSObjectOffset(GlobalObject::kGlobalReceiverOffset)); + environment()->SetExpressionStackAt(receiver_index, global_receiver); +} + + void HOptimizedGraphBuilder::VisitCall(Call* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); @@ -7738,13 +7759,11 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { HValue* function = Pop(); Add(function, expr->target()); - // Replace the global object with the global receiver. - HGlobalReceiver* global_receiver = Add(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) { @@ -7761,9 +7780,12 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { } 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(var->name(), argument_count)); + call = PreProcessCall(New(var->name(), argument_count)); } else { call = PreProcessCall(New( expr->target(), argument_count)); @@ -7788,6 +7810,12 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { CHECK_ALIVE(VisitExpressions(expr->arguments())); Add(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 "); @@ -7808,12 +7836,11 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { } else { CHECK_ALIVE(VisitForValue(expr->expression())); HValue* function = Top(); - HGlobalObject* global_object = Add(); - HGlobalReceiver* receiver = Add(global_object); + HValue* receiver = graph()->GetConstantHole(); Push(Add(receiver)); CHECK_ALIVE(VisitArgumentList(expr->arguments())); - - call = New(function, argument_count); + call = New( + function, argument_count, NORMAL_CONTEXTUAL_CALL); Drop(argument_count + 1); } } diff --git a/src/hydrogen.h b/src/hydrogen.h index ca3fb43..2af2469 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -2502,6 +2502,9 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { HValue* receiver, Handle receiver_map); + void InstallGlobalReceiverInExpressionStack(int index, + Handle function); + // The translation state of the currently-being-translated function. FunctionState* function_state_; diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 1f73a7d..67503e3 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -32,6 +32,7 @@ #include "codegen.h" #include "deoptimizer.h" #include "full-codegen.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -783,12 +784,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 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); @@ -961,12 +957,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { // 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); diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 305a6d8..2ba08c5 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -2517,29 +2517,46 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { 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); diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index e43e525..d3b1dd8 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -2769,11 +2769,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { { 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 diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index 2973beb..4db8802 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -1112,8 +1112,9 @@ void CallICBase::GenerateMiss(MacroAssembler* masm, // 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); } diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 1a20fc3..ade9ee3 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -3675,10 +3675,7 @@ void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) { // 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); } @@ -4245,7 +4242,10 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) { 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); diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 6e720f1..6bf6179 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -1166,7 +1166,7 @@ LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( 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); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 6ab3964..d63c2bb 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -2468,11 +2468,23 @@ void StubCompiler::GenerateBooleanCheck(Register object, Label* miss) { } -void CallStubCompiler::PatchGlobalProxy(Handle object) { +void CallStubCompiler::PatchGlobalProxy(Handle object, + Handle 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, + 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); } } @@ -2566,7 +2578,7 @@ void CallStubCompiler::GenerateJumpFunction(Handle object, GenerateFunctionCheck(function, ebx, miss); if (!function.is(edi)) __ mov(edi, function); - PatchGlobalProxy(object); + PatchGlobalProxy(object, function); // Invoke the function. __ InvokeFunction(edi, arguments(), JUMP_FUNCTION, @@ -2681,6 +2693,15 @@ Handle StoreStubCompiler::CompileStoreCallback( #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 setter) { diff --git a/src/runtime.cc b/src/runtime.cc index 0c1c619..281c650 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -9252,7 +9252,7 @@ static ObjectPair LoadContextSlotHelper(Arguments args, // GetProperty below can cause GC. Handle receiver_handle( object->IsGlobalObject() - ? GlobalObject::cast(*object)->global_receiver() + ? Object::cast(isolate->heap()->the_hole_value()) : object->IsJSProxy() ? static_cast(*object) : ComputeReceiverForNonGlobal(isolate, JSObject::cast(*object)), isolate); diff --git a/src/runtime.js b/src/runtime.js index 35bc07a..2a949ae 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -48,7 +48,6 @@ var $Number = global.Number; var $Function = global.Function; var $Boolean = global.Boolean; var $NaN = %GetRootNaN(); -var builtins = this; // ECMA-262 Section 11.9.3. function EQUALS(y) { diff --git a/src/stub-cache.cc b/src/stub-cache.cc index dc2c340..e8b94f1 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -1155,7 +1155,7 @@ void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { - PatchGlobalProxy(object); + PatchGlobalProxy(object, function); GenerateJumpFunctionIgnoreReceiver(function); } @@ -1163,7 +1163,7 @@ void CallStubCompiler::GenerateJumpFunction(Handle object, void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { - PatchGlobalProxy(object); + PatchGlobalProxy(object, function); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); diff --git a/src/stub-cache.h b/src/stub-cache.h index ebf0bd3..0cf9e8f 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -912,7 +912,11 @@ class CallStubCompiler: public StubCompiler { // Patch the global proxy over the global object if the global object is the // receiver. - void PatchGlobalProxy(Handle object); + static void FetchGlobalProxy(MacroAssembler* masm, + Register target, + Register function); + void PatchGlobalProxy(Handle object, Register function); + void PatchGlobalProxy(Handle object, Handle function); // Returns the register containing the holder of |name|. Register HandlerFrontendHeader(Handle object, diff --git a/src/v8globals.h b/src/v8globals.h index a9980e5..da290b2 100644 --- a/src/v8globals.h +++ b/src/v8globals.h @@ -293,8 +293,10 @@ enum CallFunctionFlags { // 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 }; diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 7803073..b2cd558 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -32,6 +32,7 @@ #include "codegen.h" #include "deoptimizer.h" #include "full-codegen.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -846,12 +847,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 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); @@ -1031,13 +1027,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { // 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); diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 920843d..b8e783d 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -2350,29 +2350,46 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { 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); } @@ -2414,7 +2431,7 @@ void CallFunctionStub::Generate(MacroAssembler* 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 adaptor = diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index a56f223..99206b9 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -2746,11 +2746,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { { 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 diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 9448d37..b2d33e0 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -998,7 +998,7 @@ void CallICBase::GenerateMiss(MacroAssembler* masm, // 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); } diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index d35219c..e6a20f2 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -3250,10 +3250,7 @@ void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) { // 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); } @@ -3818,7 +3815,10 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) { 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); diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 950ee28..3bbe1fc 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -2378,10 +2378,21 @@ void StubCompiler::GenerateBooleanCheck(Register object, Label* miss) { } -void CallStubCompiler::PatchGlobalProxy(Handle object) { +void CallStubCompiler::PatchGlobalProxy(Handle object, + Handle 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, + Register function) { + if (object->IsGlobalObject()) { + FetchGlobalProxy(masm(), rdx, function); + StackArgumentsAccessor args(rsp, arguments().immediate()); __ movq(args.GetReceiverOperand(), rdx); } } @@ -2475,7 +2486,7 @@ void CallStubCompiler::GenerateJumpFunction(Handle object, GenerateFunctionCheck(function, rbx, miss); if (!function.is(rdi)) __ movq(rdi, function); - PatchGlobalProxy(object); + PatchGlobalProxy(object, function); // Invoke the function. __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION, @@ -2588,6 +2599,15 @@ Handle StoreStubCompiler::CompileStoreCallback( #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 setter) { diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index eeb7865..372c70d 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -8704,12 +8704,23 @@ TEST(DetachGlobal) { } +void GetThisX(const v8::FunctionCallbackInfo& 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 env2 = Context::New(env1->GetIsolate()); + Local inner_global_template = + FunctionTemplate::New(env1->GetIsolate())->InstanceTemplate(); + inner_global_template ->SetAccessorProperty( + v8_str("this_x"), FunctionTemplate::New(env1->GetIsolate(), GetThisX)); + v8::Local env2 = + Context::New(env1->GetIsolate(), NULL, inner_global_template); Local foo = v8_str("foo"); @@ -8717,15 +8728,21 @@ TEST(DetachedAccesses) { 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 env2_global = env2->Global(); @@ -8733,10 +8750,14 @@ TEST(DetachedAccesses) { env2->DetachGlobal(); Local 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(), @@ -8746,13 +8767,62 @@ TEST(DetachedAccesses) { 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 results = Local::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 results = Local::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::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)); + } } @@ -20110,11 +20180,10 @@ THREADED_TEST(ForeignFunctionReceiver) { 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)()"); } diff --git a/test/mjsunit/contextual-calls.js b/test/mjsunit/contextual-calls.js new file mode 100644 index 0000000..10c3e8d --- /dev/null +++ b/test/mjsunit/contextual-calls.js @@ -0,0 +1,103 @@ +// 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); +} diff --git a/test/mjsunit/harmony/proxies-function.js b/test/mjsunit/harmony/proxies-function.js index 6b8d098..8c91e9b 100644 --- a/test/mjsunit/harmony/proxies-function.js +++ b/test/mjsunit/harmony/proxies-function.js @@ -53,8 +53,7 @@ var receiver 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) { @@ -67,8 +66,7 @@ function TestCall(isStrict, callTrap) { 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) @@ -746,3 +744,31 @@ function TestCalls() { 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()); + } +}