[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:
parent
1d735a2d2e
commit
7e8fa4b96a
@ -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,
|
||||
]);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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);
|
95
test/mjsunit/harmony/proxies-get.js
Normal file
95
test/mjsunit/harmony/proxies-get.js
Normal 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);
|
||||
})();
|
Loading…
Reference in New Issue
Block a user