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:
littledan 2016-02-22 13:09:49 -08:00 committed by Commit bot
parent 7033ae511f
commit 579c01072d
8 changed files with 29 additions and 264 deletions

View File

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

View File

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

View File

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

View File

@ -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,

View File

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

View File

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

View File

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

View File

@ -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 = [];