Commit d9e0a5a9 authored by cbruni's avatar cbruni Committed by Commit bot

[runtime] [proxy] Adding [[SetPrototypeOf]] trap.

LOG=N
BUG=v8:1543

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

Cr-Commit-Position: refs/heads/master@{#32471}
parent 82e6bed4
......@@ -189,7 +189,9 @@ class CallSite {
T(ProxyHandlerReturned, "Proxy handler % returned % from '%' trap") \
T(ProxyHandlerTrapMissing, "Proxy handler % has no '%' trap") \
T(ProxyHandlerTrapMustBeCallable, \
"Proxy handler %0 has non-callable '%' trap") \
"Proxy handler % has non-callable '%' trap") \
T(ProxySetPrototypeFailed, \
"Proxy handler returned false when setting prototype '%'") \
T(ProxyNonObjectPropNames, "Trap '%' returned non-object %") \
T(ProxyPreventExtensionsViolatesInvariant, \
"Trap 'preventExtensions' returned true but the proxy's target is " \
......
......@@ -14835,13 +14835,78 @@ Handle<Map> Map::TransitionToPrototype(Handle<Map> map,
Maybe<bool> JSReceiver::SetPrototype(Handle<JSReceiver> object,
Handle<Object> value, bool from_javascript,
ShouldThrow should_throw) {
if (!object->IsJSObject()) return Just(false);
// TODO(neis): Deal with proxies.
if (object->IsJSProxy()) {
return JSProxy::SetPrototype(Handle<JSProxy>::cast(object), value,
from_javascript, should_throw);
}
return JSObject::SetPrototype(Handle<JSObject>::cast(object), value,
from_javascript, should_throw);
}
// ES6: 9.5.2 [[SetPrototypeOf]] (V)
// static
Maybe<bool> JSProxy::SetPrototype(Handle<JSProxy> proxy, Handle<Object> value,
bool from_javascript,
ShouldThrow should_throw) {
Isolate* isolate = proxy->GetIsolate();
Handle<JSReceiver> handle;
Handle<Name> trap_name = isolate->factory()->setPrototypeOf_string();
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
DCHECK(value->IsJSReceiver() || value->IsNull());
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
Handle<Object> raw_handler(proxy->handler(), isolate);
// 3. If handler is null, throw a TypeError exception.
// 4. Assert: Type(handler) is Object.
if (proxy->IsRevoked()) {
DCHECK(raw_handler->IsNull());
DCHECK(proxy->target()->IsNull());
isolate->Throw(
*isolate->factory()->NewTypeError(MessageTemplate::kProxyRevoked));
return Nothing<bool>();
}
Handle<JSReceiver> handler = Handle<JSReceiver>::cast(raw_handler);
// 5. Let target be the value of the [[ProxyTarget]] internal slot.
Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
// 6. Let trap be ? GetMethod(handler, "getPrototypeOf").
Handle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, trap, Object::GetMethod(handler, trap_name), Nothing<bool>());
// 7. If trap is undefined, then return target.[[SetPrototypeOf]]().
if (trap->IsUndefined()) {
return JSReceiver::SetPrototype(target, value, from_javascript,
should_throw);
}
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, «target, V»)).
Handle<Object> argv[] = {target, value};
Handle<Object> trap_result;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, trap_result,
Execution::Call(isolate, trap, handler, arraysize(argv), argv),
Nothing<bool>());
bool bool_trap_result = trap_result->BooleanValue();
// 9. Let extensibleTarget be ? IsExtensible(target).
Maybe<bool> is_extensible = JSReceiver::IsExtensible(target);
if (is_extensible.IsNothing()) return Nothing<bool>();
// 10. If extensibleTarget is true, return booleanTrapResult.
if (is_extensible.FromJust()) return Just(bool_trap_result);
// 11. Let targetProto be ? target.[[GetPrototypeOf]]().
Handle<Object> target_proto;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_proto,
Object::GetPrototype(isolate, target),
Nothing<bool>());
// 12. If booleanTrapResult is true and SameValue(V, targetProto) is false,
// throw a TypeError exception.
if (bool_trap_result && !value->SameValue(*target_proto)) {
isolate->Throw(*isolate->factory()->NewTypeError(
MessageTemplate::kProxyTrapViolatesInvariant, target));
return Nothing<bool>();
}
// 13. Return booleanTrapResult.
return Just(bool_trap_result);
}
Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object,
Handle<Object> value, bool from_javascript,
ShouldThrow should_throw) {
......
......@@ -9500,6 +9500,11 @@ class JSProxy: public JSReceiver {
// ES6 9.5.1
static MaybeHandle<Object> GetPrototype(Handle<JSProxy> receiver);
// ES6 9.5.2
MUST_USE_RESULT static Maybe<bool> SetPrototype(Handle<JSProxy> proxy,
Handle<Object> value,
bool from_javascript,
ShouldThrow should_throw);
// ES6 9.5.3
MUST_USE_RESULT static Maybe<bool> IsExtensible(Handle<JSProxy> proxy);
......
......@@ -183,9 +183,14 @@ RUNTIME_FUNCTION(Runtime_SetPrototype) {
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, obj, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1);
MAYBE_RETURN(
JSReceiver::SetPrototype(obj, prototype, true, Object::THROW_ON_ERROR),
isolate->heap()->exception());
Maybe<bool> status =
JSReceiver::SetPrototype(obj, prototype, true, Object::THROW_ON_ERROR);
if (status.IsNothing()) return isolate->heap()->exception();
if (!status.FromJust()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kProxySetPrototypeFailed, prototype));
}
return *obj;
}
......
// 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
var target = { target: 1 };
target.__proto__ = {};
var handler = { handler: 1 };
var proxy = new Proxy(target, handler);
assertSame(Object.getPrototypeOf(proxy), target.__proto__ );
assertThrows(function() { Object.setPrototypeOf(proxy, undefined) }, TypeError);
assertThrows(function() { Object.setPrototypeOf(proxy, 1) }, TypeError);
var prototype = [1];
assertSame(proxy, Object.setPrototypeOf(proxy, prototype));
assertSame(prototype, Object.getPrototypeOf(proxy));
assertSame(prototype, Object.getPrototypeOf(target));
handler.setPrototypeOf = function(target, proto) {
return false;
};
assertThrows(function() { Object.setPrototypeOf(proxy, {a:1}) }, TypeError);
handler.setPrototypeOf = function(target, proto) {
return undefined;
};
assertThrows(function() { Object.setPrototypeOf(proxy, {a:2}) }, TypeError);
handler.setPrototypeOf = function(proto) {};
assertThrows(function() { Object.setPrototypeOf(proxy, {a:3}) }, TypeError);
handler.setPrototypeOf = function(target, proto) {
throw Error();
};
assertThrows(function() { Object.setPrototypeOf(proxy, {a:4}) }, Error);
var seen_prototype;
var seen_target;
handler.setPrototypeOf = function(target, proto) {
seen_target = target;
seen_prototype = proto;
return true;
}
assertSame(Object.setPrototypeOf(proxy, {a:5}), proxy);
assertSame(target, seen_target);
assertEquals({a:5}, seen_prototype);
// Target is a Proxy:
var target2 = new Proxy(target, {});
var proxy2 = new Proxy(target2, {});
assertSame(Object.getPrototypeOf(proxy2), target.__proto__ );
prototype = [2,3];
assertSame(proxy2, Object.setPrototypeOf(proxy2, prototype));
assertSame(prototype, Object.getPrototypeOf(proxy2));
assertSame(prototype, Object.getPrototypeOf(target));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment