[runtime] [proxy] Implementing [[Call]]

BUG=v8:1543
LOG=N

Review URL: https://codereview.chromium.org/1499593003

Cr-Commit-Position: refs/heads/master@{#32675}
diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc
index f028cad..4033ea6 100644
--- a/src/arm/builtins-arm.cc
+++ b/src/arm/builtins-arm.cc
@@ -1685,8 +1685,14 @@
   __ cmp(r5, Operand(JS_PROXY_TYPE));
   __ b(ne, &non_function);
 
-  // 1. Call to Proxy.
-  // TODO(neis): Implement [[Call]] on proxies.
+  // 1. Runtime fallback for Proxy [[Call]].
+  __ Push(r1);
+  // Increase the arguments size to include the pushed function and the
+  // existing receiver on the stack.
+  __ add(r0, r0, Operand(2));
+  // Tail-call to the runtime.
+  __ JumpToExternalReference(
+      ExternalReference(Runtime::kJSProxyCall, masm->isolate()));
 
   // 2. Call to something else, which might have a [[Call]] internal method (if
   // not we raise an exception).
diff --git a/src/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc
index 2c7aaae..b7be974 100644
--- a/src/arm64/builtins-arm64.cc
+++ b/src/arm64/builtins-arm64.cc
@@ -1677,8 +1677,14 @@
   __ Cmp(x5, JS_PROXY_TYPE);
   __ B(ne, &non_function);
 
-  // 1. Call to proxy.
-  // TODO(neis): This doesn't match the ES6 spec for [[Call]] on proxies.
+  // 1. Runtime fallback for Proxy [[Call]].
+  __ Push(x1);
+  // Increase the arguments size to include the pushed function and the
+  // existing receiver on the stack.
+  __ Add(x0, x0, Operand(2));
+  // Tail-call to the runtime.
+  __ JumpToExternalReference(
+      ExternalReference(Runtime::kJSProxyCall, masm->isolate()));
 
   // 2. Call to something else, which might have a [[Call]] internal method (if
   // not we raise an exception).
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 8a96ac0..190b70c 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -218,6 +218,7 @@
   void InstallBuiltinFunctionIds();
   void InstallExperimentalBuiltinFunctionIds();
   void InitializeNormalizedMapCaches();
+  void InstallJSProxyMaps();
 
   enum ExtensionTraversalState {
     UNVISITED, VISITED, INSTALLED
@@ -362,6 +363,20 @@
 
 namespace {
 
+Handle<JSFunction> InstallFunction(Handle<JSObject> target,
+                                   Handle<Name> property_name,
+                                   Handle<JSFunction> function,
+                                   Handle<String> function_name,
+                                   PropertyAttributes attributes = DONT_ENUM) {
+  JSObject::AddProperty(target, property_name, function, attributes);
+  if (target->IsJSGlobalObject()) {
+    function->shared()->set_instance_class_name(*function_name);
+  }
+  function->shared()->set_native(true);
+  return function;
+}
+
+
 Handle<JSFunction> InstallFunction(Handle<JSObject> target, Handle<Name> name,
                                    InstanceType type, int instance_size,
                                    MaybeHandle<JSObject> maybe_prototype,
@@ -382,12 +397,7 @@
                                  kInstallConstructor, strict_function_map)
           : factory->NewFunctionWithoutPrototype(name_string, call_code,
                                                  strict_function_map);
-  JSObject::AddProperty(target, name, function, attributes);
-  if (target->IsJSGlobalObject()) {
-    function->shared()->set_instance_class_name(*name_string);
-  }
-  function->shared()->set_native(true);
-  return function;
+  return InstallFunction(target, name, function, name_string, attributes);
 }
 
 
@@ -2144,21 +2154,58 @@
 }
 
 
+void Genesis::InstallJSProxyMaps() {
+  // Allocate the different maps for all Proxy types.
+  // Next to the default proxy, we need maps indicating callable and
+  // constructable proxies.
+
+  Handle<Map> proxy_function_map =
+      Map::Copy(isolate()->sloppy_function_without_prototype_map(), "Proxy");
+  proxy_function_map->set_is_constructor(true);
+  native_context()->set_proxy_function_map(*proxy_function_map);
+
+  Handle<Map> proxy_map =
+      factory()->NewMap(JS_PROXY_TYPE, JSProxy::kSize, FAST_ELEMENTS);
+  native_context()->set_proxy_map(*proxy_map);
+
+  Handle<Map> proxy_callable_map = Map::Copy(proxy_map, "callable Proxy");
+  proxy_callable_map->set_is_callable();
+  native_context()->set_proxy_callable_map(*proxy_callable_map);
+
+  Handle<Map> proxy_constructor_map =
+      Map::Copy(proxy_callable_map, "constructor Proxy");
+  proxy_constructor_map->set_is_constructor(true);
+  native_context()->set_proxy_constructor_map(*proxy_constructor_map);
+}
+
+
 void Genesis::InitializeGlobal_harmony_proxies() {
   if (!FLAG_harmony_proxies) return;
   Handle<JSGlobalObject> global(
       JSGlobalObject::cast(native_context()->global_object()));
   Isolate* isolate = global->GetIsolate();
-  Handle<JSFunction> proxy_fun = InstallFunction(
-      global, "Proxy", JS_PROXY_TYPE, JSProxy::kSize,
-      isolate->initial_object_prototype(), Builtins::kProxyConstructor);
-  // TODO(verwaest): Set to null in InstallFunction.
-  proxy_fun->initial_map()->set_prototype(isolate->heap()->null_value());
-  proxy_fun->shared()->set_construct_stub(
+  Factory* factory = isolate->factory();
+
+  InstallJSProxyMaps();
+
+  // Create the Proxy object.
+  Handle<String> name = factory->Proxy_string();
+  Handle<Code> code(isolate->builtins()->ProxyConstructor());
+
+  Handle<JSFunction> proxy_function =
+      factory->NewFunction(isolate->proxy_function_map(), name, code);
+
+  JSFunction::SetInitialMap(proxy_function,
+                            Handle<Map>(native_context()->proxy_map(), isolate),
+                            factory->null_value());
+
+  proxy_function->shared()->set_construct_stub(
       *isolate->builtins()->ProxyConstructor_ConstructStub());
-  proxy_fun->shared()->set_internal_formal_parameter_count(2);
-  proxy_fun->shared()->set_length(2);
-  native_context()->set_proxy_function(*proxy_fun);
+  proxy_function->shared()->set_internal_formal_parameter_count(2);
+  proxy_function->shared()->set_length(2);
+
+  native_context()->set_proxy_function(*proxy_function);
+  InstallFunction(global, name, proxy_function, name);
 }
 
 
diff --git a/src/builtins.cc b/src/builtins.cc
index 9fb8078..fcac684 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -1799,6 +1799,7 @@
 // ES6 section 26.2.1.1 Proxy ( target, handler ) for the [[Construct]] case.
 BUILTIN(ProxyConstructor_ConstructStub) {
   HandleScope scope(isolate);
+  DCHECK(isolate->proxy_function()->IsConstructor());
   DCHECK_EQ(3, args.length());
   Handle<Object> target = args.at<Object>(1);
   Handle<Object> handler = args.at<Object>(2);
diff --git a/src/contexts.h b/src/contexts.h
index 71a7425..a7bbebf 100644
--- a/src/contexts.h
+++ b/src/contexts.h
@@ -220,7 +220,11 @@
   V(OBJECT_FUNCTION_INDEX, JSFunction, object_function)                        \
   V(OBJECT_FUNCTION_PROTOTYPE_MAP_INDEX, Map, object_function_prototype_map)   \
   V(OPAQUE_REFERENCE_FUNCTION_INDEX, JSFunction, opaque_reference_function)    \
+  V(PROXY_CALLABLE_MAP_INDEX, Map, proxy_callable_map)                         \
+  V(PROXY_CONSTRUCTOR_MAP_INDEX, Map, proxy_constructor_map)                   \
   V(PROXY_FUNCTION_INDEX, JSFunction, proxy_function)                          \
+  V(PROXY_FUNCTION_MAP_INDEX, Map, proxy_function_map)                         \
+  V(PROXY_MAP_INDEX, Map, proxy_map)                                           \
   V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function)                        \
   V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map)                           \
   V(SCRIPT_CONTEXT_TABLE_INDEX, ScriptContextTable, script_context_table)      \
diff --git a/src/factory.cc b/src/factory.cc
index 7c215c8..f2dcc47 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -1206,13 +1206,14 @@
   Handle<Context> context(isolate()->native_context());
   Handle<SharedFunctionInfo> info =
       NewSharedFunctionInfo(name, code, map->is_constructor());
-  DCHECK(is_sloppy(info->language_mode()) &&
-         (map.is_identical_to(isolate()->sloppy_function_map()) ||
-          map.is_identical_to(
-              isolate()->sloppy_function_without_prototype_map()) ||
-          map.is_identical_to(
-              isolate()->sloppy_function_with_readonly_prototype_map()) ||
-          map.is_identical_to(isolate()->strict_function_map())));
+  DCHECK(is_sloppy(info->language_mode()));
+  DCHECK(
+      map.is_identical_to(isolate()->sloppy_function_map()) ||
+      map.is_identical_to(isolate()->sloppy_function_without_prototype_map()) ||
+      map.is_identical_to(
+          isolate()->sloppy_function_with_readonly_prototype_map()) ||
+      map.is_identical_to(isolate()->strict_function_map()) ||
+      map.is_identical_to(isolate()->proxy_function_map()));
   return NewFunction(map, info, context);
 }
 
@@ -1941,7 +1942,16 @@
 Handle<JSProxy> Factory::NewJSProxy(Handle<JSReceiver> target,
                                     Handle<JSReceiver> handler) {
   // Allocate the proxy object.
-  Handle<Map> map(isolate()->proxy_function()->initial_map());
+  Handle<Map> map;
+  if (target->IsCallable()) {
+    if (target->IsConstructor()) {
+      map = Handle<Map>(isolate()->proxy_constructor_map());
+    } else {
+      map = Handle<Map>(isolate()->proxy_callable_map());
+    }
+  } else {
+    map = Handle<Map>(isolate()->proxy_map());
+  }
   DCHECK(map->prototype()->IsNull());
   Handle<JSProxy> result = New<JSProxy>(map, NEW_SPACE);
   result->set_target(*target);
diff --git a/src/factory.h b/src/factory.h
index a456fa2..afa05bd 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -517,6 +517,8 @@
                                  Handle<Code> code,
                                  InstanceType type,
                                  int instance_size);
+  Handle<JSFunction> NewFunction(Handle<Map> map, Handle<String> name,
+                                 MaybeHandle<Code> maybe_code);
 
   // Create a serialized scope info.
   Handle<ScopeInfo> NewScopeInfo(int length);
@@ -695,10 +697,6 @@
                                  Handle<SharedFunctionInfo> info,
                                  Handle<Context> context,
                                  PretenureFlag pretenure = TENURED);
-
-  Handle<JSFunction> NewFunction(Handle<Map> map,
-                                 Handle<String> name,
-                                 MaybeHandle<Code> maybe_code);
 };
 
 }  // namespace internal
diff --git a/src/heap/heap.h b/src/heap/heap.h
index 331c5a6..0135bf4 100644
--- a/src/heap/heap.h
+++ b/src/heap/heap.h
@@ -291,6 +291,7 @@
   V(Promise_string, "Promise")                                   \
   V(proto_string, "__proto__")                                   \
   V(prototype_string, "prototype")                               \
+  V(Proxy_string, "Proxy")                                       \
   V(query_colon_string, "(?:)")                                  \
   V(RegExp_string, "RegExp")                                     \
   V(setPrototypeOf_string, "setPrototypeOf")                     \
diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc
index b7cb183..5e897de 100644
--- a/src/ia32/builtins-ia32.cc
+++ b/src/ia32/builtins-ia32.cc
@@ -1542,8 +1542,16 @@
   __ CmpInstanceType(ecx, JS_PROXY_TYPE);
   __ j(not_equal, &non_function);
 
-  // 1. Call to Proxy.
-  // TODO(neis): Implement [[Call]] on proxies.
+  // 1. Runtime fallback for Proxy [[Call]].
+  __ PopReturnAddressTo(ecx);
+  __ Push(edi);
+  __ PushReturnAddressFrom(ecx);
+  // Increase the arguments size to include the pushed function and the
+  // existing receiver on the stack.
+  __ add(eax, Immediate(2));
+  // Tail-call to the runtime.
+  __ JumpToExternalReference(
+      ExternalReference(Runtime::kJSProxyCall, masm->isolate()));
 
   // 2. Call to something else, which might have a [[Call]] internal method (if
   // not we raise an exception).
diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc
index a617ff8..9d1473d 100644
--- a/src/mips/builtins-mips.cc
+++ b/src/mips/builtins-mips.cc
@@ -1694,8 +1694,14 @@
           RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE));
   __ Branch(&non_function, ne, t2, Operand(JS_PROXY_TYPE));
 
-  // 1. Call Proxy.
-  // TODO(neis): implement call on Proxy
+  // 1. Runtime fallback for Proxy [[Call]].
+  __ Push(a1);
+  // Increase the arguments size to include the pushed function and the
+  // existing receiver on the stack.
+  __ Addu(a0, a0, 2);
+  // Tail-call to the runtime.
+  __ JumpToExternalReference(
+      ExternalReference(Runtime::kJSProxyCall, masm->isolate()));
 
   // 2. Call to something else, which might have a [[Call]] internal method (if
   // not we raise an exception).
diff --git a/src/mips64/builtins-mips64.cc b/src/mips64/builtins-mips64.cc
index 357a70d..59b5e22 100644
--- a/src/mips64/builtins-mips64.cc
+++ b/src/mips64/builtins-mips64.cc
@@ -1686,8 +1686,14 @@
           RelocInfo::CODE_TARGET, eq, t2, Operand(JS_FUNCTION_TYPE));
   __ Branch(&non_function, ne, t2, Operand(JS_PROXY_TYPE));
 
-  // 1. Call to function proxy.
-  // TODO(neis): Implement [[Call]] on proxies.
+  // 1. Runtime fallback for Proxy [[Call]].
+  __ Push(a1);
+  // Increase the arguments size to include the pushed function and the
+  // existing receiver on the stack.
+  __ Daddu(a0, a0, 2);
+  // Tail-call to the runtime.
+  __ JumpToExternalReference(
+      ExternalReference(Runtime::kJSProxyCall, masm->isolate()));
 
   // 2. Call to something else, which might have a [[Call]] internal method (if
   // not we raise an exception).
diff --git a/src/objects.cc b/src/objects.cc
index 973c28b..e04d099 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -997,7 +997,7 @@
   if (is_extensible.FromJust()) return handler_proto;
   // 11. Let targetProto be ? target.[[GetPrototypeOf]]().
   Handle<Object> target_proto;
-  ASSIGN_RETURN_ON_EXCEPTION(isolate, handler_proto,
+  ASSIGN_RETURN_ON_EXCEPTION(isolate, target_proto,
                              Object::GetPrototype(isolate, target), Object);
   // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError.
   if (!handler_proto->SameValue(*target_proto)) {
diff --git a/src/ppc/builtins-ppc.cc b/src/ppc/builtins-ppc.cc
index 22382d6..ff212f0 100644
--- a/src/ppc/builtins-ppc.cc
+++ b/src/ppc/builtins-ppc.cc
@@ -1698,8 +1698,14 @@
   __ cmpi(r8, Operand(JS_PROXY_TYPE));
   __ bne(&non_function);
 
-  // 1. Call to proxy.
-  // TODO(neis): Implement [[Call]] on proxies.
+  // 1. Runtime fallback for Proxy [[Call]].
+  __ Push(r4);
+  // Increase the arguments size to include the pushed function and the
+  // existing receiver on the stack.
+  __ addi(r3, r3, Operand(2));
+  // Tail-call to the runtime.
+  __ JumpToExternalReference(
+      ExternalReference(Runtime::kJSProxyCall, masm->isolate()));
 
   // 2. Call to something else, which might have a [[Call]] internal method (if
   // not we raise an exception).
diff --git a/src/runtime/runtime-proxy.cc b/src/runtime/runtime-proxy.cc
index 52441e9..c501893 100644
--- a/src/runtime/runtime-proxy.cc
+++ b/src/runtime/runtime-proxy.cc
@@ -5,13 +5,73 @@
 #include "src/runtime/runtime-utils.h"
 
 #include "src/arguments.h"
+#include "src/elements.h"
 #include "src/factory.h"
+#include "src/isolate-inl.h"
 #include "src/objects-inl.h"
 
 namespace v8 {
 namespace internal {
 
 
+// ES6 9.5.13 [[Call]] (thisArgument, argumentsList)
+RUNTIME_FUNCTION(Runtime_JSProxyCall) {
+  HandleScope scope(isolate);
+  DCHECK_LE(2, args.length());
+  // thisArgument == receiver
+  CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0);
+  CONVERT_ARG_HANDLE_CHECKED(JSProxy, proxy, args.length() - 1);
+  Handle<String> trap_name = isolate->factory()->apply_string();
+  // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
+  Handle<Object> handler(proxy->handler(), isolate);
+  // 2. If handler is null, throw a TypeError exception.
+  if (proxy->IsRevoked()) {
+    THROW_NEW_ERROR_RETURN_FAILURE(
+        isolate, NewTypeError(MessageTemplate::kProxyRevoked, trap_name));
+  }
+  // 3. Assert: Type(handler) is Object.
+  DCHECK(handler->IsJSReceiver());
+  // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
+  Handle<JSReceiver> target(proxy->target(), isolate);
+  // 5. Let trap be ? GetMethod(handler, "apply").
+  Handle<Object> trap;
+  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+      isolate, trap,
+      Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name));
+  // 6. If trap is undefined, then
+  int const arguments_length = args.length() - 2;
+  if (trap->IsUndefined()) {
+    // 6.a. Return Call(target, thisArgument, argumentsList).
+    ScopedVector<Handle<Object>> argv(arguments_length);
+    for (int i = 0; i < arguments_length; ++i) {
+      argv[i] = args.at<Object>(i + 1);
+    }
+    Handle<Object> result;
+    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+        isolate, result, Execution::Call(isolate, target, receiver,
+                                         arguments_length, argv.start()));
+    return *result;
+  }
+  // 7. Let argArray be CreateArrayFromList(argumentsList).
+  Handle<JSArray> arg_array = isolate->factory()->NewJSArray(
+      FAST_ELEMENTS, arguments_length, arguments_length);
+  ElementsAccessor* accessor = arg_array->GetElementsAccessor();
+  {
+    DisallowHeapAllocation no_gc;
+    FixedArrayBase* elements = arg_array->elements();
+    for (int i = 0; i < arguments_length; i++) {
+      accessor->Set(elements, i, args[i + 1]);
+    }
+  }
+  // 8. Return Call(trap, handler, «target, thisArgument, argArray»).
+  Handle<Object> trap_result;
+  Handle<Object> trap_args[] = {target, receiver, arg_array};
+  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+      isolate, trap_result,
+      Execution::Call(isolate, trap, handler, arraysize(trap_args), trap_args));
+  return *trap_result;
+}
+
 
 RUNTIME_FUNCTION(Runtime_IsJSProxy) {
   SealHandleScope shs(isolate);
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index 3837e97..633fcc4 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -541,6 +541,7 @@
 
 #define FOR_EACH_INTRINSIC_PROXY(F) \
   F(IsJSProxy, 1, 1)                \
+  F(JSProxyCall, -1 /* >= 2 */, 1)   \
   F(GetHandler, 1, 1)               \
   F(RevokeProxy, 1, 1)
 
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
index 1a6ee7c..31237d8 100644
--- a/src/x64/builtins-x64.cc
+++ b/src/x64/builtins-x64.cc
@@ -1745,8 +1745,16 @@
   __ CmpInstanceType(rcx, JS_PROXY_TYPE);
   __ j(not_equal, &non_function);
 
-  // 1. Call to function proxy.
-  // TODO(neis): Implement [[Call]] on proxies.
+  // 1. Runtime fallback for Proxy [[Call]].
+  __ PopReturnAddressTo(kScratchRegister);
+  __ Push(rdi);
+  __ PushReturnAddressFrom(kScratchRegister);
+  // Increase the arguments size to include the pushed function and the
+  // existing receiver on the stack.
+  __ addp(rax, Immediate(2));
+  // Tail-call to the runtime.
+  __ JumpToExternalReference(
+      ExternalReference(Runtime::kJSProxyCall, masm->isolate()), 1);
 
   // 2. Call to something else, which might have a [[Call]] internal method (if
   // not we raise an exception).
diff --git a/src/x87/builtins-x87.cc b/src/x87/builtins-x87.cc
index 130b616..4418d87 100644
--- a/src/x87/builtins-x87.cc
+++ b/src/x87/builtins-x87.cc
@@ -1542,8 +1542,16 @@
   __ CmpInstanceType(ecx, JS_PROXY_TYPE);
   __ j(not_equal, &non_function);
 
-  // 1. Call to function proxy.
-  // TODO(neis): Implement [[Call]] on proxies.
+  // 1. Runtime fallback for Proxy [[Call]].
+  __ PopReturnAddressTo(ecx);
+  __ Push(edi);
+  __ PushReturnAddressFrom(ecx);
+  // Increase the arguments size to include the pushed function and the
+  // existing receiver on the stack.
+  __ addp(eax, Immediate(2));
+  // Tail-call to the runtime.
+  __ JumpToExternalReference(
+      ExternalReference(Runtime::kJSProxyCall, masm->isolate()));
 
   // 2. Call to something else, which might have a [[Call]] internal method (if
   // not we raise an exception).
diff --git a/test/mjsunit/harmony/proxies-apply.js b/test/mjsunit/harmony/proxies-apply.js
new file mode 100644
index 0000000..4ddffe7
--- /dev/null
+++ b/test/mjsunit/harmony/proxies-apply.js
@@ -0,0 +1,89 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-proxies --harmony-reflect
+
+(function testNonCallable() {
+  var proxy = new Proxy({},{});
+  assertThrows(function(){ proxy() }, TypeError);
+
+  var proxy2 = new Proxy(proxy, {});
+  assertThrows(function(){ proxy2() }, TypeError);
+})();
+
+(function testCallProxyFallbackNoArguments() {
+  var called = false;
+  var target = function() {
+    called = true;
+  }
+  var proxy = new Proxy(target, {});
+  assertFalse(called);
+  proxy();
+  assertTrue(called);
+
+  called = false;
+  var proxy2 = new Proxy(proxy, {});
+  assertFalse(called);
+  proxy2();
+  assertTrue(called);
+})();
+
+(function testCallProxyFallback1Argument() {
+  var called = false;
+  var target = function(a) {
+    called = true;
+    assertEquals('1', a);
+  }
+  var proxy = new Proxy(target, {});
+  assertFalse(called);
+  proxy('1');
+  assertTrue(called);
+})();
+
+(function testCallProxyFallback2Arguments() {
+  var called = false;
+  var target = function(a, b) {
+    called = true;
+    assertEquals('1', a);
+    assertEquals('2', b);
+  }
+  var proxy = new Proxy(target, {});
+  assertFalse(called);
+  proxy('1', '2');
+  assertTrue(called);
+})();
+
+(function testCallProxyFallbackChangedReceiver() {
+  var apply_receiver = {receiver:true};
+  var seen_receiver = undefined;
+  var target = function() {
+    seen_receiver = this;
+  }
+  var proxy = new Proxy(target, {});
+  assertEquals(undefined, seen_receiver);
+  Reflect.apply(proxy, apply_receiver, [1,2,3,4]);
+  assertSame(apply_receiver, seen_receiver);
+})();
+
+(function testCallProxyTrap() {
+  var called_target = false;
+  var called_handler = false;
+  var target = function(a, b) {
+    called_target = true;
+    assertEquals(1, a);
+    assertEquals(2, b);
+  }
+  var handler = {
+    apply: function(target, this_arg, args) {
+      target.apply(this_arg, args);
+      called_handler = true;
+    }
+  }
+  var proxy = new Proxy(target, handler);
+  assertFalse(called_target);
+  assertFalse(called_handler);
+  Reflect.apply(proxy, {rec:1}, [1,2]);
+  assertTrue(called_target);
+  assertTrue(called_handler);
+})();