Bug 1216751 part 4. Implement forEach for iterable interfaces. r=qdot

This commit is contained in:
Boris Zbarsky 2016-02-17 22:58:04 -05:00
parent ac58157b15
commit 6f458fb1f3
4 changed files with 93 additions and 15 deletions

View File

@ -2289,7 +2289,7 @@ class MethodDefiner(PropertyDefiner):
maplikeOrSetlikeOrIterable and maplikeOrSetlikeOrIterable and
maplikeOrSetlikeOrIterable.isIterable() and maplikeOrSetlikeOrIterable.isIterable() and
maplikeOrSetlikeOrIterable.isValueIterator()): maplikeOrSetlikeOrIterable.isValueIterator()):
# Add our keys/values/entries # Add our keys/values/entries/forEach
self.regular.append({ self.regular.append({
"name": "keys", "name": "keys",
"methodInfo": False, "methodInfo": False,
@ -2317,6 +2317,15 @@ class MethodDefiner(PropertyDefiner):
"condition": PropertyDefiner.getControllingCondition(m, "condition": PropertyDefiner.getControllingCondition(m,
descriptor) descriptor)
}) })
self.regular.append({
"name": "forEach",
"methodInfo": False,
"selfHostedName": "ArrayForEach",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
if not static: if not static:
stringifier = descriptor.operations['Stringifier'] stringifier = descriptor.operations['Stringifier']
@ -15811,6 +15820,31 @@ class CGIterableMethodGenerator(CGGeneric):
using CGCallGenerator. using CGCallGenerator.
""" """
def __init__(self, descriptor, iterable, methodName): def __init__(self, descriptor, iterable, methodName):
if methodName == "forEach":
CGGeneric.__init__(self, fill(
"""
if (!JS::IsCallable(arg0)) {
ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "Argument 1 of ${ifaceName}.forEach");
return false;
}
JS::AutoValueArray<3> callArgs(cx);
callArgs[2].setObject(*obj);
JS::Rooted<JS::Value> ignoredReturnVal(cx);
for (size_t i = 0; i < self->GetIterableLength(); ++i) {
if (!ToJSValue(cx, self->GetValueAtIndex(i), callArgs[0])) {
return false;
}
if (!ToJSValue(cx, self->GetKeyAtIndex(i), callArgs[1])) {
return false;
}
if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs),
&ignoredReturnVal)) {
return false;
}
}
""",
ifaceName=descriptor.interface.identifier.name))
return
CGGeneric.__init__(self, fill( CGGeneric.__init__(self, fill(
""" """
typedef ${iterClass} itrType; typedef ${iterClass} itrType;

View File

@ -3617,6 +3617,17 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
deps.add(self.valueType) deps.add(self.valueType)
return deps return deps
def getForEachArguments(self):
return [IDLArgument(self.location,
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
"callback"),
BuiltinTypes[IDLBuiltinType.Types.object]),
IDLArgument(self.location,
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
"thisArg"),
BuiltinTypes[IDLBuiltinType.Types.any],
optional=True)]
# Iterable adds ES6 iterator style functions and traits # Iterable adds ES6 iterator style functions and traits
# (keys/values/entries/@@iterator) to an interface. # (keys/values/entries/@@iterator) to an interface.
class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase): class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
@ -3652,6 +3663,11 @@ class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
self.addMethod("values", members, False, self.iteratorType, self.addMethod("values", members, False, self.iteratorType,
affectsNothing=True, newObject=True) affectsNothing=True, newObject=True)
# void forEach(callback(valueType, keyType), optional any thisArg)
self.addMethod("forEach", members, False,
BuiltinTypes[IDLBuiltinType.Types.void],
self.getForEachArguments())
def isValueIterator(self): def isValueIterator(self):
return not self.isPairIterator() return not self.isPairIterator()
@ -3703,17 +3719,8 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
affectsNothing=True, isIteratorAlias=self.isSetlike()) affectsNothing=True, isIteratorAlias=self.isSetlike())
# void forEach(callback(valueType, keyType), thisVal) # void forEach(callback(valueType, keyType), thisVal)
foreachArguments = [IDLArgument(self.location,
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
"callback"),
BuiltinTypes[IDLBuiltinType.Types.object]),
IDLArgument(self.location,
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
"thisArg"),
BuiltinTypes[IDLBuiltinType.Types.any],
optional=True)]
self.addMethod("forEach", members, False, BuiltinTypes[IDLBuiltinType.Types.void], self.addMethod("forEach", members, False, BuiltinTypes[IDLBuiltinType.Types.void],
foreachArguments) self.getForEachArguments())
def getKeyArg(): def getKeyArg():
return IDLArgument(self.location, return IDLArgument(self.location,

View File

@ -45,8 +45,8 @@ def WebIDLTest(parser, harness):
prefix + " - Interface failed but not as a WebIDLError exception: %s" % e) prefix + " - Interface failed but not as a WebIDLError exception: %s" % e)
iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys", iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys",
"values"]] "values", "forEach"]]
setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has", "forEach"]] + setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has"]] +
[("__setlike", WebIDL.IDLMaplikeOrSetlike)] + [("__setlike", WebIDL.IDLMaplikeOrSetlike)] +
iterableMembers) iterableMembers)
setROMembers.extend([("size", WebIDL.IDLAttribute)]) setROMembers.extend([("size", WebIDL.IDLAttribute)])
@ -62,7 +62,7 @@ def WebIDLTest(parser, harness):
"__clear", "__clear",
"__delete"]] + "__delete"]] +
setRWMembers) setRWMembers)
mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has", "forEach"]] + mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has"]] +
[("__maplike", WebIDL.IDLMaplikeOrSetlike)] + [("__maplike", WebIDL.IDLMaplikeOrSetlike)] +
iterableMembers) iterableMembers)
mapROMembers.extend([("size", WebIDL.IDLAttribute)]) mapROMembers.extend([("size", WebIDL.IDLAttribute)])

View File

@ -14,7 +14,8 @@
base_properties = [["entries", "function", 0], base_properties = [["entries", "function", 0],
["keys", "function", 0], ["keys", "function", 0],
["values", "function", 0]] ["values", "function", 0],
["forEach", "function", 1]]
var testExistence = function testExistence(prefix, obj, properties) { var testExistence = function testExistence(prefix, obj, properties) {
for (var [name, type, args] of properties) { for (var [name, type, args] of properties) {
// Properties are somewhere up the proto chain, hasOwnProperty won't work // Properties are somewhere up the proto chain, hasOwnProperty won't work
@ -58,6 +59,8 @@
"IterableSingle: Should be using %ArrayIterator% for 'entries'"); "IterableSingle: Should be using %ArrayIterator% for 'entries'");
is(itr.values, itr[Symbol.iterator], is(itr.values, itr[Symbol.iterator],
"IterableSingle: Should be using @@iterator for 'values'"); "IterableSingle: Should be using @@iterator for 'values'");
is(itr.forEach, Array.prototype.forEach,
"IterableSingle: Should be using %ArrayIterator% for 'forEach'");
var keys = [...itr.keys()]; var keys = [...itr.keys()];
var values = [...itr.values()]; var values = [...itr.values()];
var entries = [...itr.entries()]; var entries = [...itr.entries()];
@ -81,6 +84,23 @@
is(entry.value[1], entries[i][1], is(entry.value[1], entries[i][1],
"IterableSingle: Entry iterator value 1 should match destructuring " + i); "IterableSingle: Entry iterator value 1 should match destructuring " + i);
} }
var callsToForEachCallback = 0;
var thisArg = {};
itr.forEach(function(value, index, obj) {
is(index, callsToForEachCallback,
`IterableSingle: Should have the right index at ${callsToForEachCallback} calls to forEach callback`);
is(value, values[index],
`IterableSingle: Should have the right value at ${callsToForEachCallback} calls to forEach callback`);
is(this, thisArg,
"IterableSingle: Should have the right this value for forEach callback");
is(obj, itr,
"IterableSingle: Should have the right third arg for forEach callback");
++callsToForEachCallback;
}, thisArg);
is(callsToForEachCallback, 3,
"IterableSingle: Should have right total number of calls to forEach callback");
var key = key_itr.next(); var key = key_itr.next();
var value = value_itr.next(); var value = value_itr.next();
var entry = entries_itr.next(); var entry = entries_itr.next();
@ -124,6 +144,23 @@
is(entry.value[1], entries[i][1], is(entry.value[1], entries[i][1],
"IterableDouble: Entry iterator value 1 should match destructuring " + i); "IterableDouble: Entry iterator value 1 should match destructuring " + i);
} }
callsToForEachCallback = 0;
thisArg = {};
itr.forEach(function(value, key, obj) {
is(key, keys[callsToForEachCallback],
`IterableDouble: Should have the right key at ${callsToForEachCallback} calls to forEach callback`);
is(value, values[callsToForEachCallback],
`IterableDouble: Should have the right value at ${callsToForEachCallback} calls to forEach callback`);
is(this, thisArg,
"IterableDouble: Should have the right this value for forEach callback");
is(obj, itr,
"IterableSingle: Should have the right third arg for forEach callback");
++callsToForEachCallback;
}, thisArg);
is(callsToForEachCallback, 3,
"IterableDouble: Should have right total number of calls to forEach callback");
var key = key_itr.next(); var key = key_itr.next();
var value = value_itr.next(); var value = value_itr.next();
var entry = entries_itr.next() var entry = entries_itr.next()