Make _CallFunction proxy-aware.
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 8 Nov 2011 14:39:37 +0000 (14:39 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 8 Nov 2011 14:39:37 +0000 (14:39 +0000)
Change calling convention for CallFunction stub.
Some fixes regarding strict mode call traps.

R=kmillikin@chromium.org
BUG=
TEST=

Review URL: http://codereview.chromium.org/8318014

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9916 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

29 files changed:
src/arm/code-stubs-arm.cc
src/arm/debug-arm.cc
src/arm/full-codegen-arm.cc
src/arm/lithium-arm.cc
src/arm/lithium-arm.h
src/arm/lithium-codegen-arm.cc
src/builtins.cc
src/builtins.h
src/debug.cc
src/debug.h
src/hydrogen-instructions.h
src/hydrogen.cc
src/ia32/builtins-ia32.cc
src/ia32/code-stubs-ia32.cc
src/ia32/debug-ia32.cc
src/ia32/full-codegen-ia32.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/lithium-ia32.cc
src/ia32/lithium-ia32.h
src/runtime.cc
src/runtime.h
src/x64/code-stubs-x64.cc
src/x64/debug-x64.cc
src/x64/full-codegen-x64.cc
src/x64/lithium-codegen-x64.cc
src/x64/lithium-x64.cc
src/x64/lithium-x64.h
test/mjsunit/fuzz-natives.js
test/mjsunit/harmony/proxies-function.js

index 7a541f5..023312f 100644 (file)
@@ -4917,6 +4917,7 @@ Object* CallFunctionStub::GetCachedValue(Address address) {
 
 
 void CallFunctionStub::Generate(MacroAssembler* masm) {
+  // r1 : the function to call
   Label slow, non_function;
 
   // The receiver might implicitly be the global object. This is
@@ -4931,16 +4932,12 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
     __ CompareRoot(r4, Heap::kTheHoleValueRootIndex);
     __ b(ne, &call);
     // Patch the receiver on the stack with the global receiver object.
-    __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
-    __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
-    __ str(r1, MemOperand(sp, argc_ * kPointerSize));
+    __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+    __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
+    __ str(r2, MemOperand(sp, argc_ * kPointerSize));
     __ bind(&call);
   }
 
-  // Get the function to call from the stack.
-  // function, receiver [, arguments]
-  __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize));
-
   // Check that the function is really a JavaScript function.
   // r1: pushed function (to be verified)
   __ JumpIfSmi(r1, &non_function);
@@ -4978,7 +4975,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
   __ mov(r0, Operand(argc_ + 1, RelocInfo::NONE));
   __ mov(r2, Operand(0, RelocInfo::NONE));
   __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY);
-  __ SetCallKind(r5, CALL_AS_FUNCTION);
+  __ SetCallKind(r5, CALL_AS_METHOD);
   {
     Handle<Code> adaptor =
       masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
index b866f9c..8374103 100644 (file)
@@ -267,11 +267,11 @@ void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
 }
 
 
-void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
+void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
   // ----------- S t a t e -------------
-  //  No registers used on entry.
+  //  -- r1 : function
   // -----------------------------------
-  Generate_DebugBreakCallHelper(masm, 0, 0);
+  Generate_DebugBreakCallHelper(masm, r1.bit(), 0);
 }
 
 
index 20c10c9..9507717 100644 (file)
@@ -2217,6 +2217,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
   // Record source position for debugger.
   SetSourcePosition(expr->position());
   CallFunctionStub stub(arg_count, flags);
+  __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
   __ CallStub(&stub);
   RecordJSReturnSite(expr);
   // Restore context register.
@@ -2294,6 +2295,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     // Record source position for debugger.
     SetSourcePosition(expr->position());
     CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT);
+    __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
     __ CallStub(&stub);
     RecordJSReturnSite(expr);
     // Restore context register.
@@ -3199,12 +3201,24 @@ void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
   }
   VisitForAccumulatorValue(args->last());  // Function.
 
+  // Check for proxy.
+  Label proxy, done;
+  __ CompareObjectType(r0, r1, r1, JS_FUNCTION_PROXY_TYPE);
+  __ b(eq, &proxy);
+
   // InvokeFunction requires the function in r1. Move it in there.
   __ mov(r1, result_register());
   ParameterCount count(arg_count);
   __ InvokeFunction(r1, count, CALL_FUNCTION,
                     NullCallWrapper(), CALL_AS_METHOD);
   __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+  __ jmp(&done);
+
+  __ bind(&proxy);
+  __ push(r0);
+  __ CallRuntime(Runtime::kCall, args->length());
+  __ bind(&done);
+
   context()->Plug(r0);
 }
 
index ffb7457..62eae3f 100644 (file)
@@ -1196,8 +1196,9 @@ LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) {
 
 
 LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) {
+  LOperand* function = UseFixed(instr->function(), r1);
   argument_count_ -= instr->argument_count();
-  return MarkAsCall(DefineFixed(new LCallFunction, r0), instr);
+  return MarkAsCall(DefineFixed(new LCallFunction(function), r0), instr);
 }
 
 
index 1452e53..5a73bed 100644 (file)
@@ -1379,12 +1379,17 @@ class LCallNamed: public LTemplateInstruction<1, 0, 0> {
 };
 
 
-class LCallFunction: public LTemplateInstruction<1, 0, 0> {
+class LCallFunction: public LTemplateInstruction<1, 1, 0> {
  public:
+  explicit LCallFunction(LOperand* function) {
+    inputs_[0] = function;
+  }
+
   DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function")
   DECLARE_HYDROGEN_ACCESSOR(CallFunction)
 
-  int arity() const { return hydrogen()->argument_count() - 2; }
+  LOperand* function() { return inputs_[0]; }
+  int arity() const { return hydrogen()->argument_count() - 1; }
 };
 
 
index 4cf7df4..59f9ea0 100644 (file)
@@ -3266,12 +3266,12 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) {
 
 
 void LCodeGen::DoCallFunction(LCallFunction* instr) {
+  ASSERT(ToRegister(instr->function()).is(r1));
   ASSERT(ToRegister(instr->result()).is(r0));
 
   int arity = instr->arity();
   CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
   CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
-  __ Drop(1);
   __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
 }
 
index f252911..49053db 100644 (file)
@@ -1560,8 +1560,8 @@ static void Generate_Return_DebugBreak(MacroAssembler* masm) {
 }
 
 
-static void Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm) {
-  Debug::GenerateStubNoRegistersDebugBreak(masm);
+static void Generate_CallFunctionStub_DebugBreak(MacroAssembler* masm) {
+  Debug::GenerateCallFunctionStubDebugBreak(masm);
 }
 
 
index 24059e7..3659f99 100644 (file)
@@ -192,27 +192,27 @@ enum BuiltinExtraArguments {
 
 #ifdef ENABLE_DEBUGGER_SUPPORT
 // Define list of builtins used by the debugger implemented in assembly.
-#define BUILTIN_LIST_DEBUG_A(V)                                \
-  V(Return_DebugBreak,          BUILTIN, DEBUG_BREAK,          \
-                                Code::kNoExtraICState)         \
-  V(ConstructCall_DebugBreak,   BUILTIN, DEBUG_BREAK,          \
-                                Code::kNoExtraICState)         \
-  V(StubNoRegisters_DebugBreak, BUILTIN, DEBUG_BREAK,          \
-                                Code::kNoExtraICState)         \
-  V(LoadIC_DebugBreak,          LOAD_IC, DEBUG_BREAK,          \
-                                Code::kNoExtraICState)         \
-  V(KeyedLoadIC_DebugBreak,     KEYED_LOAD_IC, DEBUG_BREAK,    \
-                                Code::kNoExtraICState)         \
-  V(StoreIC_DebugBreak,         STORE_IC, DEBUG_BREAK,         \
-                                Code::kNoExtraICState)         \
-  V(KeyedStoreIC_DebugBreak,    KEYED_STORE_IC, DEBUG_BREAK,   \
-                                Code::kNoExtraICState)         \
-  V(Slot_DebugBreak,            BUILTIN, DEBUG_BREAK,          \
-                                Code::kNoExtraICState)         \
-  V(PlainReturn_LiveEdit,       BUILTIN, DEBUG_BREAK,          \
-                                Code::kNoExtraICState)         \
-  V(FrameDropper_LiveEdit,      BUILTIN, DEBUG_BREAK,          \
-                                Code::kNoExtraICState)
+#define BUILTIN_LIST_DEBUG_A(V)                                 \
+  V(Return_DebugBreak,           BUILTIN, DEBUG_BREAK,          \
+                                 Code::kNoExtraICState)         \
+  V(ConstructCall_DebugBreak,    BUILTIN, DEBUG_BREAK,          \
+                                 Code::kNoExtraICState)         \
+  V(CallFunctionStub_DebugBreak, BUILTIN, DEBUG_BREAK,          \
+                                 Code::kNoExtraICState)         \
+  V(LoadIC_DebugBreak,           LOAD_IC, DEBUG_BREAK,          \
+                                 Code::kNoExtraICState)         \
+  V(KeyedLoadIC_DebugBreak,      KEYED_LOAD_IC, DEBUG_BREAK,    \
+                                 Code::kNoExtraICState)         \
+  V(StoreIC_DebugBreak,          STORE_IC, DEBUG_BREAK,         \
+                                 Code::kNoExtraICState)         \
+  V(KeyedStoreIC_DebugBreak,     KEYED_STORE_IC, DEBUG_BREAK,   \
+                                 Code::kNoExtraICState)         \
+  V(Slot_DebugBreak,             BUILTIN, DEBUG_BREAK,          \
+                                 Code::kNoExtraICState)         \
+  V(PlainReturn_LiveEdit,        BUILTIN, DEBUG_BREAK,          \
+                                 Code::kNoExtraICState)         \
+  V(FrameDropper_LiveEdit,       BUILTIN, DEBUG_BREAK,          \
+                                 Code::kNoExtraICState)
 #else
 #define BUILTIN_LIST_DEBUG_A(V)
 #endif
index 88149d8..6e5a51f 100644 (file)
@@ -1570,7 +1570,7 @@ Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) {
   if (code->kind() == Code::STUB) {
     ASSERT(code->major_key() == CodeStub::CallFunction);
     Handle<Code> result =
-        Isolate::Current()->builtins()->StubNoRegisters_DebugBreak();
+        Isolate::Current()->builtins()->CallFunctionStub_DebugBreak();
     return result;
   }
 
index 3c37186..a39d801 100644 (file)
@@ -402,7 +402,7 @@ class Debug {
   static void GenerateKeyedStoreICDebugBreak(MacroAssembler* masm);
   static void GenerateConstructCallDebugBreak(MacroAssembler* masm);
   static void GenerateReturnDebugBreak(MacroAssembler* masm);
-  static void GenerateStubNoRegistersDebugBreak(MacroAssembler* masm);
+  static void GenerateCallFunctionStubDebugBreak(MacroAssembler* masm);
   static void GenerateSlotDebugBreak(MacroAssembler* masm);
   static void GeneratePlainReturnLiveEdit(MacroAssembler* masm);
 
index 101d62a..64a40b9 100644 (file)
@@ -1594,13 +1594,14 @@ class HCallNamed: public HUnaryCall {
 };
 
 
-class HCallFunction: public HUnaryCall {
+class HCallFunction: public HBinaryCall {
  public:
-  HCallFunction(HValue* context, int argument_count)
-      : HUnaryCall(context, argument_count) {
+  HCallFunction(HValue* context, HValue* function, int argument_count)
+      : HBinaryCall(context, function, argument_count) {
   }
 
-  HValue* context() { return value(); }
+  HValue* context() { return first(); }
+  HValue* function() { return second(); }
 
   virtual Representation RequiredInputRepresentation(int index) {
     return Representation::Tagged();
index 95685df..0f5694f 100644 (file)
@@ -5152,7 +5152,8 @@ void HGraphBuilder::VisitCall(Call* expr) {
       }
 
     } else {
-      CHECK_ALIVE(VisitArgument(expr->expression()));
+      CHECK_ALIVE(VisitForValue(expr->expression()));
+      HValue* function = Top();
       HValue* context = environment()->LookupContext();
       HGlobalObject* global_object = new(zone()) HGlobalObject(context);
       HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object);
@@ -5161,9 +5162,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
       PushAndAdd(new(zone()) HPushArgument(receiver));
       CHECK_ALIVE(VisitArgumentList(expr->arguments()));
 
-      // The function to call is treated as an argument to the call function
-      // stub.
-      call = new(zone()) HCallFunction(context, argument_count + 1);
+      call = new(zone()) HCallFunction(context, function, argument_count);
       Drop(argument_count + 1);
     }
   }
@@ -6425,12 +6424,37 @@ void HGraphBuilder::GenerateCallFunction(CallRuntime* call) {
     CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
   }
   CHECK_ALIVE(VisitForValue(call->arguments()->last()));
+
   HValue* function = Pop();
   HValue* context = environment()->LookupContext();
-  HInvokeFunction* result =
-      new(zone()) HInvokeFunction(context, function, arg_count);
+
+  // Branch for function proxies, or other non-functions.
+  HHasInstanceTypeAndBranch* typecheck =
+      new(zone()) HHasInstanceTypeAndBranch(function, JS_FUNCTION_TYPE);
+  HBasicBlock* if_jsfunction = graph()->CreateBasicBlock();
+  HBasicBlock* if_nonfunction = graph()->CreateBasicBlock();
+  HBasicBlock* join = graph()->CreateBasicBlock();
+  typecheck->SetSuccessorAt(0, if_jsfunction);
+  typecheck->SetSuccessorAt(1, if_nonfunction);
+  current_block()->Finish(typecheck);
+
+  set_current_block(if_jsfunction);
+  HInstruction* invoke_result = AddInstruction(
+      new(zone()) HInvokeFunction(context, function, arg_count));
   Drop(arg_count);
-  return ast_context()->ReturnInstruction(result, call->id());
+  Push(invoke_result);
+  if_jsfunction->Goto(join);
+
+  set_current_block(if_nonfunction);
+  HInstruction* call_result = AddInstruction(
+      new(zone()) HCallFunction(context, function, arg_count));
+  Drop(arg_count);
+  Push(call_result);
+  if_nonfunction->Goto(join);
+
+  set_current_block(join);
+  join->SetJoinId(call->id());
+  return ast_context()->ReturnValue(Pop());
 }
 
 
index ac4da4c..e12e79a 100644 (file)
@@ -719,7 +719,6 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ test(edx, edx);
     __ j(zero, &function);
     __ Set(ebx, Immediate(0));
-    __ SetCallKind(ecx, CALL_AS_METHOD);
     __ cmp(edx, Immediate(1));
     __ j(not_equal, &non_proxy);
 
@@ -727,11 +726,13 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ push(edi);  // re-add proxy object as additional argument
     __ push(edx);
     __ inc(eax);
+    __ SetCallKind(ecx, CALL_AS_FUNCTION);
     __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
     __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
            RelocInfo::CODE_TARGET);
 
     __ bind(&non_proxy);
+    __ SetCallKind(ecx, CALL_AS_METHOD);
     __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
     __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
            RelocInfo::CODE_TARGET);
index 50cddca..432c10c 100644 (file)
@@ -4361,6 +4361,7 @@ Object* CallFunctionStub::GetCachedValue(Address address) {
 
 
 void CallFunctionStub::Generate(MacroAssembler* masm) {
+  // edi : the function to call
   Isolate* isolate = masm->isolate();
   Label slow, non_function;
 
@@ -4382,10 +4383,6 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
     __ bind(&receiver_ok);
   }
 
-  // Get the function to call from the stack.
-  // +2 ~ receiver, return address
-  __ mov(edi, Operand(esp, (argc_ + 2) * kPointerSize));
-
   // Check that the function really is a JavaScript function.
   __ JumpIfSmi(edi, &non_function);
   // Goto slow case if we do not have a function.
index d7184ed..2649560 100644 (file)
@@ -244,12 +244,12 @@ void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
 }
 
 
-void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
+void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
   // Register state for stub CallFunction (from CallFunctionStub in ic-ia32.cc).
   // ----------- S t a t e -------------
-  //  No registers used on entry.
+  //  -- edi: function
   // -----------------------------------
-  Generate_DebugBreakCallHelper(masm, 0, 0, false);
+  Generate_DebugBreakCallHelper(masm, edi.bit(), 0, false);
 }
 
 
index 1990f2f..5b03056 100644 (file)
@@ -137,6 +137,10 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
     __ j(zero, &ok, Label::kNear);
     // +1 for return address.
     int receiver_offset = (info->scope()->num_parameters() + 1) * kPointerSize;
+    __ mov(ecx, Operand(esp, receiver_offset));
+    __ JumpIfSmi(ecx, &ok);
+    __ CmpObjectType(ecx, JS_GLOBAL_PROXY_TYPE, ecx);
+    __ j(not_equal, &ok, Label::kNear);
     __ mov(Operand(esp, receiver_offset),
            Immediate(isolate()->factory()->undefined_value()));
     __ bind(&ok);
@@ -2105,6 +2109,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
     flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET);
   }
   CallFunctionStub stub(arg_count, flags);
+  __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
   __ CallStub(&stub, expr->id());
   if (record_call_target) {
     // There is a one element cache in the instruction stream.
@@ -2189,6 +2194,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     // Record source position for debugger.
     SetSourcePosition(expr->position());
     CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT);
+    __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
     __ CallStub(&stub);
     RecordJSReturnSite(expr);
     // Restore context register.
@@ -3101,12 +3107,24 @@ void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
   }
   VisitForAccumulatorValue(args->last());  // Function.
 
+  // Check for proxy.
+  Label proxy, done;
+  __ CmpObjectType(eax, JS_FUNCTION_PROXY_TYPE, ebx);
+  __ j(equal, &proxy);
+
   // InvokeFunction requires the function in edi. Move it in there.
   __ mov(edi, result_register());
   ParameterCount count(arg_count);
   __ InvokeFunction(edi, count, CALL_FUNCTION,
                     NullCallWrapper(), CALL_AS_METHOD);
   __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+  __ jmp(&done);
+
+  __ bind(&proxy);
+  __ push(eax);
+  __ CallRuntime(Runtime::kCall, args->length());
+  __ bind(&done);
+
   context()->Plug(eax);
 }
 
index d4cbbce..f8459fc 100644 (file)
@@ -3117,12 +3117,12 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) {
 
 void LCodeGen::DoCallFunction(LCallFunction* instr) {
   ASSERT(ToRegister(instr->context()).is(esi));
+  ASSERT(ToRegister(instr->function()).is(edi));
   ASSERT(ToRegister(instr->result()).is(eax));
 
   int arity = instr->arity();
   CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
   CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
-  __ Drop(1);
 }
 
 
index 227d0b5..1f01df6 100644 (file)
@@ -1233,8 +1233,9 @@ LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) {
 
 LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) {
   LOperand* context = UseFixed(instr->context(), esi);
+  LOperand* function = UseFixed(instr->function(), edi);
   argument_count_ -= instr->argument_count();
-  LCallFunction* result = new(zone()) LCallFunction(context);
+  LCallFunction* result = new(zone()) LCallFunction(context, function);
   return MarkAsCall(DefineFixed(result, eax), instr);
 }
 
index 98487b4..cb8b3dc 100644 (file)
@@ -1421,17 +1421,19 @@ class LCallNamed: public LTemplateInstruction<1, 1, 0> {
 };
 
 
-class LCallFunction: public LTemplateInstruction<1, 1, 0> {
+class LCallFunction: public LTemplateInstruction<1, 2, 0> {
  public:
-  explicit LCallFunction(LOperand* context) {
+  explicit LCallFunction(LOperand* context, LOperand* function) {
     inputs_[0] = context;
+    inputs_[1] = function;
   }
 
   DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function")
   DECLARE_HYDROGEN_ACCESSOR(CallFunction)
 
   LOperand* context() { return inputs_[0]; }
-  int arity() const { return hydrogen()->argument_count() - 2; }
+  LOperand* function() { return inputs_[1]; }
+  int arity() const { return hydrogen()->argument_count() - 1; }
 };
 
 
index c1a87db..0e59111 100644 (file)
@@ -8699,6 +8699,42 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CheckIsBootstrapping) {
 }
 
 
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Call) {
+  HandleScope scope(isolate);
+  ASSERT(args.length() >= 2);
+  CONVERT_CHECKED(JSReceiver, fun, args[args.length() - 1]);
+  Object* receiver = args[0];
+  int argc = args.length() - 2;
+
+  // If there are too many arguments, allocate argv via malloc.
+  const int argv_small_size = 10;
+  Handle<Object> argv_small_buffer[argv_small_size];
+  SmartArrayPointer<Handle<Object> > argv_large_buffer;
+  Handle<Object>* argv = argv_small_buffer;
+  if (argc > argv_small_size) {
+    argv = new Handle<Object>[argc];
+    if (argv == NULL) return isolate->StackOverflow();
+    argv_large_buffer = SmartArrayPointer<Handle<Object> >(argv);
+  }
+
+  for (int i = 0; i < argc; ++i) {
+     MaybeObject* maybe = args[1 + i];
+     Object* object;
+     if (!maybe->To<Object>(&object)) return maybe;
+     argv[i] = Handle<Object>(object);
+  }
+
+  bool threw;
+  Handle<JSReceiver> hfun(fun);
+  Handle<Object> hreceiver(receiver);
+  Handle<Object> result =
+      Execution::Call(hfun, hreceiver, argc, argv, &threw, true);
+
+  if (threw) return Failure::Exception();
+  return *result;
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_Apply) {
   HandleScope scope(isolate);
   ASSERT(args.length() == 5);
index c411b30..581a6ab 100644 (file)
@@ -80,6 +80,7 @@ namespace internal {
   \
   /* Utilities */ \
   F(CheckIsBootstrapping, 0, 1) \
+  F(Call, -1 /* >= 2 */, 1) \
   F(Apply, 5, 1) \
   F(GetFunctionDelegate, 1, 1) \
   F(GetConstructorDelegate, 1, 1) \
index d7618b7..bd842d9 100644 (file)
@@ -3365,6 +3365,7 @@ Object* CallFunctionStub::GetCachedValue(Address address) {
 
 
 void CallFunctionStub::Generate(MacroAssembler* masm) {
+  // rdi : the function to call
   Label slow, non_function;
 
   // The receiver might implicitly be the global object. This is
@@ -3385,10 +3386,6 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
     __ bind(&call);
   }
 
-  // Get the function to call from the stack.
-  // +2 ~ receiver, return address
-  __ movq(rdi, Operand(rsp, (argc_ + 2) * kPointerSize));
-
   // Check that the function really is a JavaScript function.
   __ JumpIfSmi(rdi, &non_function);
   // Goto slow case if we do not have a function.
@@ -3425,7 +3422,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
   __ push(rcx);
   __ Set(rax, argc_ + 1);
   __ Set(rbx, 0);
-  __ SetCallKind(rcx, CALL_AS_FUNCTION);
+  __ SetCallKind(rcx, CALL_AS_METHOD);
   __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY);
   {
     Handle<Code> adaptor =
index 2149fc2..339b961 100644 (file)
@@ -250,12 +250,12 @@ void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
 }
 
 
-void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
+void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
   // Register state for stub CallFunction (from CallFunctionStub in ic-x64.cc).
   // ----------- S t a t e -------------
-  //  No registers used on entry.
+  //  -- rdi : function
   // -----------------------------------
-  Generate_DebugBreakCallHelper(masm, 0, 0, false);
+  Generate_DebugBreakCallHelper(masm, rdi.bit(), 0, false);
 }
 
 
index 1638c72..aeff76a 100644 (file)
@@ -2067,6 +2067,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
   // Record source position for debugger.
   SetSourcePosition(expr->position());
   CallFunctionStub stub(arg_count, flags);
+  __ movq(rdi, Operand(rsp, (arg_count + 1) * kPointerSize));
   __ CallStub(&stub);
   RecordJSReturnSite(expr);
   // Restore context register.
@@ -2137,6 +2138,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     // Record source position for debugger.
     SetSourcePosition(expr->position());
     CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT);
+    __ movq(rdi, Operand(rsp, (arg_count + 1) * kPointerSize));
     __ CallStub(&stub);
     RecordJSReturnSite(expr);
     // Restore context register.
@@ -3034,12 +3036,24 @@ void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
   }
   VisitForAccumulatorValue(args->last());  // Function.
 
+  // Check for proxy.
+  Label proxy, done;
+  __ CmpObjectType(rax, JS_FUNCTION_PROXY_TYPE, rbx);
+  __ j(equal, &proxy);
+
   // InvokeFunction requires the function in rdi. Move it in there.
   __ movq(rdi, result_register());
   ParameterCount count(arg_count);
   __ InvokeFunction(rdi, count, CALL_FUNCTION,
                     NullCallWrapper(), CALL_AS_METHOD);
   __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+  __ jmp(&done);
+
+  __ bind(&proxy);
+  __ push(rax);
+  __ CallRuntime(Runtime::kCall, args->length());
+  __ bind(&done);
+
   context()->Plug(rax);
 }
 
index 38a8c18..d4875da 100644 (file)
@@ -3036,13 +3036,13 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) {
 
 
 void LCodeGen::DoCallFunction(LCallFunction* instr) {
+  ASSERT(ToRegister(instr->function()).is(rdi));
   ASSERT(ToRegister(instr->result()).is(rax));
 
   int arity = instr->arity();
   CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
   CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
   __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
-  __ Drop(1);
 }
 
 
index 598f890..e9e7896 100644 (file)
@@ -1191,8 +1191,9 @@ LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) {
 
 
 LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) {
+  LOperand* function = UseFixed(instr->function(), rdi);
   argument_count_ -= instr->argument_count();
-  LCallFunction* result = new LCallFunction();
+  LCallFunction* result = new LCallFunction(function);
   return MarkAsCall(DefineFixed(result, rax), instr);
 }
 
index b542071..f5edd62 100644 (file)
@@ -1374,14 +1374,17 @@ class LCallNamed: public LTemplateInstruction<1, 0, 0> {
 };
 
 
-class LCallFunction: public LTemplateInstruction<1, 0, 0> {
+class LCallFunction: public LTemplateInstruction<1, 1, 0> {
  public:
-  LCallFunction() {}
+  explicit LCallFunction(LOperand* function) {
+    inputs_[0] = function;
+  }
 
   DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function")
   DECLARE_HYDROGEN_ACCESSOR(CallFunction)
 
-  int arity() const { return hydrogen()->argument_count() - 2; }
+  LOperand* function() { return inputs_[0]; }
+  int arity() const { return hydrogen()->argument_count() - 1; }
 };
 
 
index ff6677e..c4d18d0 100644 (file)
@@ -163,6 +163,9 @@ var knownProblems = {
   "PromoteScheduledException": true,
   "DeleteHandleScopeExtensions": true,
 
+  // Vararg with minimum number > 0.
+  "Call": true,
+
   // Requires integer arguments to be non-negative.
   "Apply": true,
 
index 6a88d19..42ba659 100644 (file)
@@ -25,7 +25,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-// Flags: --harmony-proxies
+// Flags: --harmony-proxies --allow-natives-syntax
 
 
 // Helper.
@@ -53,7 +53,7 @@ var receiver
 
 function TestCall(isStrict, callTrap) {
   assertEquals(42, callTrap(5, 37))
-  assertEquals(isStrict ? undefined : global_object, receiver)
+  assertSame(isStrict ? undefined : global_object, receiver)
 
   var handler = {
     get: function(r, k) {
@@ -61,11 +61,12 @@ function TestCall(isStrict, callTrap) {
     }
   }
   var f = Proxy.createFunction(handler, callTrap)
+  var o = {f: f}
+  global_object.f = f
 
   receiver = 333
   assertEquals(42, f(11, 31))
-  assertEquals(isStrict ? undefined : global_object, receiver)
-  var o = {f: f}
+  assertSame(isStrict ? undefined : global_object, receiver)
   receiver = 333
   assertEquals(42, o.f(10, 32))
   assertSame(o, receiver)
@@ -82,6 +83,9 @@ function TestCall(isStrict, callTrap) {
   assertEquals(42, f.call(o, 32, 10))
   assertSame(o, receiver)
   receiver = 333
+  assertEquals(42, f.call(undefined, 33, 9))
+  assertSame(isStrict ? undefined : global_object, receiver)
+  receiver = 333
   assertEquals(42, f.call(null, 33, 9))
   assertSame(isStrict ? null : global_object, receiver)
   receiver = 333
@@ -101,6 +105,24 @@ function TestCall(isStrict, callTrap) {
   receiver = 333
   assertEquals(32, Function.prototype.apply.call(f, o, [17, 15]))
   assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %Call(o, 11, 31, f))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %Call(null, 11, 31, f))
+  assertSame(isStrict ? null : global_object, receiver)
+  receiver = 333
+  assertEquals(42, %Apply(f, o, [11, 31], 0, 2))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %Apply(f, null, [11, 31], 0, 2))
+  assertSame(isStrict ? null : global_object, receiver)
+  receiver = 333
+  assertEquals(42, %_CallFunction(o, 11, 31, f))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %_CallFunction(null, 11, 31, f))
+  assertSame(isStrict ? null : global_object, receiver)
 
   var ff = Function.prototype.bind.call(f, o, 12)
   assertTrue(ff.length <= 1)  // TODO(rossberg): Not spec'ed yet, be lax.
@@ -108,8 +130,29 @@ function TestCall(isStrict, callTrap) {
   assertEquals(42, ff(30))
   assertSame(o, receiver)
   receiver = 333
+  assertEquals(33, Function.prototype.call.call(ff, {}, 21))
+  assertSame(o, receiver)
+  receiver = 333
   assertEquals(32, Function.prototype.apply.call(ff, {}, [20]))
   assertSame(o, receiver)
+  receiver = 333
+  assertEquals(23, %Call({}, 11, ff))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(23, %Call({}, 11, 3, ff))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(24, %Apply(ff, {}, [12, 13], 0, 1))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(24, %Apply(ff, {}, [12, 13], 0, 2))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(34, %_CallFunction({}, 22, ff))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(34, %_CallFunction({}, 22, 3, ff))
+  assertSame(o, receiver)
 
   var fff = Function.prototype.bind.call(ff, o, 30)
   assertEquals(0, fff.length)
@@ -119,6 +162,30 @@ function TestCall(isStrict, callTrap) {
   receiver = 333
   assertEquals(42, Function.prototype.call.call(fff, {}))
   assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, Function.prototype.apply.call(fff, {}))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %Call({}, fff))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %Call({}, 11, 3, fff))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %Apply(fff, {}, [], 0, 0))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %Apply(fff, {}, [12, 13], 0, 0))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %Apply(fff, {}, [12, 13], 0, 2))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %_CallFunction({}, fff))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %_CallFunction({}, 3, 4, 5, fff))
+  assertSame(o, receiver)
 
   var f = CreateFrozen({}, callTrap)
   receiver = 333
@@ -144,10 +211,13 @@ function TestCall(isStrict, callTrap) {
   assertEquals(32, Function.prototype.apply.call(f, o, [17, 15]))
   assertSame(o, receiver)
   receiver = 333
-  assertEquals(42, ff(30))
+  assertEquals(23, %Call({}, 11, 12, f))
   assertSame(o, receiver)
   receiver = 333
-  assertEquals(32, Function.prototype.apply.call(ff, {}, [20]))
+  assertEquals(27, %Apply(f, {}, [12, 13, 14], 1, 2))
+  assertSame(o, receiver)
+  receiver = 333
+  assertEquals(42, %_CallFunction(o, 18, 24, f))
   assertSame(o, receiver)
 }
 
@@ -163,7 +233,8 @@ TestCall(true, function(x, y) {
 })
 
 TestCall(false, function() {
-  receiver = this; return arguments[0] + arguments[1]
+  receiver = this
+  return arguments[0] + arguments[1]
 })
 
 TestCall(false, Proxy.createFunction(handler, function(x, y) {
@@ -209,6 +280,12 @@ function TestCallThrow(callTrap) {
   assertThrows(function(){ ({x: f})["x"](11) }, "myexn")
   assertThrows(function(){ Function.prototype.call.call(f, {}, 2) }, "myexn")
   assertThrows(function(){ Function.prototype.apply.call(f, {}, [1]) }, "myexn")
+  assertThrows(function(){ %Call({}, f) }, "myexn")
+  assertThrows(function(){ %Call({}, 1, 2, f) }, "myexn")
+  assertThrows(function(){ %Apply({}, f, [], 3, 0) }, "myexn")
+  assertThrows(function(){ %Apply({}, f, [3, 4], 0, 1) }, "myexn")
+  assertThrows(function(){ %_CallFunction({}, f) }, "myexn")
+  assertThrows(function(){ %_CallFunction({}, 1, 2, f) }, "myexn")
 
   var f = CreateFrozen({}, callTrap)
   assertThrows(function(){ f(11) }, "myexn")
@@ -216,6 +293,12 @@ function TestCallThrow(callTrap) {
   assertThrows(function(){ ({x: f})["x"](11) }, "myexn")
   assertThrows(function(){ Function.prototype.call.call(f, {}, 2) }, "myexn")
   assertThrows(function(){ Function.prototype.apply.call(f, {}, [1]) }, "myexn")
+  assertThrows(function(){ %Call({}, f) }, "myexn")
+  assertThrows(function(){ %Call({}, 1, 2, f) }, "myexn")
+  assertThrows(function(){ %Apply({}, f, [], 3, 0) }, "myexn")
+  assertThrows(function(){ %Apply({}, f, [3, 4], 0, 1) }, "myexn")
+  assertThrows(function(){ %_CallFunction({}, f) }, "myexn")
+  assertThrows(function(){ %_CallFunction({}, 1, 2, f) }, "myexn")
 }
 
 TestCallThrow(function() { throw "myexn" })
@@ -520,3 +603,125 @@ TestAccessorCall(
   CreateFrozen({}, function() { receiver = this; return 42 }),
   CreateFrozen({}, function(x) { receiver = this; value = x })
 )
+
+
+
+// TODO(rossberg): Ultimately, I want to have the following test function
+// run through, but it currently fails on so many cases (some not even
+// involving proxies), that I leave that for later...
+/*
+function TestCalls() {
+  var handler = {
+    get: function(r, k) {
+      return k == "length" ? 2 : Function.prototype[k]
+    }
+  }
+  var bind = Function.prototype.bind
+  var o = {}
+
+  var traps = [
+    function(x, y) {
+      return {receiver: this, result: x + y, strict: false}
+    },
+    function(x, y) { "use strict";
+      return {receiver: this, result: x + y, strict: true}
+    },
+    function() {
+      var x = arguments[0], y = arguments[1]
+      return {receiver: this, result: x + y, strict: false}
+    },
+    Proxy.createFunction(handler, function(x, y) {
+      return {receiver: this, result: x + y, strict: false}
+    }),
+    Proxy.createFunction(handler, function() {
+      var x = arguments[0], y = arguments[1]
+      return {receiver: this, result: x + y, strict: false}
+    }),
+    Proxy.createFunction(handler, function(x, y) { "use strict"
+      return {receiver: this, result: x + y, strict: true}
+    }),
+    CreateFrozen(handler, function(x, y) {
+      return {receiver: this, result: x + y, strict: false}
+    }),
+    CreateFrozen(handler, function(x, y) { "use strict"
+      return {receiver: this, result: x + y, strict: true}
+    }),
+  ]
+  var creates = [
+    function(trap) { return trap },
+    function(trap) { return CreateFrozen({}, callTrap) },
+    function(trap) { return Proxy.createFunction(handler, callTrap) },
+    function(trap) {
+      return Proxy.createFunction(handler, CreateFrozen({}, callTrap))
+    },
+    function(trap) {
+      return Proxy.createFunction(handler, Proxy.createFunction(handler, callTrap))
+    },
+  ]
+  var binds = [
+    function(f, o, x, y) { return f },
+    function(f, o, x, y) { return bind.call(f, o) },
+    function(f, o, x, y) { return bind.call(f, o, x) },
+    function(f, o, x, y) { return bind.call(f, o, x, y) },
+    function(f, o, x, y) { return bind.call(f, o, x, y, 5) },
+    function(f, o, x, y) { return bind.call(bind.call(f, o), {}, x, y) },
+    function(f, o, x, y) { return bind.call(bind.call(f, o, x), {}, y) },
+    function(f, o, x, y) { return bind.call(bind.call(f, o, x, y), {}, 5) },
+  ]
+  var calls = [
+    function(f, x, y) { return f(x, y) },
+    function(f, x, y) { var g = f; return g(x, y) },
+    function(f, x, y) { with ({}) return f(x, y) },
+    function(f, x, y) { var g = f; with ({}) return g(x, y) },
+    function(f, x, y, o) { with (o) return f(x, y) },
+    function(f, x, y, o) { return f.call(o, x, y) },
+    function(f, x, y, o) { return f.apply(o, [x, y]) },
+    function(f, x, y, o) { return Function.prototype.call.call(f, o, x, y) },
+    function(f, x, y, o) { return Function.prototype.apply.call(f, o, [x, y]) },
+    function(f, x, y, o) { return %_CallFunction(o, x, y, f) },
+    function(f, x, y, o) { return %Call(o, x, y, f) },
+    function(f, x, y, o) { return %Apply(f, o, [null, x, y, null], 1, 2) },
+    function(f, x, y, o) { return %Apply(f, o, arguments, 2, 2) },
+    function(f, x, y, o) { if (typeof o == "object") return o.f(x, y) },
+    function(f, x, y, o) { if (typeof o == "object") return o["f"](x, y) },
+    function(f, x, y, o) { if (typeof o == "object") return (1, o).f(x, y) },
+    function(f, x, y, o) { if (typeof o == "object") return (1, o)["f"](x, y) },
+  ]
+  var receivers = [o, global_object, undefined, null, 2, "bla", true]
+  var expectedNonStricts = [o, global_object, global_object, global_object]
+
+  for (var t = 0; t < traps.length; ++t) {
+    for (var i = 0; i < creates.length; ++i) {
+      for (var j = 0; j < binds.length; ++j) {
+        for (var k = 0; k < calls.length; ++k) {
+          for (var m = 0; m < receivers.length; ++m) {
+            for (var n = 0; n < receivers.length; ++n) {
+              var bound = receivers[m]
+              var receiver = receivers[n]
+              var func = binds[j](creates[i](traps[t]), bound, 31, 11)
+              var expected = j > 0 ? bound : receiver
+              var expectedNonStrict = expectedNonStricts[j > 0 ? m : n]
+              o.f = func
+              global_object.f = func
+              var x = calls[k](func, 11, 31, receiver)
+              if (x !== undefined) {
+                assertEquals(42, x.result)
+                if (calls[k].length < 4)
+                  assertSame(x.strict ? undefined : global_object, x.receiver)
+                else if (x.strict)
+                  assertSame(expected, x.receiver)
+                else if (expectedNonStrict === undefined)
+                  assertSame(expected, x.receiver.valueOf())
+                else
+                  assertSame(expectedNonStrict, x.receiver)
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+TestCalls()
+*/