[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);
+})();