Remove the Proxy enumerate trap
In ES2016, the Proxy enumerate trap is removed. This patch changes for-in iteration on Proxies to use the ownKeys trap. Due to the clean organization of that code, the patch basically consists of deletions. R=adamk LOG=Y BUG=v8:4768 Review URL: https://codereview.chromium.org/1717893002 Cr-Commit-Position: refs/heads/master@{#34200}
This commit is contained in:
parent
7033ae511f
commit
579c01072d
@ -140,7 +140,6 @@ enum BindingFlags {
|
||||
V(PROMISE_REJECT_INDEX, JSFunction, promise_reject) \
|
||||
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
|
||||
V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
|
||||
V(PROXY_ENUMERATE_INDEX, JSFunction, proxy_enumerate) \
|
||||
V(RANGE_ERROR_FUNCTION_INDEX, JSFunction, range_error_function) \
|
||||
V(REFERENCE_ERROR_FUNCTION_INDEX, JSFunction, reference_error_function) \
|
||||
V(SET_ADD_METHOD_INDEX, JSFunction, set_add) \
|
||||
|
@ -12,11 +12,6 @@
|
||||
// Imports
|
||||
//
|
||||
var GlobalProxy = global.Proxy;
|
||||
var MakeTypeError;
|
||||
|
||||
utils.Import(function(from) {
|
||||
MakeTypeError = from.MakeTypeError;
|
||||
});
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@ -25,33 +20,6 @@ function ProxyCreateRevocable(target, handler) {
|
||||
return {proxy: p, revoke: () => %JSProxyRevoke(p)};
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Proxy Builtins
|
||||
|
||||
// Implements part of ES6 9.5.11 Proxy.[[Enumerate]]:
|
||||
// Call the trap, which should return an iterator, exhaust the iterator,
|
||||
// and return an array containing the values.
|
||||
function ProxyEnumerate(trap, handler, target) {
|
||||
// 7. Let trapResult be ? Call(trap, handler, «target»).
|
||||
var trap_result = %_Call(trap, handler, target);
|
||||
// 8. If Type(trapResult) is not Object, throw a TypeError exception.
|
||||
if (!IS_RECEIVER(trap_result)) {
|
||||
throw MakeTypeError(kProxyEnumerateNonObject);
|
||||
}
|
||||
// 9. Return trapResult.
|
||||
var result = [];
|
||||
for (var it = trap_result.next(); !it.done; it = trap_result.next()) {
|
||||
var key = it.value;
|
||||
// Not yet spec'ed as of 2015-11-25, but will be spec'ed soon:
|
||||
// If the iterator returns a non-string value, throw a TypeError.
|
||||
if (!IS_STRING(key)) {
|
||||
throw MakeTypeError(kProxyEnumerateNonString);
|
||||
}
|
||||
result.push(key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
//Set up non-enumerable properties of the Proxy object.
|
||||
@ -59,11 +27,4 @@ utils.InstallFunctions(GlobalProxy, DONT_ENUM, [
|
||||
"revocable", ProxyCreateRevocable
|
||||
]);
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Exports
|
||||
|
||||
%InstallToContext([
|
||||
"proxy_enumerate", ProxyEnumerate,
|
||||
]);
|
||||
|
||||
})
|
||||
|
@ -8680,15 +8680,9 @@ static Maybe<bool> GetKeys_Internal(Isolate* isolate,
|
||||
PrototypeIterator::GetCurrent<JSReceiver>(iter);
|
||||
Maybe<bool> result = Just(false); // Dummy initialization.
|
||||
if (current->IsJSProxy()) {
|
||||
if (type == OWN_ONLY) {
|
||||
result = JSProxy::OwnPropertyKeys(isolate, receiver,
|
||||
Handle<JSProxy>::cast(current),
|
||||
filter, accumulator);
|
||||
} else {
|
||||
DCHECK(type == INCLUDE_PROTOS);
|
||||
result = JSProxy::Enumerate(
|
||||
isolate, receiver, Handle<JSProxy>::cast(current), accumulator);
|
||||
}
|
||||
result = JSProxy::OwnPropertyKeys(isolate, receiver,
|
||||
Handle<JSProxy>::cast(current), filter,
|
||||
accumulator);
|
||||
} else {
|
||||
DCHECK(current->IsJSObject());
|
||||
result = GetKeysFromJSObject(isolate, receiver,
|
||||
@ -8702,54 +8696,6 @@ static Maybe<bool> GetKeys_Internal(Isolate* isolate,
|
||||
}
|
||||
|
||||
|
||||
// ES6 9.5.11
|
||||
// Returns false in case of exception.
|
||||
// static
|
||||
Maybe<bool> JSProxy::Enumerate(Isolate* isolate, Handle<JSReceiver> receiver,
|
||||
Handle<JSProxy> proxy,
|
||||
KeyAccumulator* accumulator) {
|
||||
STACK_CHECK(Nothing<bool>());
|
||||
// 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.
|
||||
// 3. Assert: Type(handler) is Object.
|
||||
if (proxy->IsRevoked()) {
|
||||
isolate->Throw(*isolate->factory()->NewTypeError(
|
||||
MessageTemplate::kProxyRevoked,
|
||||
isolate->factory()->enumerate_string()));
|
||||
return Nothing<bool>();
|
||||
}
|
||||
// 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, "enumerate").
|
||||
Handle<Object> trap;
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler),
|
||||
isolate->factory()->enumerate_string()),
|
||||
Nothing<bool>());
|
||||
// 6. If trap is undefined, then
|
||||
if (trap->IsUndefined()) {
|
||||
// 6a. Return target.[[Enumerate]]().
|
||||
return GetKeys_Internal(isolate, receiver, target, INCLUDE_PROTOS,
|
||||
ENUMERABLE_STRINGS, accumulator);
|
||||
}
|
||||
// The "proxy_enumerate" helper calls the trap (steps 7 - 9), which returns
|
||||
// a generator; it then iterates over that generator until it's exhausted
|
||||
// and returns an array containing the generated values.
|
||||
Handle<Object> trap_result_array;
|
||||
Handle<Object> args[] = {trap, handler, target};
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate, trap_result_array,
|
||||
Execution::Call(isolate, isolate->proxy_enumerate(),
|
||||
isolate->factory()->undefined_value(), arraysize(args),
|
||||
args),
|
||||
Nothing<bool>());
|
||||
accumulator->NextPrototype();
|
||||
accumulator->AddKeysFromProxy(Handle<JSObject>::cast(trap_result_array));
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
|
||||
// ES6 9.5.12
|
||||
// Returns |true| on success, |nothing| in case of exception.
|
||||
// static
|
||||
|
@ -9747,12 +9747,6 @@ class JSProxy: public JSReceiver {
|
||||
MUST_USE_RESULT static Maybe<bool> DeletePropertyOrElement(
|
||||
Handle<JSProxy> proxy, Handle<Name> name, LanguageMode language_mode);
|
||||
|
||||
// ES6 9.5.11
|
||||
MUST_USE_RESULT static Maybe<bool> Enumerate(Isolate* isolate,
|
||||
Handle<JSReceiver> receiver,
|
||||
Handle<JSProxy> proxy,
|
||||
KeyAccumulator* accumulator);
|
||||
|
||||
// ES6 9.5.12
|
||||
MUST_USE_RESULT static Maybe<bool> OwnPropertyKeys(
|
||||
Isolate* isolate, Handle<JSReceiver> receiver, Handle<JSProxy> proxy,
|
||||
|
@ -28,13 +28,14 @@ var deopt_has = false;
|
||||
var deopt_enum = false;
|
||||
|
||||
var handler = {
|
||||
enumerate(target) {
|
||||
ownKeys() {
|
||||
if (deopt_enum) {
|
||||
%DeoptimizeFunction(f2);
|
||||
deopt_enum = false;
|
||||
}
|
||||
return keys[Symbol.iterator]();
|
||||
return keys;
|
||||
},
|
||||
getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }},
|
||||
|
||||
has(target, k) {
|
||||
if (deopt_has) {
|
||||
@ -42,7 +43,7 @@ var handler = {
|
||||
deopt_has = false;
|
||||
}
|
||||
has_keys.push(k);
|
||||
return {value: 10, configurable: true, writable: false, enumerable: true};
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -136,14 +137,13 @@ function listener(event, exec_state, event_data, data) {
|
||||
}
|
||||
|
||||
var handler3 = {
|
||||
enumerate(target) {
|
||||
return ["a", "b"][Symbol.iterator]();
|
||||
},
|
||||
ownKeys() { return ["a", "b"] },
|
||||
getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }},
|
||||
|
||||
has(target, k) {
|
||||
if (k == "a") count++;
|
||||
if (x) %ScheduleBreak();
|
||||
return {value: 10, configurable: true, writable: false, enumerable: true};
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -36,13 +36,6 @@ var handler = {
|
||||
set: function(target, name, value) {
|
||||
return false; // l
|
||||
}, // m
|
||||
enumerate: function(target) {
|
||||
function* keys() { // n
|
||||
yield "foo"; // o
|
||||
yield "bar"; // p
|
||||
} // q
|
||||
return keys(); // r
|
||||
}, // s
|
||||
}
|
||||
|
||||
var proxy = new Proxy(target, handler);
|
||||
@ -52,9 +45,6 @@ debugger; // a
|
||||
var has = "step" in proxy; // b
|
||||
var get = proxy.step; // c
|
||||
proxy.step = 43; // d
|
||||
for (var i in proxy) { // e
|
||||
log.push(i); // f
|
||||
}
|
||||
|
||||
Debug.setListener(null); // g
|
||||
|
||||
@ -67,12 +57,5 @@ assertEquals([
|
||||
"b0", "h4b20", "i2b20", // [[Has]]
|
||||
"c0", "j4c15", "k2c15", // [[Get]]
|
||||
"d0", "l4d11", "m2d11", // [[Set]]
|
||||
"e14", "r4e14", "q4r11e14", "s2e14", // for-in [[Enumerate]]
|
||||
"o6e14", "q4e14", "p6e14", "q4e14", "q4e14", // exhaust iterator
|
||||
"e9", // for-in-body
|
||||
"h4e9","i2e9", // [[Has]] property
|
||||
"f2","foo", "e9", // for-in-body
|
||||
"h4e9","i2e9", // [[Has]]property
|
||||
"f2","bar", "e9", // for-in-body
|
||||
"g0"
|
||||
], log);
|
||||
|
@ -1,109 +0,0 @@
|
||||
// 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_one": 1
|
||||
};
|
||||
target.__proto__ = {
|
||||
"target_two": 2
|
||||
};
|
||||
var handler = {
|
||||
enumerate: function(target) {
|
||||
function* keys() {
|
||||
yield "foo";
|
||||
yield "bar";
|
||||
}
|
||||
return keys();
|
||||
},
|
||||
// For-in calls "has" on every iteration, so for TestForIn() below to
|
||||
// detect all results of the "enumerate" trap, "has" must return true.
|
||||
has: function(target, name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var proxy = new Proxy(target, handler);
|
||||
|
||||
function TestForIn(receiver, expected) {
|
||||
var result = [];
|
||||
for (var k in receiver) {
|
||||
result.push(k);
|
||||
}
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
TestForIn(proxy, ["foo", "bar"]);
|
||||
|
||||
// Test revoked proxy.
|
||||
var pair = Proxy.revocable(target, handler);
|
||||
TestForIn(pair.proxy, ["foo", "bar"]);
|
||||
pair.revoke();
|
||||
assertThrows(()=>{ TestForIn(pair.proxy, ["foo", "bar"]) }, TypeError);
|
||||
|
||||
// Properly call traps on proxies on the prototype chain.
|
||||
var receiver = {
|
||||
"receiver_one": 1
|
||||
};
|
||||
receiver.__proto__ = proxy;
|
||||
TestForIn(receiver, ["receiver_one", "foo", "bar"]);
|
||||
|
||||
// Fall through to default behavior when trap is undefined.
|
||||
handler.enumerate = undefined;
|
||||
TestForIn(proxy, ["target_one", "target_two"]);
|
||||
delete handler.enumerate;
|
||||
TestForIn(proxy, ["target_one", "target_two"]);
|
||||
|
||||
// Non-string keys must be filtered.
|
||||
function TestNonStringKey(key) {
|
||||
handler.enumerate = function(target) {
|
||||
function* keys() { yield key; }
|
||||
return keys();
|
||||
}
|
||||
assertThrows("for (var k in proxy) {}", TypeError);
|
||||
}
|
||||
|
||||
TestNonStringKey(1);
|
||||
TestNonStringKey(3.14);
|
||||
TestNonStringKey(Symbol("foo"));
|
||||
TestNonStringKey({bad: "value"});
|
||||
TestNonStringKey(null);
|
||||
TestNonStringKey(undefined);
|
||||
TestNonStringKey(true);
|
||||
|
||||
(function testProtoProxyEnumerate() {
|
||||
var keys = ['a', 'b', 'c', 'd'];
|
||||
var handler = {
|
||||
enumerate() { return keys[Symbol.iterator]() },
|
||||
has(target, key) { return false }
|
||||
};
|
||||
var proxy = new Proxy({}, handler);
|
||||
var seen_keys = [];
|
||||
for (var i in proxy) {
|
||||
seen_keys.push(i);
|
||||
}
|
||||
assertEquals([], seen_keys);
|
||||
|
||||
handler.has = function(target, key) { return true };
|
||||
for (var i in proxy) {
|
||||
seen_keys.push(i);
|
||||
}
|
||||
assertEquals(keys, seen_keys);
|
||||
|
||||
o = {__proto__:proxy};
|
||||
handler.has = function(target, key) { return false };
|
||||
seen_keys = [];
|
||||
for (var i in o) {
|
||||
seen_keys.push(i);
|
||||
}
|
||||
assertEquals([], seen_keys);
|
||||
|
||||
handler.has = function(target, key) { return true };
|
||||
seen_keys = [];
|
||||
for (var i in o) {
|
||||
seen_keys.push(i);
|
||||
}
|
||||
assertEquals(keys, seen_keys);
|
||||
})();
|
@ -27,21 +27,15 @@
|
||||
|
||||
// Flags: --harmony-proxies
|
||||
|
||||
|
||||
// Helper.
|
||||
|
||||
function TestWithProxies(test, x, y, z) {
|
||||
test(function(h){ return new Proxy({}, h) }, x, y, z)
|
||||
test(function(h) {
|
||||
return new Proxy(function() {}, h)
|
||||
}, x, y, z)
|
||||
}
|
||||
|
||||
|
||||
// Iterate over a proxy.
|
||||
|
||||
Array.prototype.values = function() { return this[Symbol.iterator]() }
|
||||
|
||||
function TestForIn(properties, handler) {
|
||||
TestWithProxies(TestForIn2, properties, handler)
|
||||
}
|
||||
@ -54,23 +48,18 @@ function TestForIn2(create, properties, handler) {
|
||||
}
|
||||
|
||||
TestForIn(["0", "a"], {
|
||||
enumerate() { return ["0", "a"].values() },
|
||||
has(target, property) { return true }
|
||||
ownKeys() { return ["0", "a"] },
|
||||
has(target, property) { return true },
|
||||
getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }}
|
||||
})
|
||||
|
||||
TestForIn(["null", "a"], {
|
||||
enumerate() { return this.enumerate2() },
|
||||
enumerate2() { return ["null", "a"].values() },
|
||||
has(target, property) { return true }
|
||||
ownKeys() { return this.enumerate() },
|
||||
enumerate() { return ["null", "a"] },
|
||||
has(target, property) { return true },
|
||||
getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }}
|
||||
})
|
||||
|
||||
TestForIn(["b", "a", "0", "c"], new Proxy({}, {
|
||||
get: function(pr, pk) {
|
||||
return function() { return ["b", "a", "0", "c"].values() }
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
|
||||
// Iterate over an object with a proxy prototype.
|
||||
|
||||
@ -94,19 +83,21 @@ function TestForInDerived2(create, properties, handler) {
|
||||
}
|
||||
|
||||
TestForInDerived(["0", "a"], {
|
||||
enumerate: function() { return ["0", "a"].values() },
|
||||
has: function(t, k) { return k == "0" || k == "a" }
|
||||
ownKeys: function() { return ["0", "a"] },
|
||||
has: function(t, k) { return k == "0" || k == "a" },
|
||||
getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }}
|
||||
})
|
||||
|
||||
TestForInDerived(["null", "a"], {
|
||||
enumerate: function() { return this.enumerate2() },
|
||||
enumerate2: function() { return ["null", "a"].values() },
|
||||
has: function(t, k) { return k == "null" || k == "a" }
|
||||
ownKeys: function() { return this.enumerate() },
|
||||
enumerate: function() { return ["null", "a"] },
|
||||
has: function(t, k) { return k == "null" || k == "a" },
|
||||
getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }}
|
||||
})
|
||||
|
||||
|
||||
|
||||
// Throw exception in enumerate trap.
|
||||
// Throw exception in ownKeys trap.
|
||||
|
||||
function TestForInThrow(handler) {
|
||||
TestWithProxies(TestForInThrow2, handler)
|
||||
@ -120,12 +111,12 @@ function TestForInThrow2(create, handler) {
|
||||
}
|
||||
|
||||
TestForInThrow({
|
||||
enumerate: function() { throw "myexn" }
|
||||
ownKeys: function() { throw "myexn" }
|
||||
})
|
||||
|
||||
TestForInThrow({
|
||||
enumerate: function() { return this.enumerate2() },
|
||||
enumerate2: function() { throw "myexn" }
|
||||
ownKeys: function() { return this.enumerate() },
|
||||
enumerate: function() { throw "myexn" }
|
||||
})
|
||||
|
||||
TestForInThrow(new Proxy({}, {
|
||||
@ -135,7 +126,7 @@ TestForInThrow(new Proxy({}, {
|
||||
}));
|
||||
|
||||
(function() {
|
||||
var p = new Proxy({}, {enumerate:function() { return ["0"].values(); }});
|
||||
var p = new Proxy({}, {ownKeys:function() { return ["0"]; }});
|
||||
var o = [0];
|
||||
o.__proto__ = p;
|
||||
var keys = [];
|
||||
|
Loading…
Reference in New Issue
Block a user