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

BUG=v8:1543
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#32675}
This commit is contained in:
cbruni 2015-12-08 08:04:08 -08:00 committed by Commit bot
parent cd96d74124
commit 7299412473
18 changed files with 309 additions and 44 deletions

View File

@ -1685,8 +1685,14 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ 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).

View File

@ -1677,8 +1677,14 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ 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).

View File

@ -218,6 +218,7 @@ class Genesis BASE_EMBEDDED {
void InstallBuiltinFunctionIds();
void InstallExperimentalBuiltinFunctionIds();
void InitializeNormalizedMapCaches();
void InstallJSProxyMaps();
enum ExtensionTraversalState {
UNVISITED, VISITED, INSTALLED
@ -362,6 +363,20 @@ void Bootstrapper::DetachGlobal(Handle<Context> env) {
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 @@ Handle<JSFunction> InstallFunction(Handle<JSObject> target, Handle<Name> name,
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::InitializeGlobal_harmony_simd() {
}
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);
}

View File

@ -1799,6 +1799,7 @@ BUILTIN(ProxyConstructor) {
// 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);

View File

@ -220,7 +220,11 @@ enum BindingFlags {
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) \

View File

@ -1206,13 +1206,14 @@ Handle<JSFunction> Factory::NewFunction(Handle<Map> map,
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<JSDataView> Factory::NewJSDataView(Handle<JSArrayBuffer> buffer,
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);

View File

@ -517,6 +517,8 @@ class Factory final {
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 @@ class Factory final {
Handle<SharedFunctionInfo> info,
Handle<Context> context,
PretenureFlag pretenure = TENURED);
Handle<JSFunction> NewFunction(Handle<Map> map,
Handle<String> name,
MaybeHandle<Code> maybe_code);
};
} // namespace internal

View File

@ -291,6 +291,7 @@ namespace internal {
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") \

View File

@ -1542,8 +1542,16 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ 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).

View File

@ -1694,8 +1694,14 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
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).

View File

@ -1686,8 +1686,14 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
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).

View File

@ -997,7 +997,7 @@ MaybeHandle<Object> JSProxy::GetPrototype(Handle<JSProxy> proxy) {
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)) {

View File

@ -1698,8 +1698,14 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ 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).

View File

@ -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);

View File

@ -541,6 +541,7 @@ namespace internal {
#define FOR_EACH_INTRINSIC_PROXY(F) \
F(IsJSProxy, 1, 1) \
F(JSProxyCall, -1 /* >= 2 */, 1) \
F(GetHandler, 1, 1) \
F(RevokeProxy, 1, 1)

View File

@ -1745,8 +1745,16 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ 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).

View File

@ -1542,8 +1542,16 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
__ 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).

View File

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