Implement function proxies (except for their use as constructors).
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 13 Sep 2011 11:42:57 +0000 (11:42 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 13 Sep 2011 11:42:57 +0000 (11:42 +0000)
Introduce new %Apply native.

Extend Execution::Call to optionally handle receiver rewriting (needed for %Apply).

Fix Function.prototype.bind for functions that have .apply modified.

R=kmillikin@chromium.org
BUG=v8:1543
TEST=

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

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

34 files changed:
src/arm/builtins-arm.cc
src/arm/code-stubs-arm.cc
src/arm/full-codegen-arm.cc
src/arm/macro-assembler-arm.h
src/array.js
src/builtins.h
src/execution.cc
src/execution.h
src/factory.cc
src/factory.h
src/heap.cc
src/heap.h
src/hydrogen.cc
src/ia32/builtins-ia32.cc
src/ia32/code-stubs-ia32.cc
src/json.js
src/macros.py
src/messages.js
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects-visiting.cc
src/objects.cc
src/objects.h
src/proxy.js
src/runtime.cc
src/runtime.h
src/runtime.js
src/string.js
src/v8natives.js
src/x64/builtins-x64.cc
src/x64/code-stubs-x64.cc
test/mjsunit/fuzz-natives.js
test/mjsunit/harmony/proxies.js

index a35380c..60d2081 100644 (file)
@@ -1230,16 +1230,17 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
   // 2. Get the function to call (passed as receiver) from the stack, check
   //    if it is a function.
   // r0: actual number of arguments
-  Label non_function;
+  Label slow, non_function;
   __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
   __ JumpIfSmi(r1, &non_function);
   __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
-  __ b(ne, &non_function);
+  __ b(ne, &slow);
 
   // 3a. Patch the first argument if necessary when calling a function.
   // r0: actual number of arguments
   // r1: function
   Label shift_arguments;
+  __ mov(r4, Operand(0, RelocInfo::NONE));  // indicate regular JS_FUNCTION
   { Label convert_to_object, use_global_receiver, patch_receiver;
     // Change context eagerly in case we need the global receiver.
     __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
@@ -1286,8 +1287,9 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ pop(r0);
     __ mov(r0, Operand(r0, ASR, kSmiTagSize));
     __ LeaveInternalFrame();
-    // Restore the function to r1.
+    // Restore the function to r1, and the flag to r4.
     __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
+    __ mov(r4, Operand(0, RelocInfo::NONE));
     __ jmp(&patch_receiver);
 
     // Use the global receiver object from the called function as the
@@ -1307,23 +1309,30 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ jmp(&shift_arguments);
   }
 
-  // 3b. Patch the first argument when calling a non-function.  The
+  // 3b. Check for function proxy.
+  __ bind(&slow);
+  __ mov(r4, Operand(1, RelocInfo::NONE));  // indicate function proxy
+  __ cmp(r2, Operand(JS_FUNCTION_PROXY_TYPE));
+  __ b(eq, &shift_arguments);
+  __ bind(&non_function);
+  __ mov(r4, Operand(2, RelocInfo::NONE));  // indicate non-function
+
+  // 3c. Patch the first argument when calling a non-function.  The
   //     CALL_NON_FUNCTION builtin expects the non-function callee as
   //     receiver, so overwrite the first argument which will ultimately
   //     become the receiver.
   // r0: actual number of arguments
   // r1: function
-  __ bind(&non_function);
+  // r4: call type (0: JS function, 1: function proxy, 2: non-function)
   __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
   __ str(r1, MemOperand(r2, -kPointerSize));
-  // Clear r1 to indicate a non-function being called.
-  __ mov(r1, Operand(0, RelocInfo::NONE));
 
   // 4. Shift arguments and return address one slot down on the stack
   //    (overwriting the original receiver).  Adjust argument count to make
   //    the original first argument the new receiver.
   // r0: actual number of arguments
   // r1: function
+  // r4: call type (0: JS function, 1: function proxy, 2: non-function)
   __ bind(&shift_arguments);
   { Label loop;
     // Calculate the copy start address (destination). Copy end address is sp.
@@ -1341,16 +1350,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ pop();
   }
 
-  // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
+  // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin,
+  //     or a function proxy via CALL_FUNCTION_PROXY.
   // r0: actual number of arguments
   // r1: function
-  { Label function;
-    __ tst(r1, r1);
-    __ b(ne, &function);
+  // r4: call type (0: JS function, 1: function proxy, 2: non-function)
+  { Label function, non_proxy;
+    __ tst(r4, r4);
+    __ b(eq, &function);
     // Expected number of arguments is 0 for CALL_NON_FUNCTION.
     __ mov(r2, Operand(0, RelocInfo::NONE));
-    __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
     __ SetCallKind(r5, CALL_AS_METHOD);
+    __ cmp(r4, Operand(1));
+    __ b(ne, &non_proxy);
+
+    __ push(r1);  // re-add proxy object as additional argument
+    __ add(r0, r0, Operand(1));
+    __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY);
+    __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+            RelocInfo::CODE_TARGET);
+
+    __ bind(&non_proxy);
+    __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
     __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
             RelocInfo::CODE_TARGET);
     __ bind(&function);
@@ -1393,7 +1414,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ push(r0);
   __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
 
-  // Check the stack for overflow. We are not trying need to catch
+  // Check the stack for overflow. We are not trying to catch
   // interruptions (e.g. debug break and preemption) here, so the "real stack
   // limit" is checked.
   Label okay;
@@ -1418,18 +1439,24 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ mov(r1, Operand(0, RelocInfo::NONE));  // initial index
   __ push(r1);
 
+  // Get the receiver.
+  __ ldr(r0, MemOperand(fp, kRecvOffset));
+
+  // Check that the function is a JS function (otherwise it must be a proxy).
+  Label push_receiver;
+  __ ldr(r1, MemOperand(fp, kFunctionOffset));
+  __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
+  __ b(ne, &push_receiver);
+
   // Change context eagerly to get the right global object if necessary.
-  __ ldr(r0, MemOperand(fp, kFunctionOffset));
-  __ ldr(cp, FieldMemOperand(r0, JSFunction::kContextOffset));
-  // Load the shared function info while the function is still in r0.
-  __ ldr(r1, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
+  __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+  // Load the shared function info while the function is still in r1.
+  __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
 
   // Compute the receiver.
-  Label call_to_object, use_global_receiver, push_receiver;
-  __ ldr(r0, MemOperand(fp, kRecvOffset));
-
   // Do not transform the receiver for strict mode functions.
-  __ ldr(r2, FieldMemOperand(r1, SharedFunctionInfo::kCompilerHintsOffset));
+  Label call_to_object, use_global_receiver;
+  __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kCompilerHintsOffset));
   __ tst(r2, Operand(1 << (SharedFunctionInfo::kStrictModeFunction +
                            kSmiTagSize)));
   __ b(ne, &push_receiver);
@@ -1504,9 +1531,12 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ b(ne, &loop);
 
   // Invoke the function.
+  Label call_proxy;
   ParameterCount actual(r0);
   __ mov(r0, Operand(r0, ASR, kSmiTagSize));
   __ ldr(r1, MemOperand(fp, kFunctionOffset));
+  __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
+  __ b(ne, &call_proxy);
   __ InvokeFunction(r1, actual, CALL_FUNCTION,
                     NullCallWrapper(), CALL_AS_METHOD);
 
@@ -1514,6 +1544,20 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ LeaveInternalFrame();
   __ add(sp, sp, Operand(3 * kPointerSize));
   __ Jump(lr);
+
+  // Invoke the function proxy.
+  __ bind(&call_proxy);
+  __ push(r1);  // add function proxy as last argument
+  __ add(r0, r0, Operand(1));
+  __ mov(r2, Operand(0, RelocInfo::NONE));
+  __ SetCallKind(r5, CALL_AS_METHOD);
+  __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY);
+  __ Call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+          RelocInfo::CODE_TARGET);
+
+  __ LeaveInternalFrame();
+  __ add(sp, sp, Operand(3 * kPointerSize));
+  __ Jump(lr);
 }
 
 
index 7fd2fe6..e65f6d9 100644 (file)
@@ -4713,7 +4713,7 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
 
 
 void CallFunctionStub::Generate(MacroAssembler* masm) {
-  Label slow;
+  Label slow, non_function;
 
   // The receiver might implicitly be the global object. This is
   // indicated by passing the hole as the receiver to the call
@@ -4739,7 +4739,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
 
   // Check that the function is really a JavaScript function.
   // r1: pushed function (to be verified)
-  __ JumpIfSmi(r1, &slow);
+  __ JumpIfSmi(r1, &non_function);
   // Get the map of the function object.
   __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
   __ b(ne, &slow);
@@ -4767,8 +4767,23 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
 
   // Slow-case: Non-function called.
   __ bind(&slow);
+  // Check for function proxy.
+  __ cmp(r2, Operand(JS_FUNCTION_PROXY_TYPE));
+  __ b(ne, &non_function);
+  __ push(r1);  // put proxy as additional argument
+  __ mov(r0, Operand(argc_ + 1, RelocInfo::NONE));
+  __ mov(r2, Operand(0, RelocInfo::NONE));
+  __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY);
+  __ SetCallKind(r5, CALL_AS_FUNCTION);
+  {
+    Handle<Code> adaptor =
+      masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
+    __ Jump(adaptor, RelocInfo::CODE_TARGET);
+  }
+
   // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
   // of the original receiver from the call site).
+  __ bind(&non_function);
   __ str(r1, MemOperand(sp, argc_ * kPointerSize));
   __ mov(r0, Operand(argc_));  // Setup the number of arguments.
   __ mov(r2, Operand(0, RelocInfo::NONE));
index 4d27a8d..ebfd200 100644 (file)
@@ -2514,7 +2514,7 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) {
                          &if_true, &if_false, &fall_through);
 
   __ JumpIfSmi(r0, if_false);
-  __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE);
+  __ CompareObjectType(r0, r1, r2, JS_FUNCTION_TYPE);
   PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
   Split(eq, if_true, if_false, fall_through);
 
index 9d66359..6084fde 100644 (file)
@@ -596,9 +596,7 @@ class MacroAssembler: public Assembler {
 
   // Compare instance type in a map.  map contains a valid map object whose
   // object type should be compared with the given type.  This both
-  // sets the flags and leaves the object type in the type_reg register.  It
-  // leaves the heap object in the heap_object register unless the heap_object
-  // register is the same register as type_reg.
+  // sets the flags and leaves the object type in the type_reg register.
   void CompareInstanceType(Register map,
                            Register type_reg,
                            InstanceType type);
index 32a370f..4dd23c8 100644 (file)
@@ -208,7 +208,7 @@ function ConvertToLocaleString(e) {
     // Call ToString if toLocaleString is not a function.
     // See issue 877615.
     var e_obj = ToObject(e);
-    if (IS_FUNCTION(e_obj.toLocaleString))
+    if (IS_SPEC_FUNCTION(e_obj.toLocaleString))
       return ToString(e_obj.toLocaleString());
     else
       return ToString(e);
@@ -730,7 +730,7 @@ function ArraySort(comparefn) {
   // In-place QuickSort algorithm.
   // For short (length <= 22) arrays, insertion sort is used for efficiency.
 
-  if (!IS_FUNCTION(comparefn)) {
+  if (!IS_SPEC_FUNCTION(comparefn)) {
     comparefn = function (x, y) {
       if (x === y) return 0;
       if (%_IsSmi(x) && %_IsSmi(y)) {
@@ -993,7 +993,7 @@ function ArrayFilter(f, receiver) {
                         ["Array.prototype.filter"]);
   }
 
-  if (!IS_FUNCTION(f)) {
+  if (!IS_SPEC_FUNCTION(f)) {
     throw MakeTypeError('called_non_callable', [ f ]);
   }
   if (IS_NULL_OR_UNDEFINED(receiver)) {
@@ -1022,7 +1022,7 @@ function ArrayForEach(f, receiver) {
                         ["Array.prototype.forEach"]);
   }
 
-  if (!IS_FUNCTION(f)) {
+  if (!IS_SPEC_FUNCTION(f)) {
     throw MakeTypeError('called_non_callable', [ f ]);
   }
   if (IS_NULL_OR_UNDEFINED(receiver)) {
@@ -1048,7 +1048,7 @@ function ArraySome(f, receiver) {
                         ["Array.prototype.some"]);
   }
 
-  if (!IS_FUNCTION(f)) {
+  if (!IS_SPEC_FUNCTION(f)) {
     throw MakeTypeError('called_non_callable', [ f ]);
   }
   if (IS_NULL_OR_UNDEFINED(receiver)) {
@@ -1073,7 +1073,7 @@ function ArrayEvery(f, receiver) {
                         ["Array.prototype.every"]);
   }
 
-  if (!IS_FUNCTION(f)) {
+  if (!IS_SPEC_FUNCTION(f)) {
     throw MakeTypeError('called_non_callable', [ f ]);
   }
   if (IS_NULL_OR_UNDEFINED(receiver)) {
@@ -1097,7 +1097,7 @@ function ArrayMap(f, receiver) {
                         ["Array.prototype.map"]);
   }
 
-  if (!IS_FUNCTION(f)) {
+  if (!IS_SPEC_FUNCTION(f)) {
     throw MakeTypeError('called_non_callable', [ f ]);
   }
   if (IS_NULL_OR_UNDEFINED(receiver)) {
@@ -1245,7 +1245,7 @@ function ArrayReduce(callback, current) {
                         ["Array.prototype.reduce"]);
   }
 
-  if (!IS_FUNCTION(callback)) {
+  if (!IS_SPEC_FUNCTION(callback)) {
     throw MakeTypeError('called_non_callable', [callback]);
   }
 
@@ -1281,7 +1281,7 @@ function ArrayReduceRight(callback, current) {
                         ["Array.prototype.reduceRight"]);
   }
 
-  if (!IS_FUNCTION(callback)) {
+  if (!IS_SPEC_FUNCTION(callback)) {
     throw MakeTypeError('called_non_callable', [callback]);
   }
   var i = ToUint32(this.length) - 1;
index f9a5a13..31090d3 100644 (file)
@@ -238,6 +238,8 @@ enum BuiltinExtraArguments {
   V(FILTER_KEY, 1)                       \
   V(CALL_NON_FUNCTION, 0)                \
   V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \
+  V(CALL_FUNCTION_PROXY, 1)                \
+  V(CALL_FUNCTION_PROXY_AS_CONSTRUCTOR, 1) \
   V(TO_OBJECT, 0)                        \
   V(TO_NUMBER, 0)                        \
   V(TO_STRING, 0)                        \
index bdbdca8..cdea005 100644 (file)
@@ -149,12 +149,28 @@ Handle<Object> Execution::Call(Handle<Object> callable,
                                Handle<Object> receiver,
                                int argc,
                                Object*** args,
-                               bool* pending_exception) {
+                               bool* pending_exception,
+                               bool convert_receiver) {
   if (!callable->IsJSFunction()) {
     callable = TryGetFunctionDelegate(callable, pending_exception);
     if (*pending_exception) return callable;
   }
   Handle<JSFunction> func = Handle<JSFunction>::cast(callable);
+
+  // In non-strict mode, convert receiver.
+  if (convert_receiver && !receiver->IsJSReceiver() &&
+      !func->shared()->native() && !func->shared()->strict_mode()) {
+    if (receiver->IsUndefined() || receiver->IsNull()) {
+      // Careful, func->context()->global()->global_receiver() gives
+      // the JSBuiltinsObject if func is a builtin. Not what we want here.
+      receiver =
+          Handle<Object>(func->GetIsolate()->global()->global_receiver());
+    } else {
+      receiver = ToObject(receiver, pending_exception);
+    }
+    if (*pending_exception) return callable;
+  }
+
   return Invoke(false, func, receiver, argc, args, pending_exception);
 }
 
@@ -210,10 +226,17 @@ Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) {
   // If you return a function from here, it will be called when an
   // attempt is made to call the given object as a function.
 
+  // If object is a function proxies, get its handler. Iterate if necessary.
+  Object* fun = *object;
+  while (fun->IsJSFunctionProxy()) {
+    fun = JSFunctionProxy::cast(fun)->call_trap();
+  }
+  if (fun->IsJSFunction()) return Handle<Object>(fun);
+
   // Objects created through the API can have an instance-call handler
   // that should be used when calling the object as a function.
-  if (object->IsHeapObject() &&
-      HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+  if (fun->IsHeapObject() &&
+      HeapObject::cast(fun)->map()->has_instance_call_handler()) {
     return Handle<JSFunction>(
         isolate->global_context()->call_as_function_delegate());
   }
@@ -227,10 +250,17 @@ Handle<Object> Execution::TryGetFunctionDelegate(Handle<Object> object,
   ASSERT(!object->IsJSFunction());
   Isolate* isolate = Isolate::Current();
 
+  // If object is a function proxies, get its handler. Iterate if necessary.
+  Object* fun = *object;
+  while (fun->IsJSFunctionProxy()) {
+    fun = JSFunctionProxy::cast(fun)->call_trap();
+  }
+  if (fun->IsJSFunction()) return Handle<Object>(fun);
+
   // Objects created through the API can have an instance-call handler
   // that should be used when calling the object as a function.
-  if (object->IsHeapObject() &&
-      HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+  if (fun->IsHeapObject() &&
+      HeapObject::cast(fun)->map()->has_instance_call_handler()) {
     return Handle<JSFunction>(
         isolate->global_context()->call_as_function_delegate());
   }
@@ -253,10 +283,17 @@ Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) {
   // If you return a function from here, it will be called when an
   // attempt is made to call the given object as a constructor.
 
+  // If object is a function proxies, get its handler. Iterate if necessary.
+  Object* fun = *object;
+  while (fun->IsJSFunctionProxy()) {
+    fun = JSFunctionProxy::cast(fun)->call_trap();
+  }
+  if (fun->IsJSFunction()) return Handle<Object>(fun);
+
   // Objects created through the API can have an instance-call handler
   // that should be used when calling the object as a function.
-  if (object->IsHeapObject() &&
-      HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+  if (fun->IsHeapObject() &&
+      HeapObject::cast(fun)->map()->has_instance_call_handler()) {
     return Handle<JSFunction>(
         isolate->global_context()->call_as_constructor_delegate());
   }
@@ -274,10 +311,17 @@ Handle<Object> Execution::TryGetConstructorDelegate(
   // If you return a function from here, it will be called when an
   // attempt is made to call the given object as a constructor.
 
+  // If object is a function proxies, get its handler. Iterate if necessary.
+  Object* fun = *object;
+  while (fun->IsJSFunctionProxy()) {
+    fun = JSFunctionProxy::cast(fun)->call_trap();
+  }
+  if (fun->IsJSFunction()) return Handle<Object>(fun);
+
   // Objects created through the API can have an instance-call handler
   // that should be used when calling the object as a function.
-  if (object->IsHeapObject() &&
-      HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+  if (fun->IsHeapObject() &&
+      HeapObject::cast(fun)->map()->has_instance_call_handler()) {
     return Handle<JSFunction>(
         isolate->global_context()->call_as_constructor_delegate());
   }
@@ -553,7 +597,7 @@ Handle<Object> Execution::ToDetailString(Handle<Object> obj, bool* exc) {
 
 
 Handle<Object> Execution::ToObject(Handle<Object> obj, bool* exc) {
-  if (obj->IsJSObject()) return obj;
+  if (obj->IsSpecObject()) return obj;
   RETURN_NATIVE_CALL(to_object, 1, { obj.location() }, exc);
 }
 
index bb5f804..5cd7141 100644 (file)
@@ -53,11 +53,16 @@ class Execution : public AllStatic {
   // *pending_exception tells whether the invoke resulted in
   // a pending exception.
   //
+  // When convert_receiver is set, and the receiver is not an object,
+  // and the function called is not in strict mode, receiver is converted to
+  // an object.
+  //
   static Handle<Object> Call(Handle<Object> callable,
                              Handle<Object> receiver,
                              int argc,
                              Object*** args,
-                             bool* pending_exception);
+                             bool* pending_exception,
+                             bool convert_receiver = false);
 
   // Construct object from function, the caller supplies an array of
   // arguments. Arguments are Object* type. After function returns,
index ad6d34b..9728926 100644 (file)
@@ -922,10 +922,19 @@ Handle<JSProxy> Factory::NewJSProxy(Handle<Object> handler,
 }
 
 
-void Factory::BecomeJSObject(Handle<JSProxy> object) {
+void Factory::BecomeJSObject(Handle<JSReceiver> object) {
   CALL_HEAP_FUNCTION_VOID(
       isolate(),
-      isolate()->heap()->ReinitializeJSProxyAsJSObject(*object));
+      isolate()->heap()->ReinitializeJSReceiver(
+          *object, JS_OBJECT_TYPE, JSObject::kHeaderSize));
+}
+
+
+void Factory::BecomeJSFunction(Handle<JSReceiver> object) {
+  CALL_HEAP_FUNCTION_VOID(
+      isolate(),
+      isolate()->heap()->ReinitializeJSReceiver(
+          *object, JS_FUNCTION_TYPE, JSFunction::kSize));
 }
 
 
index c748d46..71ae750 100644 (file)
@@ -260,8 +260,9 @@ class Factory {
 
   Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
 
-  // Change the type of the argument into a regular JS object and reinitialize.
-  void BecomeJSObject(Handle<JSProxy> object);
+  // Change the type of the argument into a JS object/function and reinitialize.
+  void BecomeJSObject(Handle<JSReceiver> object);
+  void BecomeJSFunction(Handle<JSReceiver> object);
 
   Handle<JSFunction> NewFunction(Handle<String> name,
                                  Handle<Object> prototype);
index 3efcdf1..d018593 100644 (file)
@@ -3415,11 +3415,36 @@ MaybeObject* Heap::AllocateJSProxy(Object* handler, Object* prototype) {
   map->set_prototype(prototype);
 
   // Allocate the proxy object.
-  Object* result;
+  JSProxy* result;
   MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
-  if (!maybe_result->ToObject(&result)) return maybe_result;
-  JSProxy::cast(result)->set_handler(handler);
-  JSProxy::cast(result)->set_padding(Smi::FromInt(0));
+  if (!maybe_result->To<JSProxy>(&result)) return maybe_result;
+  result->InitializeBody(map->instance_size(), Smi::FromInt(0));
+  result->set_handler(handler);
+  return result;
+}
+
+
+MaybeObject* Heap::AllocateJSFunctionProxy(Object* handler,
+                                           Object* call_trap,
+                                           Object* construct_trap,
+                                           Object* prototype) {
+  // Allocate map.
+  // TODO(rossberg): Once we optimize proxies, think about a scheme to share
+  // maps. Will probably depend on the identity of the handler object, too.
+  Map* map;
+  MaybeObject* maybe_map_obj =
+      AllocateMap(JS_FUNCTION_PROXY_TYPE, JSFunctionProxy::kSize);
+  if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
+  map->set_prototype(prototype);
+
+  // Allocate the proxy object.
+  JSFunctionProxy* result;
+  MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
+  if (!maybe_result->To<JSFunctionProxy>(&result)) return maybe_result;
+  result->InitializeBody(map->instance_size(), Smi::FromInt(0));
+  result->set_handler(handler);
+  result->set_call_trap(call_trap);
+  result->set_construct_trap(construct_trap);
   return result;
 }
 
@@ -3564,16 +3589,19 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) {
 }
 
 
-MaybeObject* Heap::ReinitializeJSProxyAsJSObject(JSProxy* object) {
+MaybeObject* Heap::ReinitializeJSReceiver(
+    JSReceiver* object, InstanceType type, int size) {
+  ASSERT(type >= FIRST_JS_RECEIVER_TYPE);
+
   // Allocate fresh map.
   // TODO(rossberg): Once we optimize proxies, cache these maps.
   Map* map;
-  MaybeObject* maybe_map_obj =
-      AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+  MaybeObject* maybe_map_obj = AllocateMap(type, size);
   if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
 
-  // Check that the receiver has the same size as a fresh object.
-  ASSERT(map->instance_size() == object->map()->instance_size());
+  // Check that the receiver has at least the size of the fresh object.
+  int size_difference = object->map()->instance_size() - map->instance_size();
+  ASSERT(size_difference >= 0);
 
   map->set_prototype(object->map()->prototype());
 
@@ -3590,6 +3618,28 @@ MaybeObject* Heap::ReinitializeJSProxyAsJSObject(JSProxy* object) {
   // Reinitialize the object from the constructor map.
   InitializeJSObjectFromMap(JSObject::cast(object),
                             FixedArray::cast(properties), map);
+
+  // Functions require some minimal initialization.
+  if (type == JS_FUNCTION_TYPE) {
+    String* name;
+    MaybeObject* maybe_name = LookupAsciiSymbol("<freezing call trap>");
+    if (!maybe_name->To<String>(&name)) return maybe_name;
+    SharedFunctionInfo* shared;
+    MaybeObject* maybe_shared = AllocateSharedFunctionInfo(name);
+    if (!maybe_shared->To<SharedFunctionInfo>(&shared)) return maybe_shared;
+    JSFunction* func;
+    MaybeObject* maybe_func =
+        InitializeFunction(JSFunction::cast(object), shared, the_hole_value());
+    if (!maybe_func->To<JSFunction>(&func)) return maybe_func;
+    func->set_context(isolate()->context()->global_context());
+  }
+
+  // Put in filler if the new object is smaller than the old.
+  if (size_difference > 0) {
+    CreateFillerObjectAt(
+        object->address() + map->instance_size(), size_difference);
+  }
+
   return object;
 }
 
index cc689df..d81ff6c 100644 (file)
@@ -440,17 +440,25 @@ class Heap {
   // Please note this does not perform a garbage collection.
   MUST_USE_RESULT MaybeObject* AllocateFunctionPrototype(JSFunction* function);
 
-  // Allocates a Harmony Proxy.
+  // Allocates a Harmony proxy or function proxy.
   // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
   // failed.
   // Please note this does not perform a garbage collection.
   MUST_USE_RESULT MaybeObject* AllocateJSProxy(Object* handler,
                                                Object* prototype);
 
-  // Reinitialize a JSProxy into an (empty) JSObject.  The receiver
-  // must have the same size as an empty object.  The object is reinitialized
-  // and behaves as an object that has been freshly allocated.
-  MUST_USE_RESULT MaybeObject* ReinitializeJSProxyAsJSObject(JSProxy* object);
+  MUST_USE_RESULT MaybeObject* AllocateJSFunctionProxy(Object* handler,
+                                                       Object* call_trap,
+                                                       Object* construct_trap,
+                                                       Object* prototype);
+
+  // Reinitialize a JSReceiver into an (empty) JS object of respective type and
+  // size, but keeping the original prototype.  The receiver must have at least
+  // the size of the new object.  The object is reinitialized and behaves as an
+  // object that has been freshly allocated.
+  MUST_USE_RESULT MaybeObject* ReinitializeJSReceiver(JSReceiver* object,
+                                                      InstanceType type,
+                                                      int size);
 
   // Reinitialize an JSGlobalProxy based on a constructor.  The object
   // must have the same size as objects allocated using the
index a453651..2f4802a 100644 (file)
@@ -5915,7 +5915,9 @@ void HGraphBuilder::GenerateIsFunction(CallRuntime* call) {
   CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
   HValue* value = Pop();
   HHasInstanceTypeAndBranch* result =
-      new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE);
+      new(zone()) HHasInstanceTypeAndBranch(value,
+                                            JS_FUNCTION_TYPE,
+                                            JS_FUNCTION_PROXY_TYPE);
   return ast_context()->ReturnControl(result, call->id());
 }
 
index 845a073..310ea3d 100644 (file)
@@ -590,16 +590,17 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
 
   // 2. Get the function to call (passed as receiver) from the stack, check
   //    if it is a function.
-  Label non_function;
+  Label slow, non_function;
   // 1 ~ return address.
   __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
   __ JumpIfSmi(edi, &non_function);
   __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
-  __ j(not_equal, &non_function);
+  __ j(not_equal, &slow);
 
 
   // 3a. Patch the first argument if necessary when calling a function.
   Label shift_arguments;
+  __ Set(edx, Immediate(0));  // indicate regular JS_FUNCTION
   { Label convert_to_object, use_global_receiver, patch_receiver;
     // Change context eagerly in case we need the global receiver.
     __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
@@ -637,6 +638,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ push(ebx);
     __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
     __ mov(ebx, eax);
+    __ Set(edx, Immediate(0));  // restore
 
     __ pop(eax);
     __ SmiUntag(eax);
@@ -661,14 +663,19 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ jmp(&shift_arguments);
   }
 
-  // 3b. Patch the first argument when calling a non-function.  The
+  // 3b. Check for function proxy.
+  __ bind(&slow);
+  __ Set(edx, Immediate(1));  // indicate function proxy
+  __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
+  __ j(equal, &shift_arguments);
+  __ bind(&non_function);
+  __ Set(edx, Immediate(2));  // indicate non-function
+
+  // 3c. Patch the first argument when calling a non-function.  The
   //     CALL_NON_FUNCTION builtin expects the non-function callee as
   //     receiver, so overwrite the first argument which will ultimately
   //     become the receiver.
-  __ bind(&non_function);
   __ mov(Operand(esp, eax, times_4, 0), edi);
-  // Clear edi to indicate a non-function being called.
-  __ Set(edi, Immediate(0));
 
   // 4. Shift arguments and return address one slot down on the stack
   //    (overwriting the original receiver).  Adjust argument count to make
@@ -685,13 +692,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ dec(eax);  // One fewer argument (first argument is new receiver).
   }
 
-  // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
-  { Label function;
-    __ test(edi, Operand(edi));
-    __ j(not_zero, &function);
+  // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin,
+  //     or a function proxy via CALL_FUNCTION_PROXY.
+  { Label function, non_proxy;
+    __ test(edx, Operand(edx));
+    __ j(zero, &function);
     __ Set(ebx, Immediate(0));
-    __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
     __ SetCallKind(ecx, CALL_AS_METHOD);
+    __ cmp(Operand(edx), Immediate(1));
+    __ j(not_equal, &non_proxy);
+
+    __ pop(edx);   // return address
+    __ push(edi);  // re-add proxy object as additional argument
+    __ push(edx);
+    __ inc(eax);
+    __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
+    __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+           RelocInfo::CODE_TARGET);
+
+    __ bind(&non_proxy);
+    __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
     __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
            RelocInfo::CODE_TARGET);
     __ bind(&function);
@@ -717,13 +737,17 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
 
 
 void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
+  static const int kArgumentsOffset = 2 * kPointerSize;
+  static const int kReceiverOffset = 3 * kPointerSize;
+  static const int kFunctionOffset = 4 * kPointerSize;
+
   __ EnterInternalFrame();
 
-  __ push(Operand(ebp, 4 * kPointerSize));  // push this
-  __ push(Operand(ebp, 2 * kPointerSize));  // push arguments
+  __ push(Operand(ebp, kFunctionOffset));  // push this
+  __ push(Operand(ebp, kArgumentsOffset));  // push arguments
   __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
 
-  // Check the stack for overflow. We are not trying need to catch
+  // Check the stack for overflow. We are not trying to catch
   // interruptions (e.g. debug break and preemption) here, so the "real stack
   // limit" is checked.
   Label okay;
@@ -756,16 +780,21 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ push(eax);  // limit
   __ push(Immediate(0));  // index
 
-  // Change context eagerly to get the right global object if
-  // necessary.
-  __ mov(edi, Operand(ebp, 4 * kPointerSize));
+  // Get the receiver.
+  __ mov(ebx, Operand(ebp, kReceiverOffset));
+
+  // Check that the function is a JS function (otherwise it must be a proxy).
+  Label push_receiver;
+  __ mov(edi, Operand(ebp, kFunctionOffset));
+  __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+  __ j(not_equal, &push_receiver);
+
+  // Change context eagerly to get the right global object if necessary.
   __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
 
   // Compute the receiver.
-  Label call_to_object, use_global_receiver, push_receiver;
-  __ mov(ebx, Operand(ebp, 3 * kPointerSize));
-
   // Do not transform the receiver for strict mode functions.
+  Label call_to_object, use_global_receiver;
   __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
   __ test_b(FieldOperand(ecx, SharedFunctionInfo::kStrictModeByteOffset),
             1 << SharedFunctionInfo::kStrictModeBitWithinByte);
@@ -814,7 +843,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ mov(eax, Operand(ebp, kIndexOffset));
   __ jmp(&entry);
   __ bind(&loop);
-  __ mov(edx, Operand(ebp, 2 * kPointerSize));  // load arguments
+  __ mov(edx, Operand(ebp, kArgumentsOffset));  // load arguments
 
   // Use inline caching to speed up access to arguments.
   Handle<Code> ic = masm->isolate()->builtins()->KeyedLoadIC_Initialize();
@@ -837,14 +866,30 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ j(not_equal, &loop);
 
   // Invoke the function.
+  Label call_proxy;
   ParameterCount actual(eax);
   __ SmiUntag(eax);
-  __ mov(edi, Operand(ebp, 4 * kPointerSize));
+  __ mov(edi, Operand(ebp, kFunctionOffset));
+  __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+  __ j(not_equal, &call_proxy);
   __ InvokeFunction(edi, actual, CALL_FUNCTION,
                     NullCallWrapper(), CALL_AS_METHOD);
 
   __ LeaveInternalFrame();
   __ ret(3 * kPointerSize);  // remove this, receiver, and arguments
+
+  // Invoke the function proxy.
+  __ bind(&call_proxy);
+  __ push(edi);  // add function proxy as last argument
+  __ inc(eax);
+  __ Set(ebx, Immediate(0));
+  __ SetCallKind(ecx, CALL_AS_METHOD);
+  __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
+  __ call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+          RelocInfo::CODE_TARGET);
+
+  __ LeaveInternalFrame();
+  __ ret(3 * kPointerSize);  // remove this, receiver, and arguments
 }
 
 
index 76545ef..1009aaf 100644 (file)
@@ -4199,7 +4199,7 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
 
 
 void CallFunctionStub::Generate(MacroAssembler* masm) {
-  Label slow;
+  Label slow, non_function;
 
   // The receiver might implicitly be the global object. This is
   // indicated by passing the hole as the receiver to the call
@@ -4224,7 +4224,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
   __ mov(edi, Operand(esp, (argc_ + 2) * kPointerSize));
 
   // Check that the function really is a JavaScript function.
-  __ JumpIfSmi(edi, &slow);
+  __ JumpIfSmi(edi, &non_function);
   // Goto slow case if we do not have a function.
   __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
   __ j(not_equal, &slow);
@@ -4251,15 +4251,32 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
 
   // Slow-case: Non-function called.
   __ bind(&slow);
+  // Check for function proxy.
+  __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
+  __ j(not_equal, &non_function);
+  __ pop(ecx);
+  __ push(edi);  // put proxy as additional argument under return address
+  __ push(ecx);
+  __ Set(eax, Immediate(argc_ + 1));
+  __ Set(ebx, Immediate(0));
+  __ SetCallKind(ecx, CALL_AS_FUNCTION);
+  __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
+  {
+    Handle<Code> adaptor =
+      masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
+    __ jmp(adaptor, RelocInfo::CODE_TARGET);
+  }
+
   // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
   // of the original receiver from the call site).
+  __ bind(&non_function);
   __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
   __ Set(eax, Immediate(argc_));
   __ Set(ebx, Immediate(0));
+  __ SetCallKind(ecx, CALL_AS_METHOD);
   __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
   Handle<Code> adaptor =
       masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
-  __ SetCallKind(ecx, CALL_AS_METHOD);
   __ jmp(adaptor, RelocInfo::CODE_TARGET);
 }
 
index a491bcc..deba126 100644 (file)
@@ -54,7 +54,7 @@ function Revive(holder, name, reviver) {
 
 function JSONParse(text, reviver) {
   var unfiltered = %ParseJson(TO_STRING_INLINE(text));
-  if (IS_FUNCTION(reviver)) {
+  if (IS_SPEC_FUNCTION(reviver)) {
     return Revive({'': unfiltered}, '', reviver);
   } else {
     return unfiltered;
@@ -143,11 +143,11 @@ function JSONSerialize(key, holder, replacer, stack, indent, gap) {
   var value = holder[key];
   if (IS_SPEC_OBJECT(value)) {
     var toJSON = value.toJSON;
-    if (IS_FUNCTION(toJSON)) {
+    if (IS_SPEC_FUNCTION(toJSON)) {
       value = %_CallFunction(value, key, toJSON);
     }
   }
-  if (IS_FUNCTION(replacer)) {
+  if (IS_SPEC_FUNCTION(replacer)) {
     value = %_CallFunction(holder, key, value, replacer);
   }
   if (IS_STRING(value)) {
@@ -273,7 +273,7 @@ function BasicSerializeObject(value, stack, builder) {
 function BasicJSONSerialize(key, value, stack, builder) {
   if (IS_SPEC_OBJECT(value)) {
     var toJSON = value.toJSON;
-    if (IS_FUNCTION(toJSON)) {
+    if (IS_SPEC_FUNCTION(toJSON)) {
       value = %_CallFunction(value, ToString(key), toJSON);
     }
   }
index e3d1e03..7a493ca 100644 (file)
@@ -116,9 +116,17 @@ macro FLOOR(arg)                = $floor(arg);
 
 # Macro for ECMAScript 5 queries of the type:
 # "Type(O) is object."
-# This is the same as being either a function or an object in V8 terminology.
+# This is the same as being either a function or an object in V8 terminology
+# (including proxies).
 # In addition, an undetectable object is also included by this.
-macro IS_SPEC_OBJECT(arg) = (%_IsSpecObject(arg));
+macro IS_SPEC_OBJECT(arg)   = (%_IsSpecObject(arg));
+
+# Macro for ECMAScript 5 queries of the type:
+# "IsCallable(O)"
+# We assume here that this is the same as being either a function or a function
+# proxy. That ignores host objects with [[Call]] methods, but in most situations
+# we cannot handle those anyway.
+macro IS_SPEC_FUNCTION(arg) = (%_ClassOf(arg) === 'Function');
 
 # Inline macros. Use %IS_VAR to make sure arg is evaluated only once.
 macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg));
index 4adcb30..a9993af 100644 (file)
@@ -185,6 +185,7 @@ function FormatMessage(message) {
       "define_disallowed",            ["Cannot define property:", "%0", ", object is not extensible."],
       "non_extensible_proto",         ["%0", " is not extensible"],
       "handler_non_object",           ["Proxy.", "%0", " called with non-object as handler"],
+      "trap_function_expected",       ["Proxy.", "%0", " called with non-function for ", "%1", " trap"],
       "handler_trap_missing",         ["Proxy handler ", "%0", " has no '", "%1", "' trap"],
       "handler_trap_must_be_callable", ["Proxy handler ", "%0", " has non-callable '", "%1", "' trap"],
       "handler_returned_false",       ["Proxy handler ", "%0", " returned false for '", "%1", "' trap"],
index a3dc193..8de7162 100644 (file)
@@ -164,6 +164,9 @@ void HeapObject::HeapObjectVerify() {
     case JS_PROXY_TYPE:
       JSProxy::cast(this)->JSProxyVerify();
       break;
+    case JS_FUNCTION_PROXY_TYPE:
+      JSFunctionProxy::cast(this)->JSFunctionProxyVerify();
+      break;
     case FOREIGN_TYPE:
       Foreign::cast(this)->ForeignVerify();
       break;
@@ -536,6 +539,15 @@ void JSProxy::JSProxyVerify() {
   VerifyPointer(handler());
 }
 
+
+void JSFunctionProxy::JSFunctionProxyVerify() {
+  ASSERT(IsJSFunctionProxy());
+  JSProxyVerify();
+  VerifyPointer(call_trap());
+  VerifyPointer(construct_trap());
+}
+
+
 void Foreign::ForeignVerify() {
   ASSERT(IsForeign());
 }
index 2987b2c..ba9aae2 100644 (file)
@@ -3920,7 +3920,16 @@ void JSBuiltinsObject::set_javascript_builtin_code(Builtins::JavaScript id,
 
 
 ACCESSORS(JSProxy, handler, Object, kHandlerOffset)
-ACCESSORS(JSProxy, padding, Object, kPaddingOffset)
+ACCESSORS(JSFunctionProxy, call_trap, Object, kCallTrapOffset)
+ACCESSORS(JSFunctionProxy, construct_trap, Object, kConstructTrapOffset)
+
+
+void JSProxy::InitializeBody(int object_size, Object* value) {
+  ASSERT(!value->IsHeapObject() || !GetHeap()->InNewSpace(value));
+  for (int offset = kHeaderSize; offset < object_size; offset += kPointerSize) {
+    WRITE_FIELD(this, offset, value);
+  }
+}
 
 
 ACCESSORS(JSWeakMap, table, ObjectHashTable, kTableOffset)
index 3573572..0398572 100644 (file)
@@ -151,6 +151,9 @@ void HeapObject::HeapObjectPrint(FILE* out) {
     case JS_PROXY_TYPE:
       JSProxy::cast(this)->JSProxyPrint(out);
       break;
+    case JS_FUNCTION_PROXY_TYPE:
+      JSFunctionProxy::cast(this)->JSFunctionProxyPrint(out);
+      break;
     case JS_WEAK_MAP_TYPE:
       JSWeakMap::cast(this)->JSWeakMapPrint(out);
       break;
@@ -588,6 +591,19 @@ void JSProxy::JSProxyPrint(FILE* out) {
 }
 
 
+void JSFunctionProxy::JSFunctionProxyPrint(FILE* out) {
+  HeapObject::PrintHeader(out, "JSFunctionProxy");
+  PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
+  PrintF(out, " - handler = ");
+  handler()->Print(out);
+  PrintF(out, " - call_trap = ");
+  call_trap()->Print(out);
+  PrintF(out, " - construct_trap = ");
+  construct_trap()->Print(out);
+  PrintF(out, "\n");
+}
+
+
 void JSWeakMap::JSWeakMapPrint(FILE* out) {
   HeapObject::PrintHeader(out, "JSWeakMap");
   PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
index bde9e83..0aa21dd 100644 (file)
@@ -105,6 +105,11 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
                                  kVisitStructGeneric,
                                  JSProxy::kSize);
 
+    case JS_FUNCTION_PROXY_TYPE:
+      return GetVisitorIdForSize(kVisitStruct,
+                                 kVisitStructGeneric,
+                                 JSFunctionProxy::kSize);
+
     case FOREIGN_TYPE:
       return GetVisitorIdForSize(kVisitDataObject,
                                  kVisitDataObjectGeneric,
index ee997b2..65282c1 100644 (file)
@@ -84,7 +84,7 @@ MaybeObject* Object::ToObject(Context* global_context) {
 
 
 MaybeObject* Object::ToObject() {
-  if (IsJSObject()) {
+  if (IsJSReceiver()) {
     return this;
   } else if (IsNumber()) {
     Isolate* isolate = Isolate::Current();
@@ -627,6 +627,7 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
         } else if (heap_object->IsBoolean()) {
           holder = global_context->boolean_function()->instance_prototype();
         } else if (heap_object->IsJSProxy()) {
+          // TODO(rossberg): do something
           return heap->undefined_value();  // For now...
         } else {
           // Undefined and null have no indexed properties.
@@ -1173,6 +1174,12 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
       HeapNumber::cast(this)->HeapNumberPrint(accumulator);
       accumulator->Put('>');
       break;
+    case JS_PROXY_TYPE:
+      accumulator->Add("<JSProxy>");
+      break;
+    case JS_FUNCTION_PROXY_TYPE:
+      accumulator->Add("<JSFunctionProxy>");
+      break;
     case FOREIGN_TYPE:
       accumulator->Add("<Foreign>");
       break;
@@ -1251,6 +1258,9 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
     case JS_PROXY_TYPE:
       JSProxy::BodyDescriptor::IterateBody(this, v);
       break;
+    case JS_FUNCTION_PROXY_TYPE:
+      JSFunctionProxy::BodyDescriptor::IterateBody(this, v);
+      break;
     case FOREIGN_TYPE:
       reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v);
       break;
@@ -2337,9 +2347,13 @@ void JSProxy::Fix() {
   HandleScope scope(isolate);
   Handle<JSProxy> self(this);
 
-  isolate->factory()->BecomeJSObject(self);
+  if (IsJSFunctionProxy()) {
+    isolate->factory()->BecomeJSFunction(self);
+    // Code will be set on the JavaScript side.
+  } else {
+    isolate->factory()->BecomeJSObject(self);
+  }
   ASSERT(self->IsJSObject());
-  // TODO(rossberg): recognize function proxies.
 }
 
 
index 1f93a4c..16fa2ec 100644 (file)
@@ -4740,7 +4740,7 @@ class SharedFunctionInfo: public HeapObject {
   DECL_BOOLEAN_ACCESSORS(has_duplicate_parameters)
 
   // Indicates whether the function is a native function.
-  // These needs special threatment in .call and .apply since
+  // These needs special treatment in .call and .apply since
   // null passed as the receiver should not be translated to the
   // global object.
   DECL_BOOLEAN_ACCESSORS(native)
@@ -5007,7 +5007,7 @@ class JSFunction: public JSObject {
   // [prototype_or_initial_map]:
   DECL_ACCESSORS(prototype_or_initial_map, Object)
 
-  // [shared_function_info]: The information about the function that
+  // [shared]: The information about the function that
   // can be shared by instances.
   DECL_ACCESSORS(shared, SharedFunctionInfo)
 
@@ -6724,9 +6724,6 @@ class JSProxy: public JSReceiver {
   // [handler]: The handler property.
   DECL_ACCESSORS(handler, Object)
 
-  // [padding]: The padding slot (unused, see below).
-  DECL_ACCESSORS(padding, Object)
-
   // Casting.
   static inline JSProxy* cast(Object* obj);
 
@@ -6750,6 +6747,9 @@ class JSProxy: public JSReceiver {
   // Turn this into an (empty) JSObject.
   void Fix();
 
+  // Initializes the body after the handler slot.
+  inline void InitializeBody(int object_size, Object* value);
+
   // Dispatched behavior.
 #ifdef OBJECT_PRINT
   inline void JSProxyPrint() {
@@ -6766,9 +6766,11 @@ class JSProxy: public JSReceiver {
   // upon freeze.
   static const int kHandlerOffset = HeapObject::kHeaderSize;
   static const int kPaddingOffset = kHandlerOffset + kPointerSize;
-  static const int kSize = kPaddingOffset + kPointerSize;
+  static const int kSize = JSObject::kHeaderSize;
+  static const int kHeaderSize = kPaddingOffset;
+  static const int kPaddingSize = kSize - kPaddingOffset;
 
-  STATIC_CHECK(kSize == JSObject::kHeaderSize);
+  STATIC_CHECK(kPaddingSize >= 0);
 
   typedef FixedBodyDescriptor<kHandlerOffset,
                               kHandlerOffset + kPointerSize,
@@ -6779,12 +6781,41 @@ class JSProxy: public JSReceiver {
 };
 
 
-// TODO(rossberg): Only a stub for now.
 class JSFunctionProxy: public JSProxy {
  public:
+  // [call_trap]: The call trap.
+  DECL_ACCESSORS(call_trap, Object)
+
+  // [construct_trap]: The construct trap.
+  DECL_ACCESSORS(construct_trap, Object)
+
   // Casting.
   static inline JSFunctionProxy* cast(Object* obj);
 
+  // Dispatched behavior.
+#ifdef OBJECT_PRINT
+  inline void JSFunctionProxyPrint() {
+    JSFunctionProxyPrint(stdout);
+  }
+  void JSFunctionProxyPrint(FILE* out);
+#endif
+#ifdef DEBUG
+  void JSFunctionProxyVerify();
+#endif
+
+  // Layout description.
+  static const int kCallTrapOffset = kHandlerOffset + kPointerSize;
+  static const int kConstructTrapOffset = kCallTrapOffset + kPointerSize;
+  static const int kPaddingOffset = kConstructTrapOffset + kPointerSize;
+  static const int kSize = JSFunction::kSize;
+  static const int kPaddingSize = kSize - kPaddingOffset;
+
+  STATIC_CHECK(kPaddingSize >= 0);
+
+  typedef FixedBodyDescriptor<kHandlerOffset,
+                              kConstructTrapOffset + kPointerSize,
+                              kSize> BodyDescriptor;
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(JSFunctionProxy);
 };
index 2839159..4e44cd4 100644 (file)
@@ -29,36 +29,6 @@ global.Proxy = new $Object();
 
 var $Proxy = global.Proxy
 
-var fundamentalTraps = [
-  "getOwnPropertyDescriptor",
-  "getPropertyDescriptor",
-  "getOwnPropertyNames",
-  "getPropertyNames",
-  "defineProperty",
-  "delete",
-  "fix",
-]
-
-var derivedTraps = [
-  "has",
-  "hasOwn",
-  "get",
-  "set",
-  "enumerate",
-  "keys",
-]
-
-var functionTraps = [
-  "callTrap",
-  "constructTrap",
-]
-
-$Proxy.createFunction = function(handler, callTrap, constructTrap) {
-  handler.callTrap = callTrap
-  handler.constructTrap = constructTrap
-  $Proxy.create(handler)
-}
-
 $Proxy.create = function(handler, proto) {
   if (!IS_SPEC_OBJECT(handler))
     throw MakeTypeError("handler_non_object", ["create"])
@@ -66,6 +36,20 @@ $Proxy.create = function(handler, proto) {
   return %CreateJSProxy(handler, proto)
 }
 
+$Proxy.createFunction = function(handler, callTrap, constructTrap) {
+  if (!IS_SPEC_OBJECT(handler))
+    throw MakeTypeError("handler_non_object", ["create"])
+  if (!IS_SPEC_FUNCTION(callTrap))
+    throw MakeTypeError("trap_function_expected", ["createFunction", "call"])
+  if (IS_UNDEFINED(constructTrap)) {
+    constructTrap = callTrap
+  } else if (!IS_SPEC_FUNCTION(constructTrap)) {
+    throw MakeTypeError("trap_function_expected",
+                        ["createFunction", "construct"])
+  }
+  return %CreateJSFunctionProxy(
+    handler, callTrap, constructTrap, $Function.prototype)
+}
 
 
 
@@ -73,6 +57,13 @@ $Proxy.create = function(handler, proto) {
 // Builtins
 ////////////////////////////////////////////////////////////////////////////////
 
+function DelegateCallAndConstruct(callTrap, constructTrap) {
+  return function() {
+    return %Apply(%_IsConstructCall() ? constructTrap : callTrap,
+                  this, arguments, 0, %_ArgumentsLength())
+  }
+}
+
 function DerivedGetTrap(receiver, name) {
   var desc = this.getPropertyDescriptor(name)
   if (IS_UNDEFINED(desc)) { return desc }
index bf2a52f..0348dd1 100644 (file)
@@ -613,6 +613,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSProxy) {
 }
 
 
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSFunctionProxy) {
+  ASSERT(args.length() == 4);
+  Object* handler = args[0];
+  Object* call_trap = args[1];
+  Object* construct_trap = args[2];
+  Object* prototype = args[3];
+  Object* used_prototype =
+      prototype->IsJSReceiver() ? prototype : isolate->heap()->null_value();
+  return isolate->heap()->AllocateJSFunctionProxy(
+      handler, call_trap, construct_trap, used_prototype);
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSProxy) {
   ASSERT(args.length() == 1);
   Object* obj = args[0];
@@ -620,6 +633,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSProxy) {
 }
 
 
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSFunctionProxy) {
+  ASSERT(args.length() == 1);
+  Object* obj = args[0];
+  return isolate->heap()->ToBoolean(obj->IsJSFunctionProxy());
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHandler) {
   ASSERT(args.length() == 1);
   CONVERT_CHECKED(JSProxy, proxy, args[0]);
@@ -627,6 +647,20 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHandler) {
 }
 
 
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetCallTrap) {
+  ASSERT(args.length() == 1);
+  CONVERT_CHECKED(JSFunctionProxy, proxy, args[0]);
+  return proxy->call_trap();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetConstructTrap) {
+  ASSERT(args.length() == 1);
+  CONVERT_CHECKED(JSFunctionProxy, proxy, args[0]);
+  return proxy->construct_trap();
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_Fix) {
   ASSERT(args.length() == 1);
   CONVERT_CHECKED(JSProxy, proxy, args[0]);
@@ -2154,7 +2188,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionIsBuiltin) {
 
 
 RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) {
-  RUNTIME_ASSERT(isolate->bootstrapper()->IsActive());
   HandleScope scope(isolate);
   ASSERT(args.length() == 2);
 
@@ -5101,6 +5134,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Typeof) {
       ASSERT(heap_obj->IsUndefined());
       return isolate->heap()->undefined_symbol();
     case JS_FUNCTION_TYPE:
+    case JS_FUNCTION_PROXY_TYPE:
       return isolate->heap()->function_symbol();
     default:
       // For any kind of object not handled above, the spec rule for
@@ -8416,6 +8450,49 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CheckIsBootstrapping) {
 }
 
 
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Apply) {
+  HandleScope scope(isolate);
+  ASSERT(args.length() == 5);
+  CONVERT_CHECKED(JSReceiver, fun, args[0]);
+  Object* receiver = args[1];
+  CONVERT_CHECKED(JSObject, arguments, args[2]);
+  CONVERT_CHECKED(Smi, shift, args[3]);
+  CONVERT_CHECKED(Smi, arity, args[4]);
+
+  int offset = shift->value();
+  int argc = arity->value();
+  ASSERT(offset >= 0);
+  ASSERT(argc >= 0);
+
+  // 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 = arguments->GetElement(offset + i);
+     Object* object;
+     if (!maybe->To<Object>(&object)) return maybe;
+     argv[i] = Handle<Object>(object);
+  }
+
+  bool threw = false;
+  Handle<JSReceiver> hfun(fun);
+  Handle<Object> hreceiver(receiver);
+  Handle<Object> result = Execution::Call(
+      hfun, hreceiver, argc, reinterpret_cast<Object***>(argv), &threw, true);
+
+  if (threw) return Failure::Exception();
+  return *result;
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFunctionDelegate) {
   HandleScope scope(isolate);
   ASSERT(args.length() == 1);
index ddd5295..1538b7d 100644 (file)
@@ -80,6 +80,7 @@ namespace internal {
   \
   /* Utilities */ \
   F(CheckIsBootstrapping, 0, 1) \
+  F(Apply, 5, 1) \
   F(GetFunctionDelegate, 1, 1) \
   F(GetConstructorDelegate, 1, 1) \
   F(NewArgumentsFast, 3, 1) \
@@ -287,8 +288,12 @@ namespace internal {
   \
   /* Harmony proxies */ \
   F(CreateJSProxy, 2, 1) \
+  F(CreateJSFunctionProxy, 4, 1) \
   F(IsJSProxy, 1, 1) \
+  F(IsJSFunctionProxy, 1, 1) \
   F(GetHandler, 1, 1) \
+  F(GetCallTrap, 1, 1) \
+  F(GetConstructTrap, 1, 1) \
   F(Fix, 1, 1) \
   \
   /* Harmony weakmaps */ \
index 61deb9b..c52e7fb 100644 (file)
@@ -366,7 +366,7 @@ function IN(x) {
 // an expensive ToBoolean conversion in the generated code.
 function INSTANCE_OF(F) {
   var V = this;
-  if (!IS_FUNCTION(F)) {
+  if (!IS_SPEC_FUNCTION(F)) {
     throw %MakeTypeError('instanceof_function_expected', [V]);
   }
 
@@ -421,6 +421,31 @@ function CALL_NON_FUNCTION_AS_CONSTRUCTOR() {
 }
 
 
+function CALL_FUNCTION_PROXY() {
+  var arity = %_ArgumentsLength() - 1;
+  var proxy = %_Arguments(arity);  // The proxy comes in as an additional arg.
+  var trap = %GetCallTrap(proxy);
+  return %Apply(trap, this, arguments, 0, arity);
+}
+
+
+function CALL_FUNCTION_PROXY_AS_CONSTRUCTOR(proxy) {
+  var arity = %_ArgumentsLength() - 1;
+  var trap = %GetConstructTrap(proxy);
+  var receiver = void 0;
+  if (!IS_UNDEFINED(trap)) {
+    trap = %GetCallTrap(proxy);
+    var proto = proxy.prototype;
+    if (!IS_SPEC_OBJECT(proto) && proto !== null) {
+      throw MakeTypeError("proto_object_or_null", [proto]);
+    }
+    receiver = new global.Object();
+    receiver.__proto__ = proto;
+  }
+  return %Apply(trap, this, arguments, 1, arity);
+}
+
+
 function APPLY_PREPARE(args) {
   var length;
   // First check whether length is a positive Smi and args is an
@@ -428,7 +453,8 @@ function APPLY_PREPARE(args) {
   // that takes care of more eventualities.
   if (IS_ARRAY(args)) {
     length = args.length;
-    if (%_IsSmi(length) && length >= 0 && length < 0x800000 && IS_FUNCTION(this)) {
+    if (%_IsSmi(length) && length >= 0 && length < 0x800000 &&
+        IS_SPEC_FUNCTION(this)) {
       return length;
     }
   }
@@ -442,7 +468,7 @@ function APPLY_PREPARE(args) {
     throw %MakeRangeError('stack_overflow', []);
   }
 
-  if (!IS_FUNCTION(this)) {
+  if (!IS_SPEC_FUNCTION(this)) {
     throw %MakeTypeError('apply_non_function', [ %ToString(this), typeof this ]);
   }
 
@@ -610,13 +636,13 @@ function IsPrimitive(x) {
 // ECMA-262, section 8.6.2.6, page 28.
 function DefaultNumber(x) {
   var valueOf = x.valueOf;
-  if (IS_FUNCTION(valueOf)) {
+  if (IS_SPEC_FUNCTION(valueOf)) {
     var v = %_CallFunction(x, valueOf);
     if (%IsPrimitive(v)) return v;
   }
 
   var toString = x.toString;
-  if (IS_FUNCTION(toString)) {
+  if (IS_SPEC_FUNCTION(toString)) {
     var s = %_CallFunction(x, toString);
     if (%IsPrimitive(s)) return s;
   }
@@ -628,13 +654,13 @@ function DefaultNumber(x) {
 // ECMA-262, section 8.6.2.6, page 28.
 function DefaultString(x) {
   var toString = x.toString;
-  if (IS_FUNCTION(toString)) {
+  if (IS_SPEC_FUNCTION(toString)) {
     var s = %_CallFunction(x, toString);
     if (%IsPrimitive(s)) return s;
   }
 
   var valueOf = x.valueOf;
-  if (IS_FUNCTION(valueOf)) {
+  if (IS_SPEC_FUNCTION(valueOf)) {
     var v = %_CallFunction(x, valueOf);
     if (%IsPrimitive(v)) return v;
   }
index 6f68ce0..a3e3e09 100644 (file)
@@ -223,7 +223,7 @@ function StringReplace(search, replace) {
   // Delegate to one of the regular expression variants if necessary.
   if (IS_REGEXP(search)) {
     %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
-    if (IS_FUNCTION(replace)) {
+    if (IS_SPEC_FUNCTION(replace)) {
       if (search.global) {
         return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
       } else {
@@ -250,7 +250,7 @@ function StringReplace(search, replace) {
   builder.addSpecialSlice(0, start);
 
   // Compute the string to replace with.
-  if (IS_FUNCTION(replace)) {
+  if (IS_SPEC_FUNCTION(replace)) {
     var receiver = %GetDefaultReceiver(replace);
     builder.add(%_CallFunction(receiver,
                                search,
index 1616ac3..588bdb2 100644 (file)
@@ -298,7 +298,7 @@ function ObjectDefineGetter(name, fun) {
   if (receiver == null && !IS_UNDETECTABLE(receiver)) {
     receiver = %GlobalReceiver(global);
   }
-  if (!IS_FUNCTION(fun)) {
+  if (!IS_SPEC_FUNCTION(fun)) {
     throw new $TypeError('Object.prototype.__defineGetter__: Expecting function');
   }
   var desc = new PropertyDescriptor();
@@ -323,7 +323,7 @@ function ObjectDefineSetter(name, fun) {
   if (receiver == null && !IS_UNDETECTABLE(receiver)) {
     receiver = %GlobalReceiver(global);
   }
-  if (!IS_FUNCTION(fun)) {
+  if (!IS_SPEC_FUNCTION(fun)) {
     throw new $TypeError(
         'Object.prototype.__defineSetter__: Expecting function');
   }
@@ -453,7 +453,7 @@ function ToPropertyDescriptor(obj) {
 
   if ("get" in obj) {
     var get = obj.get;
-    if (!IS_UNDEFINED(get) && !IS_FUNCTION(get)) {
+    if (!IS_UNDEFINED(get) && !IS_SPEC_FUNCTION(get)) {
       throw MakeTypeError("getter_must_be_callable", [get]);
     }
     desc.setGet(get);
@@ -461,7 +461,7 @@ function ToPropertyDescriptor(obj) {
 
   if ("set" in obj) {
     var set = obj.set;
-    if (!IS_UNDEFINED(set) && !IS_FUNCTION(set)) {
+    if (!IS_UNDEFINED(set) && !IS_SPEC_FUNCTION(set)) {
       throw MakeTypeError("setter_must_be_callable", [set]);
     }
     desc.setSet(set);
@@ -623,7 +623,7 @@ function GetTrap(handler, name, defaultTrap) {
       throw MakeTypeError("handler_trap_missing", [handler, name]);
     }
     trap = defaultTrap;
-  } else if (!IS_FUNCTION(trap)) {
+  } else if (!IS_SPEC_FUNCTION(trap)) {
     throw MakeTypeError("handler_trap_must_be_callable", [handler, name]);
   }
   return trap;
@@ -977,7 +977,7 @@ function ObjectDefineProperty(obj, p, attributes) {
     // Clone the attributes object for protection.
     // TODO(rossberg): not spec'ed yet, so not sure if this should involve
     // non-own properties as it does (or non-enumerable ones, as it doesn't?).
-    var attributesClone = {}
+    var attributesClone = {};
     for (var a in attributes) {
       attributesClone[a] = attributes[a];
     }
@@ -1041,7 +1041,16 @@ function ProxyFix(obj) {
   if (IS_UNDEFINED(props)) {
     throw MakeTypeError("handler_returned_undefined", [handler, "fix"]);
   }
-  %Fix(obj);
+
+  if (IS_SPEC_FUNCTION(obj)) {
+    var callTrap = %GetCallTrap(obj);
+    var constructTrap = %GetConstructTrap(obj);
+    var code = DelegateCallAndConstruct(callTrap, constructTrap);
+    %Fix(obj);  // becomes a regular function
+    %SetCode(obj, code);
+  } else {
+    %Fix(obj);
+  }
   ObjectDefineProperties(obj, props);
 }
 
@@ -1412,6 +1421,10 @@ SetUpNumber();
 $Function.prototype.constructor = $Function;
 
 function FunctionSourceString(func) {
+  while (%IsJSFunctionProxy(func)) {
+    func = %GetCallTrap(func);
+  }
+
   if (!IS_FUNCTION(func)) {
     throw new $TypeError('Function.prototype.toString is not generic');
   }
@@ -1441,12 +1454,13 @@ function FunctionToString() {
 
 // ES5 15.3.4.5
 function FunctionBind(this_arg) { // Length is 1.
-  if (!IS_FUNCTION(this)) {
+  if (!IS_SPEC_FUNCTION(this)) {
       throw new $TypeError('Bind must be called on a function');
   }
   // this_arg is not an argument that should be bound.
   var argc_bound = (%_ArgumentsLength() || 1) - 1;
   var fn = this;
+
   if (argc_bound == 0) {
     var result = function() {
       if (%_IsConstructCall()) {
@@ -1455,8 +1469,7 @@ function FunctionBind(this_arg) { // Length is 1.
         // materializing it and guarantee that this function will be optimized.
         return %NewObjectFromBound(fn, null);
       }
-
-      return fn.apply(this_arg, arguments);
+      return %Apply(fn, this_arg, arguments, 0, %_ArgumentsLength());
     };
   } else {
     var bound_args = new InternalArray(argc_bound);
@@ -1486,7 +1499,7 @@ function FunctionBind(this_arg) { // Length is 1.
       for (var i = 0; i < argc; i++) {
         args[argc_bound + i] = %_Arguments(i);
       }
-      return fn.apply(this_arg, args);
+      return %Apply(fn, this_arg, args, 0, argc + argc_bound);
     };
   }
 
@@ -1497,11 +1510,16 @@ function FunctionBind(this_arg) { // Length is 1.
   // is called and make them non-enumerable and non-configurable.
   // To be consistent with our normal functions we leave this as it is.
 
-  // Set the correct length.
-  var length = (this.length - argc_bound) > 0 ? this.length - argc_bound : 0;
   %FunctionRemovePrototype(result);
   %FunctionSetBound(result);
-  %BoundFunctionSetLength(result, length);
+  // Set the correct length. If this is a function proxy, this.length might
+  // throw, or return a bogus result. Leave length alone in that case.
+  // TODO(rossberg): This is underspecified in the current proxy proposal.
+  try {
+    var old_length = ToInteger(this.length);
+    var length = (old_length - argc_bound) > 0 ? old_length - argc_bound : 0;
+    %BoundFunctionSetLength(result, length);
+  } catch(x) {}
   return result;
 }
 
index 7c6f7e3..db06909 100644 (file)
@@ -655,15 +655,16 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
 
   // 2. Get the function to call (passed as receiver) from the stack, check
   //    if it is a function.
-  Label non_function;
+  Label slow, non_function;
   // The function to call is at position n+1 on the stack.
   __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
   __ JumpIfSmi(rdi, &non_function);
   __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
-  __ j(not_equal, &non_function);
+  __ j(not_equal, &slow);
 
   // 3a. Patch the first argument if necessary when calling a function.
   Label shift_arguments;
+  __ Set(rdx, 0);  // indicate regular JS_FUNCTION
   { Label convert_to_object, use_global_receiver, patch_receiver;
     // Change context eagerly in case we need the global receiver.
     __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
@@ -701,6 +702,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ push(rbx);
     __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
     __ movq(rbx, rax);
+    __ Set(rdx, 0);  // indicate regular JS_FUNCTION
 
     __ pop(rax);
     __ SmiToInteger32(rax, rax);
@@ -725,14 +727,19 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ jmp(&shift_arguments);
   }
 
+  // 3b. Check for function proxy.
+  __ bind(&slow);
+  __ Set(rdx, 1);  // indicate function proxy
+  __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE);
+  __ j(equal, &shift_arguments);
+  __ bind(&non_function);
+  __ Set(rdx, 2);  // indicate non-function
 
-  // 3b. Patch the first argument when calling a non-function.  The
+  // 3c. Patch the first argument when calling a non-function.  The
   //     CALL_NON_FUNCTION builtin expects the non-function callee as
   //     receiver, so overwrite the first argument which will ultimately
   //     become the receiver.
-  __ bind(&non_function);
   __ movq(Operand(rsp, rax, times_pointer_size, 0), rdi);
-  __ Set(rdi, 0);
 
   // 4. Shift arguments and return address one slot down on the stack
   //    (overwriting the original receiver).  Adjust argument count to make
@@ -749,13 +756,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
     __ decq(rax);  // One fewer argument (first argument is new receiver).
   }
 
-  // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
-  { Label function;
-    __ testq(rdi, rdi);
-    __ j(not_zero, &function);
+  // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin,
+  //     or a function proxy via CALL_FUNCTION_PROXY.
+  { Label function, non_proxy;
+    __ testq(rdx, rdx);
+    __ j(zero, &function);
     __ Set(rbx, 0);
-    __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
     __ SetCallKind(rcx, CALL_AS_METHOD);
+    __ cmpq(rdx, Immediate(1));
+    __ j(not_equal, &non_proxy);
+
+    __ pop(rdx);   // return address
+    __ push(rdi);  // re-add proxy object as additional argument
+    __ push(rdx);
+    __ incq(rax);
+    __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY);
+    __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+           RelocInfo::CODE_TARGET);
+
+    __ bind(&non_proxy);
+    __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
     __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
             RelocInfo::CODE_TARGET);
     __ bind(&function);
@@ -797,11 +817,12 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   static const int kArgumentsOffset = 2 * kPointerSize;
   static const int kReceiverOffset = 3 * kPointerSize;
   static const int kFunctionOffset = 4 * kPointerSize;
+
   __ push(Operand(rbp, kFunctionOffset));
   __ push(Operand(rbp, kArgumentsOffset));
   __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
 
-  // Check the stack for overflow. We are not trying need to catch
+  // Check the stack for overflow. We are not trying to catch
   // interruptions (e.g. debug break and preemption) here, so the "real stack
   // limit" is checked.
   Label okay;
@@ -831,16 +852,20 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ push(rax);  // limit
   __ push(Immediate(0));  // index
 
-  // Change context eagerly to get the right global object if
-  // necessary.
+  // Get the receiver.
+  __ movq(rbx, Operand(rbp, kReceiverOffset));
+
+  // Check that the function is a JS function (otherwise it must be a proxy).
+  Label push_receiver;
   __ movq(rdi, Operand(rbp, kFunctionOffset));
-  __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
+  __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+  __ j(not_equal, &push_receiver);
 
-  // Compute the receiver.
-  Label call_to_object, use_global_receiver, push_receiver;
-  __ movq(rbx, Operand(rbp, kReceiverOffset));
+  // Change context eagerly to get the right global object if necessary.
+  __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
 
   // Do not transform the receiver for strict mode functions.
+  Label call_to_object, use_global_receiver;
   __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
   __ testb(FieldOperand(rdx, SharedFunctionInfo::kStrictModeByteOffset),
            Immediate(1 << SharedFunctionInfo::kStrictModeBitWithinByte));
@@ -913,14 +938,30 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ j(not_equal, &loop);
 
   // Invoke the function.
+  Label call_proxy;
   ParameterCount actual(rax);
   __ SmiToInteger32(rax, rax);
   __ movq(rdi, Operand(rbp, kFunctionOffset));
+  __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+  __ j(not_equal, &call_proxy);
   __ InvokeFunction(rdi, actual, CALL_FUNCTION,
                     NullCallWrapper(), CALL_AS_METHOD);
 
   __ LeaveInternalFrame();
-  __ ret(3 * kPointerSize);  // remove function, receiver, and arguments
+  __ ret(3 * kPointerSize);  // remove this, receiver, and arguments
+
+  // Invoke the function proxy.
+  __ bind(&call_proxy);
+  __ push(rdi);  // add function proxy as last argument
+  __ incq(rax);
+  __ Set(rbx, 0);
+  __ SetCallKind(rcx, CALL_AS_METHOD);
+  __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY);
+  __ call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+          RelocInfo::CODE_TARGET);
+
+  __ LeaveInternalFrame();
+  __ ret(3 * kPointerSize);  // remove this, receiver, and arguments
 }
 
 
index 8a5f59b..df4438b 100644 (file)
@@ -3232,7 +3232,7 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
 
 
 void CallFunctionStub::Generate(MacroAssembler* masm) {
-  Label slow;
+  Label slow, non_function;
 
   // The receiver might implicitly be the global object. This is
   // indicated by passing the hole as the receiver to the call
@@ -3257,7 +3257,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
   __ movq(rdi, Operand(rsp, (argc_ + 2) * kPointerSize));
 
   // Check that the function really is a JavaScript function.
-  __ JumpIfSmi(rdi, &slow);
+  __ JumpIfSmi(rdi, &non_function);
   // Goto slow case if we do not have a function.
   __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
   __ j(not_equal, &slow);
@@ -3284,15 +3284,32 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
 
   // Slow-case: Non-function called.
   __ bind(&slow);
+  // Check for function proxy.
+  __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE);
+  __ j(not_equal, &non_function);
+  __ pop(rcx);
+  __ push(rdi);  // put proxy as additional argument under return address
+  __ push(rcx);
+  __ Set(rax, argc_ + 1);
+  __ Set(rbx, 0);
+  __ SetCallKind(rcx, CALL_AS_FUNCTION);
+  __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY);
+  {
+    Handle<Code> adaptor =
+      masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
+    __ jmp(adaptor, RelocInfo::CODE_TARGET);
+  }
+
   // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
   // of the original receiver from the call site).
+  __ bind(&non_function);
   __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rdi);
   __ Set(rax, argc_);
   __ Set(rbx, 0);
+  __ SetCallKind(rcx, CALL_AS_METHOD);
   __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
   Handle<Code> adaptor =
       Isolate::Current()->builtins()->ArgumentsAdaptorTrampoline();
-  __ SetCallKind(rcx, CALL_AS_METHOD);
   __ Jump(adaptor, RelocInfo::CODE_TARGET);
 }
 
index f8f0a28..ff6677e 100644 (file)
@@ -163,6 +163,9 @@ var knownProblems = {
   "PromoteScheduledException": true,
   "DeleteHandleScopeExtensions": true,
 
+  // Requires integer arguments to be non-negative.
+  "Apply": true,
+
   // That can only be invoked on Array.prototype.
   "FinishArrayPrototypeSetup": true,
 
index 640033d..b1ce7e5 100644 (file)
@@ -1,5 +1,3 @@
-// Flags: --harmony-proxies
-
 // Copyright 2008 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
 // (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
+
 
 // TODO(rossberg): test exception cases.
+// TODO(rossberg): test proxies as prototypes.
+// TODO(rossberg): for-in for proxies not implemented.
+// TODO(rossberg): function proxies as constructors not implemented.
+
+
+// Helper.
+
+function TestWithProxies(test, handler) {
+  test(handler, Proxy.create)
+  test(handler, function(h) {return Proxy.createFunction(h, function() {})})
+}
 
 
 // Getters.
 
 function TestGet(handler) {
-  var o = Proxy.create(handler)
+  TestWithProxies(TestGet2, handler)
+}
+
+function TestGet2(handler, create) {
+  var o = create(handler)
   assertEquals(42, o.a)
   assertEquals(42, o["b"])
 }
@@ -76,7 +91,11 @@ TestGet(Proxy.create({
 
 
 function TestGetCall(handler) {
-  var p = Proxy.create(handler)
+  TestWithProxies(TestGetCall2, handler)
+}
+
+function TestGetCall2(handler, create) {
+  var p = create(handler)
   assertEquals(55, p.f())
   assertEquals(55, p.f("unused", "arguments"))
   assertEquals(55, p.f.call(p))
@@ -144,8 +163,13 @@ TestGetCall(Proxy.create({
 
 var key
 var val
-function TestSet(handler) {
-  var o = Proxy.create(handler)
+
+function TestSet(handler, create) {
+  TestWithProxies(TestSet2, handler)
+}
+
+function TestSet2(handler, create) {
+  var o = create(handler)
   assertEquals(42, o.a = 42)
   assertEquals("a", key)
   assertEquals(42, val)
@@ -229,8 +253,13 @@ TestSet(Proxy.create({
 
 var key
 var desc
+
 function TestDefine(handler) {
-  var o = Proxy.create(handler)
+  TestWithProxies(TestDefine2, handler)
+}
+
+function TestDefine2(handler, create) {
+  var o = create(handler)
   assertEquals(o, Object.defineProperty(o, "a", {value: 44}))
   assertEquals("a", key)
   assertEquals(1, Object.getOwnPropertyNames(desc).length)
@@ -269,8 +298,8 @@ function TestDefine(handler) {
   assertEquals("zzz", key)
   assertEquals(0, Object.getOwnPropertyNames(desc).length)
 
-// TODO(rossberg): This test requires [s in proxy] to be implemented first.
-//  var d = Proxy.create({
+// TODO(rossberg): This test requires for-in on proxies.
+//  var d = create({
 //    get: function(r, k) { return (k === "value") ? 77 : void 0 },
 //    getOwnPropertyNames: function() { return ["value"] }
 //  })
@@ -316,8 +345,13 @@ TestDefine(Proxy.create({
 // Property deletion (delete).
 
 var key
+
 function TestDelete(handler) {
-  var o = Proxy.create(handler)
+  TestWithProxies(TestDelete2, handler)
+}
+
+function TestDelete2(handler, create) {
+  var o = create(handler)
   assertEquals(true, delete o.a)
   assertEquals("a", key)
   assertEquals(true, delete o["b"])
@@ -362,7 +396,11 @@ TestDelete(Proxy.create({
 // Property descriptors (Object.getOwnPropertyDescriptor).
 
 function TestDescriptor(handler) {
-  var o = Proxy.create(handler)
+  TestWithProxies(TestDescriptor2, handler)
+}
+
+function TestDescriptor2(handler, create) {
+  var o = create(handler)
   var descs = [
     {configurable: true},
     {value: 34, enumerable: true, configurable: true},
@@ -382,7 +420,6 @@ function TestDescriptor(handler) {
   }
 }
 
-
 TestDescriptor({
   defineProperty: function(k, d) { this["__" + k] = d; return true },
   getOwnPropertyDescriptor: function(k) { return this["__" + k] }
@@ -401,8 +438,12 @@ TestDescriptor({
 // Comparison.
 
 function TestComparison(eq) {
-  var o1 = Proxy.create({})
-  var o2 = Proxy.create({})
+  TestWithProxies(TestComparison2, eq)
+}
+
+function TestComparison2(eq, create) {
+  var o1 = create({})
+  var o2 = create({})
 
   assertTrue(eq(o1, o1))
   assertTrue(eq(o2, o2))
@@ -419,21 +460,32 @@ TestComparison(function(o1, o2) { return !(o1 !== o2) })
 
 
 
-// Type.
+// Type (typeof).
 
-assertEquals("object", typeof Proxy.create({}))
-assertTrue(typeof Proxy.create({}) == "object")
-assertTrue("object" == typeof Proxy.create({}))
+function TestTypeof() {
+  assertEquals("object", typeof Proxy.create({}))
+  assertTrue(typeof Proxy.create({}) == "object")
+  assertTrue("object" == typeof Proxy.create({}))
 
-// No function proxies yet.
+  assertEquals("function", typeof Proxy.createFunction({}, function() {}))
+  assertTrue(typeof Proxy.createFunction({}, function() {}) == "function")
+  assertTrue("function" == typeof Proxy.createFunction({}, function() {}))
+}
+
+TestTypeof()
 
 
 
 // Membership test (in).
 
 var key
+
 function TestIn(handler) {
-  var o = Proxy.create(handler)
+  TestWithProxies(TestIn2, handler)
+}
+
+function TestIn2(handler, create) {
+  var o = create(handler)
   assertTrue("a" in o)
   assertEquals("a", key)
   assertTrue(99 in o)
@@ -510,8 +562,13 @@ TestIn(Proxy.create({
 // Own Properties (Object.prototype.hasOwnProperty).
 
 var key
+
 function TestHasOwn(handler) {
-  var o = Proxy.create(handler)
+  TestWithProxies(TestHasOwn2, handler)
+}
+
+function TestHasOwn2(handler, create) {
+  var o = create(handler)
   assertTrue(Object.prototype.hasOwnProperty.call(o, "a"))
   assertEquals("a", key)
   assertTrue(Object.prototype.hasOwnProperty.call(o, 99))
@@ -573,29 +630,32 @@ function TestInstanceof() {
   var p2 = Proxy.create({}, o)
   var p3 = Proxy.create({}, p2)
 
-  var f = function() {}
-  f.prototype = o
+  var f0 = function() {}
+  f0.prototype = o
   var f1 = function() {}
   f1.prototype = p1
   var f2 = function() {}
   f2.prototype = p2
 
   assertTrue(o instanceof Object)
-  assertFalse(o instanceof f)
+  assertFalse(o instanceof f0)
   assertFalse(o instanceof f1)
   assertFalse(o instanceof f2)
   assertFalse(p1 instanceof Object)
-  assertFalse(p1 instanceof f)
+  assertFalse(p1 instanceof f0)
   assertFalse(p1 instanceof f1)
   assertFalse(p1 instanceof f2)
   assertTrue(p2 instanceof Object)
-  assertTrue(p2 instanceof f)
+  assertTrue(p2 instanceof f0)
   assertFalse(p2 instanceof f1)
   assertFalse(p2 instanceof f2)
   assertTrue(p3 instanceof Object)
-  assertTrue(p3 instanceof f)
+  assertTrue(p3 instanceof f0)
   assertFalse(p3 instanceof f1)
   assertTrue(p3 instanceof f2)
+
+  var f = Proxy.createFunction({}, function() {})
+  assertTrue(f instanceof Function)
 }
 
 TestInstanceof()
@@ -642,6 +702,11 @@ function TestPrototype() {
   assertTrue(Object.prototype.isPrototypeOf.call(p2, p3))
   assertFalse(Object.prototype.isPrototypeOf.call(p2, p4))
   assertFalse(Object.prototype.isPrototypeOf.call(p3, p2))
+
+  var f = Proxy.createFunction({}, function() {})
+  assertSame(Object.getPrototypeOf(f), Function.prototype)
+  assertTrue(Object.prototype.isPrototypeOf(f))
+  assertTrue(Object.prototype.isPrototypeOf.call(Function.prototype, f))
 }
 
 TestPrototype()
@@ -651,8 +716,12 @@ TestPrototype()
 // Property names (Object.getOwnPropertyNames, Object.keys).
 
 function TestPropertyNames(names, handler) {
-  var p = Proxy.create(handler)
-  assertArrayEquals(names, Object.getOwnPropertyNames(p))
+  TestWithProxies(TestPropertyNames2, [names, handler])
+}
+
+function TestPropertyNames2(names_handler, create) {
+  var p = create(names_handler[1])
+  assertArrayEquals(names_handler[0], Object.getOwnPropertyNames(p))
 }
 
 TestPropertyNames([], {
@@ -676,8 +745,12 @@ TestPropertyNames(["[object Object]"], {
 
 
 function TestKeys(names, handler) {
-  var p = Proxy.create(handler)
-  assertArrayEquals(names, Object.keys(p))
+  TestWithProxies(TestKeys2, [names, handler])
+}
+
+function TestKeys2(names_handler, create) {
+  var p = create(names_handler[1])
+  assertArrayEquals(names_handler[0], Object.keys(p))
 }
 
 TestKeys([], {
@@ -814,10 +887,38 @@ TestFix(["b"], {
 })
 
 
+function TestFixFunction(fix) {
+  var f1 = Proxy.createFunction({
+    fix: function() { return {} }
+  }, function() {})
+  fix(f1)
+  assertEquals(0, f1.length)
+
+  var f2 = Proxy.createFunction({
+    fix: function() { return {length: {value: 3}} }
+  }, function() {})
+  fix(f2)
+  assertEquals(3, f2.length)
+
+  var f3 = Proxy.createFunction({
+    fix: function() { return {length: {value: "huh"}} }
+  }, function() {})
+  fix(f3)
+  assertEquals(0, f1.length)
+}
 
-// String conversion (Object.prototype.toString, Object.prototype.toLocaleString)
+TestFixFunction(Object.seal)
+TestFixFunction(Object.freeze)
+TestFixFunction(Object.preventExtensions)
+
+
+
+// String conversion (Object.prototype.toString,
+//                    Object.prototype.toLocaleString,
+//                    Function.prototype.toString)
 
 var key
+
 function TestToString(handler) {
   var o = Proxy.create(handler)
   key = ""
@@ -825,6 +926,14 @@ function TestToString(handler) {
   assertEquals("", key)
   assertEquals("my_proxy", Object.prototype.toLocaleString.call(o))
   assertEquals("toString", key)
+
+  var f = Proxy.createFunction(handler, function() {})
+  key = ""
+  assertEquals("[object Function]", Object.prototype.toString.call(f))
+  assertEquals("", key)
+  assertEquals("my_proxy", Object.prototype.toLocaleString.call(o))
+  assertEquals("toString", key)
+  assertDoesNotThrow(function(){ Function.prototype.toString.call(f) })
 }
 
 TestToString({
@@ -847,7 +956,11 @@ TestToString(Proxy.create({
 // Value conversion (Object.prototype.toValue)
 
 function TestValueOf(handler) {
-  var o = Proxy.create(handler)
+  TestWithProxies(TestValueOf2, handler)
+}
+
+function TestValueOf2(handler, create) {
+  var o = create(handler)
   assertSame(o, Object.prototype.valueOf.call(o))
 }
 
@@ -858,8 +971,13 @@ TestValueOf({})
 // Enumerability (Object.prototype.propertyIsEnumerable)
 
 var key
+
 function TestIsEnumerable(handler) {
-  var o = Proxy.create(handler)
+  TestWithProxies(TestIsEnumerable2, handler)
+}
+
+function TestIsEnumerable2(handler, create) {
+  var o = create(handler)
   assertTrue(Object.prototype.propertyIsEnumerable.call(o, "a"))
   assertEquals("a", key)
   assertTrue(Object.prototype.propertyIsEnumerable.call(o, 2))
@@ -896,3 +1014,84 @@ TestIsEnumerable(Proxy.create({
     }
   }
 }))
+
+
+
+// Calling (call, Function.prototype.call, Function.prototype.apply,
+//          Function.prototype.bind).
+
+var global = this
+var receiver
+
+function TestCall(isStrict, callTrap) {
+  assertEquals(42, callTrap(5, 37))
+// TODO(rossberg): unrelated bug: this does not succeed for optimized code.
+// assertEquals(isStrict ? undefined : global, receiver)
+
+  var f = Proxy.createFunction({fix: function() { return {} }}, callTrap)
+
+  receiver = 333
+  assertEquals(42, f(11, 31))
+  assertEquals(isStrict ? undefined : global, receiver)
+  var o = {}
+  assertEquals(42, Function.prototype.call.call(f, o, 20, 22))
+  assertEquals(o, receiver)
+  assertEquals(43, Function.prototype.call.call(f, null, 20, 23))
+  assertEquals(isStrict ? null : global, receiver)
+  assertEquals(44, Function.prototype.call.call(f, 2, 21, 23))
+  assertEquals(2, receiver.valueOf())
+  receiver = 333
+  assertEquals(32, Function.prototype.apply.call(f, o, [17, 15]))
+  assertEquals(o, receiver)
+  var ff = Function.prototype.bind.call(f, o, 12)
+  receiver = 333
+  assertEquals(42, ff(30))
+  assertEquals(o, receiver)
+  receiver = 333
+  assertEquals(32, Function.prototype.apply.call(ff, {}, [20]))
+  assertEquals(o, receiver)
+
+  Object.freeze(f)
+
+  receiver = 333
+  assertEquals(42, f(11, 31))
+// TODO(rossberg): unrelated bug: this does not succeed for optimized code.
+// assertEquals(isStrict ? undefined : global, receiver)
+  receiver = 333
+  assertEquals(42, Function.prototype.call.call(f, o, 20, 22))
+  assertEquals(o, receiver)
+  receiver = 333
+  assertEquals(32, Function.prototype.apply.call(f, o, [17, 15]))
+  assertEquals(o, receiver)
+  receiver = 333
+  assertEquals(42, ff(30))
+  assertEquals(o, receiver)
+  receiver = 333
+  assertEquals(32, Function.prototype.apply.call(ff, {}, [20]))
+  assertEquals(o, receiver)
+}
+
+TestCall(false, function(x, y) {
+  receiver = this; return x + y
+})
+
+TestCall(true, function(x, y) {
+  "use strict";
+  receiver = this; return x + y
+})
+
+TestCall(false, Proxy.createFunction({}, function(x, y) {
+  receiver = this; return x + y
+}))
+
+TestCall(true, Proxy.createFunction({}, function(x, y) {
+  "use strict";
+  receiver = this; return x + y
+}))
+
+var p = Proxy.createFunction({fix: function() {return {}}}, function(x, y) {
+  receiver = this; return x + y
+})
+TestCall(false, p)
+Object.freeze(p)
+TestCall(false, p)