[runtime] [proxy] implementing [[Get]] trap.

BUG=v8:1543
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#32466}
This commit is contained in:
cbruni 2015-12-01 06:04:16 -08:00 committed by Commit bot
parent 1d735a2d2e
commit 7e8fa4b96a
5 changed files with 190 additions and 43 deletions

View File

@ -71,18 +71,6 @@ function DelegateCallAndConstruct(callTrap, constructTrap) {
}
}
function DerivedGetTrap(receiver, name) {
var desc = this.getPropertyDescriptor(name)
if (IS_UNDEFINED(desc)) { return desc }
if ('value' in desc) {
return desc.value
} else {
if (IS_UNDEFINED(desc.get)) { return desc.get }
// The proposal says: desc.get.call(receiver)
return %_Call(desc.get, receiver)
}
}
function DerivedSetTrap(receiver, name, val) {
var desc = this.getOwnPropertyDescriptor(name)
if (desc) {
@ -192,7 +180,6 @@ utils.Export(function(to) {
});
%InstallToContext([
"derived_get_trap", DerivedGetTrap,
"proxy_enumerate", ProxyEnumerate,
]);

View File

@ -691,8 +691,9 @@ MaybeHandle<Object> Object::GetProperty(LookupIterator* it,
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::JSPROXY:
return JSProxy::GetPropertyWithHandler(
it->GetHolder<JSProxy>(), it->GetReceiver(), it->GetName());
return JSProxy::GetProperty(it->isolate(), it->GetHolder<JSProxy>(),
it->GetName(), it->GetReceiver(),
language_mode);
case LookupIterator::INTERCEPTOR: {
bool done;
Handle<Object> result;
@ -717,6 +718,79 @@ MaybeHandle<Object> Object::GetProperty(LookupIterator* it,
}
// static
MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
Handle<JSProxy> proxy,
Handle<Name> name,
Handle<Object> receiver,
LanguageMode language_mode) {
Handle<Name> trap_name = isolate->factory()->get_string();
// 1. Assert: IsPropertyKey(P) is true.
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
Handle<Object> handler(proxy->handler(), isolate);
// 3. If handler is null, throw a TypeError exception.
if (proxy->IsRevoked()) {
THROW_NEW_ERROR(isolate,
NewTypeError(MessageTemplate::kProxyRevoked, trap_name),
Object);
}
// 4. Assert: Type(handler) is Object.
DCHECK(handler->IsJSReceiver());
DCHECK(proxy->target()->IsJSReceiver());
// 5. Let target be the value of the [[ProxyTarget]] internal slot of O.
Handle<JSReceiver> target(JSReceiver::cast(proxy->target()), isolate);
// 6. Let trap be ? GetMethod(handler, "get").
Handle<Object> trap;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap,
Object::GetMethod(Handle<JSReceiver>::cast(handler), trap_name), Object);
// 7. If trap is undefined, then
if (trap->IsUndefined()) {
// 7.a Return target.[[Get]](P, Receiver).
LookupIterator it =
LookupIterator::PropertyOrElement(isolate, receiver, name, target);
return Object::GetProperty(&it, language_mode);
}
// 8. Let trapResult be ? Call(trap, handler, «target, P, Receiver»).
Handle<Object> trap_result;
Handle<Object> args[] = {target, name, receiver};
ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result,
Execution::Call(isolate, trap, handler, arraysize(args), args), Object);
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
PropertyDescriptor target_desc;
bool target_found =
JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
if (isolate->has_pending_exception()) return MaybeHandle<Object>();
// 10. If targetDesc is not undefined, then
if (target_found) {
// 10.a. If IsDataDescriptor(targetDesc) and targetDesc.[[Configurable]] is
// false and targetDesc.[[Writable]] is false, then
// 10.a.i. If SameValue(trapResult, targetDesc.[[Value]]) is false,
// throw a TypeError exception.
bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) &&
!target_desc.configurable() &&
!target_desc.writable() &&
!trap_result->SameValue(*target_desc.value());
// 10.b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]]
// is false and targetDesc.[[Get]] is undefined, then
// 10.b.i. If trapResult is not undefined, throw a TypeError exception.
inconsistent =
inconsistent ||
(PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
!target_desc.configurable() && target_desc.get()->IsUndefined());
if (inconsistent) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kProxyTrapViolatesInvariant, trap_name),
Object);
}
}
// 11. Return trap_result
return trap_result;
}
Handle<Object> JSReceiver::GetDataProperty(Handle<JSReceiver> object,
Handle<Name> name) {
LookupIterator it(object, name,
@ -919,20 +993,6 @@ bool JSProxy::IsRevoked() {
}
MaybeHandle<Object> JSProxy::GetPropertyWithHandler(Handle<JSProxy> proxy,
Handle<Object> receiver,
Handle<Name> name) {
Isolate* isolate = proxy->GetIsolate();
// TODO(rossberg): adjust once there is a story for symbols vs proxies.
if (name->IsSymbol()) return isolate->factory()->undefined_value();
Handle<Object> args[] = { receiver, name };
return CallTrap(
proxy, "get", isolate->derived_get_trap(), arraysize(args), args);
}
MaybeHandle<Object> Object::GetPropertyWithAccessor(
LookupIterator* it, LanguageMode language_mode) {
Isolate* isolate = it->isolate();

View File

@ -9521,6 +9521,18 @@ class JSProxy: public JSReceiver {
Handle<JSProxy> proxy,
Handle<Name> name);
// ES6 9.5.8
MUST_USE_RESULT static MaybeHandle<Object> GetProperty(
Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name,
Handle<Object> receiver, LanguageMode language_mode);
// ES6 9.5.9
MUST_USE_RESULT static Maybe<bool> SetProperty(Handle<JSProxy> proxy,
Handle<Name> name,
Handle<Object> value,
Handle<Object> receiver,
LanguageMode language_mode);
// ES6 9.5.10 (when passed SLOPPY)
MUST_USE_RESULT static Maybe<bool> DeletePropertyOrElement(
Handle<JSProxy> proxy, Handle<Name> name, LanguageMode language_mode);
@ -9543,12 +9555,6 @@ class JSProxy: public JSReceiver {
MUST_USE_RESULT static Maybe<PropertyAttributes> GetPropertyAttributes(
LookupIterator* it);
MUST_USE_RESULT static Maybe<bool> SetProperty(Handle<JSProxy> proxy,
Handle<Name> name,
Handle<Object> value,
Handle<Object> receiver,
LanguageMode language_mode);
// Dispatched behavior.
DECLARE_PRINTER(JSProxy)
DECLARE_VERIFIER(JSProxy)

View File

@ -26,14 +26,13 @@ handler.getPrototypeOf = function() {
assertSame(Object.getPrototypeOf(proxy), target_prototype);
// Test with proxy target:
var proxy2 = new Proxy(proxy, {});
var proxy2 = new Proxy(proxy, {'handler':1});
assertSame(Object.getPrototypeOf(proxy2), target_prototype);
// Test with Proxy handler:
// TODO(neis,cbruni): Uncomment once the get trap works again.
// var proxy3_prototype = {};
// var handler_proxy = new Proxy({
// getPrototypeOf: function() { return proxy3_prototype }
// }, {});
// var proxy3 = new Proxy(target, handler_proxy);
// assertSame(Object.getPrototypeOf(proxy3), target_prototype);
var proxy3_prototype = {'proto3':true};
var handler_proxy = new Proxy({
getPrototypeOf: function() { return proxy3_prototype }
}, {});
var proxy3 = new Proxy(target, handler_proxy);
assertSame(Object.getPrototypeOf(proxy3), proxy3_prototype);

View File

@ -0,0 +1,95 @@
// 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
(function testBasicFunctionality() {
var target = {
target_one: 1,
property: "value"
};
var handler = {handler:1};
var proxy = new Proxy(target, handler);
assertEquals("value", proxy.property);
assertEquals(undefined, proxy.nothing);
assertEquals(undefined, proxy.handler);
handler.get = function() { return "value 2" };
assertEquals("value 2", proxy.property);
assertEquals("value 2", proxy.nothing);
assertEquals("value 2", proxy.handler);
var handler2 = new Proxy({get: function() { return "value 3" }},{});
var proxy2 = new Proxy(target, handler2);
assertEquals("value 3", proxy2.property);
assertEquals("value 3", proxy2.nothing);
assertEquals("value 3", proxy2.handler);
})();
(function testThrowOnGettingTrap() {
var handler = new Proxy({}, {get: function(){ throw Error() }});
var proxy = new Proxy({}, handler);
assertThrows("proxy.property", Error);
})();
(function testFallback() {
var target = {property:"value"};
var proxy = new Proxy(target, {});
assertEquals("value", proxy.property);
assertEquals(undefined, proxy.property2);
})();
(function testFallbackUndefinedTrap() {
var handler = new Proxy({}, {get: function(){ return undefined }});
var target = {property:"value"};
var proxy = new Proxy(target, handler);
assertEquals("value", proxy.property);
assertEquals(undefined, proxy.property2);
})();
(function testFailingInvariant() {
var target = {};
var handler = { get: function(){ return "value" }}
var proxy = new Proxy(target, handler);
assertEquals("value", proxy.property);
assertEquals("value", proxy.key);
assertEquals("value", proxy.key2);
assertEquals("value", proxy.key3);
// Define a non-configurable, non-writeable property on the target for
// which the handler will return a different value.
Object.defineProperty(target, "key", {
configurable: false,
writable: false,
value: "different value"
});
assertEquals("value", proxy.property);
assertThrows(function(){ proxy.key }, TypeError);
assertEquals("value", proxy.key2);
assertEquals("value", proxy.key3);
// Define a non-configurable getter on the target for which the handler
// will return a value, according to the spec we do not throw.
Object.defineProperty(target, "key2", {
configurable: false,
get: function() { return "different value" }
});
assertEquals("value", proxy.property);
assertThrows(function(){ proxy.key }, TypeError);
assertEquals("value", proxy.key2);
assertEquals("value", proxy.key3);
// Define a non-configurable setter without a corresponding getter on the
// target for which the handler will return a value.
Object.defineProperty(target, "key3", {
configurable: false,
set: function() { }
});
assertEquals("value", proxy.property);
assertThrows(function(){ proxy.key }, TypeError);
assertEquals("value", proxy.key2);
assertThrows(function(){ proxy.key3 }, TypeError);
})();