#488: basic support for DOM iterables, {NodeList,DOMTokenList}.forEach M1216751 M1290636

This commit is contained in:
Cameron Kaiser 2018-03-20 18:06:26 -07:00
parent 83c11df480
commit 9470d4fb94
6 changed files with 254 additions and 229 deletions

View File

@ -1136,11 +1136,13 @@ class CGHeaders(CGWrapper):
if desc.interface.maplikeOrSetlikeOrIterable: if desc.interface.maplikeOrSetlikeOrIterable:
# We need ToJSValue.h for maplike/setlike type conversions # We need ToJSValue.h for maplike/setlike type conversions
bindingHeaders.add("mozilla/dom/ToJSValue.h") bindingHeaders.add("mozilla/dom/ToJSValue.h")
# Add headers for the key and value types of the maplike, since # Add headers for the key and value types of the
# they'll be needed for convenience functions # maplike/setlike/iterable, since they'll be needed for
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType, # convenience functions
desc, None)) if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
if desc.interface.maplikeOrSetlikeOrIterable.valueType: addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
desc, None))
if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType, addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType,
desc, None)) desc, None))
@ -2269,34 +2271,50 @@ class MethodDefiner(PropertyDefiner):
"condition": MemberCondition() "condition": MemberCondition()
}) })
# Generate the maplike/setlike iterator, if one wasn't already # Generate the keys/values/entries aliases for value iterables.
# generated by a method. If we already have an @@iterator symbol, fail. maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
if descriptor.interface.maplikeOrSetlikeOrIterable: if (not static and
if hasIterator(methods, self.regular): not unforgeable and
raise TypeError("Cannot have maplike/setlike/iterable interface with " maplikeOrSetlikeOrIterable and
"other members that generate @@iterator " maplikeOrSetlikeOrIterable.isIterable() and
"on interface %s, such as indexed getters " maplikeOrSetlikeOrIterable.isValueIterator()):
"or aliased functions." % # Add our keys/values/entries/forEach
self.descriptor.interface.identifier.name) self.regular.append({
for m in methods: "name": "keys",
if (m.isMaplikeOrSetlikeOrIterableMethod() and "methodInfo": False,
(((m.maplikeOrSetlikeOrIterable.isMaplike() or "selfHostedName": "ArrayKeys",
(m.maplikeOrSetlikeOrIterable.isIterable() and "length": 0,
m.maplikeOrSetlikeOrIterable.hasValueType())) and "flags": "JSPROP_ENUMERATE",
m.identifier.name == "entries") or "condition": PropertyDefiner.getControllingCondition(m,
(((m.maplikeOrSetlikeOrIterable.isSetlike() or descriptor)
(m.maplikeOrSetlikeOrIterable.isIterable() and })
not m.maplikeOrSetlikeOrIterable.hasValueType()))) and self.regular.append({
m.identifier.name == "values"))): "name": "values",
self.regular.append({ "methodInfo": False,
"name": "@@iterator", "selfHostedName": "ArrayValues",
"methodName": m.identifier.name, "length": 0,
"length": methodLength(m), "flags": "JSPROP_ENUMERATE",
"flags": "0", "condition": PropertyDefiner.getControllingCondition(m,
"condition": PropertyDefiner.getControllingCondition(m, descriptor)
descriptor), })
}) self.regular.append({
break "name": "entries",
"methodInfo": False,
"selfHostedName": "ArrayEntries",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
self.regular.append({
"name": "forEach",
"methodInfo": False,
"selfHostedName": "ArrayForEach",
"length": 1,
"flags": "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
if not static: if not static:
stringifier = descriptor.operations['Stringifier'] stringifier = descriptor.operations['Stringifier']
@ -13008,8 +13026,9 @@ class CGForwardDeclarations(CGWrapper):
# arguments to helper functions, and they'll need to be forward # arguments to helper functions, and they'll need to be forward
# declared in the header. # declared in the header.
if d.interface.maplikeOrSetlikeOrIterable: if d.interface.maplikeOrSetlikeOrIterable:
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType, if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
config) builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
config)
if d.interface.maplikeOrSetlikeOrIterable.hasValueType(): if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.valueType, builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.valueType,
config) config)
@ -15735,6 +15754,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

@ -893,8 +893,5 @@ def getAllTypes(descriptors, dictionaries, callbacks):
def iteratorNativeType(descriptor): def iteratorNativeType(descriptor):
assert descriptor.interface.isIterable() assert descriptor.interface.isIterable()
iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
if iterableDecl.valueType is None: assert iterableDecl.isPairIterator()
iterClass = "OneTypeIterableIterator" return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType
else:
iterClass = "TwoTypeIterableIterator"
return "mozilla::dom::%s<%s>" % (iterClass, descriptor.nativeType)

View File

@ -6,19 +6,17 @@
/** /**
* The IterableIterator class is used for WebIDL interfaces that have a * The IterableIterator class is used for WebIDL interfaces that have a
* iterable<> member defined. It handles the ES6 Iterator-like functions that * iterable<> member defined with two types (so a pair iterator). It handles
* are generated for the iterable interface. * the ES6 Iterator-like functions that are generated for the iterable
* interface.
* *
* For iterable interfaces, the implementation class will need to * For iterable interfaces with a pair iterator, the implementation class will
* implement these two functions: * need to implement these two functions:
* *
* - size_t GetIterableLength() * - size_t GetIterableLength()
* - Returns the number of elements available to iterate over * - Returns the number of elements available to iterate over
* - [type] GetValueAtIndex(size_t index) * - [type] GetValueAtIndex(size_t index)
* - Returns the value at the requested index. * - Returns the value at the requested index.
*
* If this is a two-type iterator, then the implementation class will also need to implement:
*
* - [type] GetKeyAtIndex(size_t index) * - [type] GetKeyAtIndex(size_t index)
* - Returns the key at the requested index * - Returns the key at the requested index
* *
@ -60,13 +58,77 @@ protected:
}; };
template <typename T> template <typename T>
class IterableIterator : public IterableIteratorBase class IterableIterator final : public IterableIteratorBase
{ {
public: public:
explicit IterableIterator(T* aIterableObj) typedef bool (*WrapFunc)(JSContext* aCx,
IterableIterator<T>* aObject,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
explicit IterableIterator(T* aIterableObj,
IterableIteratorType aIteratorType,
WrapFunc aWrapFunc)
: mIterableObj(aIterableObj) : mIterableObj(aIterableObj)
, mIteratorType(aIteratorType)
, mWrapFunc(aWrapFunc)
, mIndex(0)
{ {
MOZ_ASSERT(mIterableObj); MOZ_ASSERT(mIterableObj);
MOZ_ASSERT(mWrapFunc);
}
void
Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
{
JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
if (mIndex >= this->mIterableObj->GetIterableLength()) {
DictReturn(aCx, aResult, true, value, aRv);
return;
}
switch (mIteratorType) {
case IterableIteratorType::Keys:
{
if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
DictReturn(aCx, aResult, false, value, aRv);
break;
}
case IterableIteratorType::Values:
{
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
DictReturn(aCx, aResult, false, value, aRv);
break;
}
case IterableIteratorType::Entries:
{
JS::Rooted<JS::Value> key(aCx);
if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &key)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
KeyAndValueReturn(aCx, key, value, aResult, aRv);
break;
}
default:
MOZ_CRASH("Invalid iterator type!");
}
++mIndex;
}
bool
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
{
return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
} }
protected: protected:
@ -128,161 +190,6 @@ protected:
// Binding Implementation object that we're iterating over. // Binding Implementation object that we're iterating over.
RefPtr<T> mIterableObj; RefPtr<T> mIterableObj;
};
template<typename T>
class OneTypeIterableIterator final : public IterableIterator<T>
{
public:
typedef typename IterableIterator<T>::IterableIteratorType IterableIteratorType;
using IterableIterator<T>::DictReturn;
using IterableIterator<T>::KeyAndValueReturn;
typedef bool (*WrapFunc)(JSContext* aCx,
OneTypeIterableIterator<T>* aObject,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
OneTypeIterableIterator(T* aIterableObj,
IterableIteratorType aIteratorType,
WrapFunc aWrapFunc)
: IterableIterator<T>(aIterableObj)
, mIteratorType(aIteratorType)
, mWrapFunc(aWrapFunc)
, mIndex(0)
{
MOZ_ASSERT(mWrapFunc);
}
void
Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
{
JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
if (mIndex >= this->mIterableObj->GetIterableLength()) {
DictReturn(aCx, aResult, true, value, aRv);
return;
}
switch (mIteratorType) {
case IterableIteratorType::Keys:
case IterableIteratorType::Values:
{
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
DictReturn(aCx, aResult, false, value, aRv);
break;
}
case IterableIteratorType::Entries:
{
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
KeyAndValueReturn(aCx, value, value, aResult, aRv);
break;
}
default:
MOZ_CRASH("Invalid iterator type!");
}
++mIndex;
}
bool
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
{
return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
}
protected:
virtual ~OneTypeIterableIterator() {}
// Tells whether this is a key, value, or entries iterator.
IterableIteratorType mIteratorType;
// Function pointer to binding-type-specific Wrap() call for this iterator.
WrapFunc mWrapFunc;
// Current index of iteration.
uint32_t mIndex;
};
template<typename T>
class TwoTypeIterableIterator final : public IterableIterator<T>
{
public:
typedef typename IterableIterator<T>::IterableIteratorType IterableIteratorType;
using IterableIterator<T>::DictReturn;
using IterableIterator<T>::KeyAndValueReturn;
typedef bool (*WrapFunc)(JSContext* aCx,
TwoTypeIterableIterator<T>* aObject,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
TwoTypeIterableIterator(T* aIterableObj, IterableIteratorType aIteratorType,
WrapFunc aWrapFunc)
: IterableIterator<T>(aIterableObj)
, mIteratorType(aIteratorType)
, mWrapFunc(aWrapFunc)
, mIndex(0)
{
MOZ_ASSERT(mWrapFunc);
}
void
Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
{
JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
if (mIndex >= this->mIterableObj->GetIterableLength()) {
DictReturn(aCx, aResult, true, value, aRv);
return;
}
switch (mIteratorType) {
case IterableIteratorType::Keys:
{
if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
DictReturn(aCx, aResult, false, value, aRv);
break;
}
case IterableIteratorType::Values:
{
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
DictReturn(aCx, aResult, false, value, aRv);
break;
}
case IterableIteratorType::Entries:
{
JS::Rooted<JS::Value> key(aCx);
if (!ToJSValue(aCx, this->mIterableObj->GetKeyAtIndex(mIndex), &key)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (!ToJSValue(aCx, this->mIterableObj->GetValueAtIndex(mIndex), &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
KeyAndValueReturn(aCx, key, value, aResult, aRv);
break;
}
default:
MOZ_CRASH("Invalid iterator type!");
}
++mIndex;
}
bool
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
{
return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
}
protected:
virtual ~TwoTypeIterableIterator() {}
// Tells whether this is a key, value, or entries iterator. // Tells whether this is a key, value, or entries iterator.
IterableIteratorType mIteratorType; IterableIteratorType mIteratorType;
// Function pointer to binding-type-specific Wrap() call for this iterator. // Function pointer to binding-type-specific Wrap() call for this iterator.

View File

@ -1089,7 +1089,7 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
def validate(self): def validate(self):
# We don't support consequential unforgeable interfaces. Need to check # We don't support consequential unforgeable interfaces. Need to check
# this here, becaue in finish() an interface might not know yet that # this here, because in finish() an interface might not know yet that
# it's consequential. # it's consequential.
if self.getExtendedAttribute("Unforgeable") and self.isConsequential(): if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
raise WebIDLError( raise WebIDLError(
@ -1108,6 +1108,8 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
self.identifier.name, self.identifier.name,
locations) locations)
indexedGetter = None
hasLengthAttribute = False
for member in self.members: for member in self.members:
member.validate() member.validate()
@ -1118,8 +1120,13 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
[self.location, member.location]) [self.location, member.location])
# Check that PutForwards refers to another attribute and that no # Check that PutForwards refers to another attribute and that no
# cycles exist in forwarded assignments. # cycles exist in forwarded assignments. Also check for a
# integer-typed "length" attribute.
if member.isAttr(): if member.isAttr():
if (member.identifier.name == "length" and
member.type.isInteger()):
hasLengthAttribute = True
iface = self iface = self
attr = member attr = member
putForwards = attr.getExtendedAttribute("PutForwards") putForwards = attr.getExtendedAttribute("PutForwards")
@ -1157,8 +1164,11 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
putForwards = attr.getExtendedAttribute("PutForwards") putForwards = attr.getExtendedAttribute("PutForwards")
# Check that the name of an [Alias] doesn't conflict with an # Check that the name of an [Alias] doesn't conflict with an
# interface member. # interface member and whether we support indexed properties.
if member.isMethod(): if member.isMethod():
if member.isGetter() and member.isIndexed():
indexedGetter = member
for alias in member.aliases: for alias in member.aliases:
if self.isOnGlobalProtoChain(): if self.isOnGlobalProtoChain():
raise WebIDLError("[Alias] must not be used on a " raise WebIDLError("[Alias] must not be used on a "
@ -1219,6 +1229,35 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
"exposed conditionally", "exposed conditionally",
[self.location]) [self.location])
# Value iterators are only allowed on interfaces with indexed getters,
# and pair iterators are only allowed on interfaces without indexed
# getters.
if self.isIterable():
iterableDecl = self.maplikeOrSetlikeOrIterable
if iterableDecl.isValueIterator():
if not indexedGetter:
raise WebIDLError("Interface with value iterator does not "
"support indexed properties",
[self.location])
if iterableDecl.valueType != indexedGetter.signatures()[0][0]:
raise WebIDLError("Iterable type does not match indexed "
"getter type",
[iterableDecl.location,
indexedGetter.location])
if not hasLengthAttribute:
raise WebIDLError('Interface with value iterator does not '
'have an integer-typed "length" attribute',
[self.location])
else:
assert iterableDecl.isPairIterator()
if indexedGetter:
raise WebIDLError("Interface with pair iterator supports "
"indexed properties",
[self.location, iterableDecl.location,
indexedGetter.location])
def isInterface(self): def isInterface(self):
return True return True
@ -3408,7 +3447,10 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind): def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind):
IDLInterfaceMember.__init__(self, location, identifier, ifaceKind) IDLInterfaceMember.__init__(self, location, identifier, ifaceKind)
assert isinstance(keyType, IDLType) if keyType is not None:
assert isinstance(keyType, IDLType)
else:
assert valueType is not None
assert ifaceType in ['maplike', 'setlike', 'iterable'] assert ifaceType in ['maplike', 'setlike', 'iterable']
if valueType is not None: if valueType is not None:
assert isinstance(valueType, IDLType) assert isinstance(valueType, IDLType)
@ -3427,6 +3469,9 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
def isIterable(self): def isIterable(self):
return self.maplikeOrSetlikeOrIterableType == "iterable" return self.maplikeOrSetlikeOrIterableType == "iterable"
def hasKeyType(self):
return self.keyType is not None
def hasValueType(self): def hasValueType(self):
return self.valueType is not None return self.valueType is not None
@ -3451,7 +3496,8 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
[self.location, member.location]) [self.location, member.location])
def addMethod(self, name, members, allowExistingOperations, returnType, args=[], def addMethod(self, name, members, allowExistingOperations, returnType, args=[],
chromeOnly=False, isPure=False, affectsNothing=False, newObject=False): chromeOnly=False, isPure=False, affectsNothing=False, newObject=False,
isIteratorAlias=False):
""" """
Create an IDLMethod based on the parameters passed in. Create an IDLMethod based on the parameters passed in.
@ -3511,16 +3557,20 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
if newObject: if newObject:
method.addExtendedAttributes( method.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("NewObject",))]) [IDLExtendedAttribute(self.location, ("NewObject",))])
if isIteratorAlias:
method.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
members.append(method) members.append(method)
def resolve(self, parentScope): def resolve(self, parentScope):
self.keyType.resolveType(parentScope) if self.keyType:
self.keyType.resolveType(parentScope)
if self.valueType: if self.valueType:
self.valueType.resolveType(parentScope) self.valueType.resolveType(parentScope)
def finish(self, scope): def finish(self, scope):
IDLInterfaceMember.finish(self, scope) IDLInterfaceMember.finish(self, scope)
if not self.keyType.isComplete(): if self.keyType and not self.keyType.isComplete():
t = self.keyType.complete(scope) t = self.keyType.complete(scope)
assert not isinstance(t, IDLUnresolvedType) assert not isinstance(t, IDLUnresolvedType)
@ -3542,9 +3592,23 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
IDLInterfaceMember.handleExtendedAttribute(self, attr) IDLInterfaceMember.handleExtendedAttribute(self, attr)
def _getDependentObjects(self): def _getDependentObjects(self):
deps = set()
if self.keyType:
deps.add(self.keyType)
if self.valueType: if self.valueType:
return set([self.keyType, self.valueType]) deps.add(self.valueType)
return set([self.keyType]) 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.
@ -3565,9 +3629,15 @@ class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
we generate our functions as if they were part of the interface we generate our functions as if they were part of the interface
specification during parsing. specification during parsing.
""" """
# We only need to add entries/keys/values here if we're a pair iterator.
# Value iterators just copy these from %ArrayPrototype% instead.
if not self.isPairIterator():
return
# object entries() # object entries()
self.addMethod("entries", members, False, self.iteratorType, self.addMethod("entries", members, False, self.iteratorType,
affectsNothing=True, newObject=True) affectsNothing=True, newObject=True,
isIteratorAlias=True)
# object keys() # object keys()
self.addMethod("keys", members, False, self.iteratorType, self.addMethod("keys", members, False, self.iteratorType,
affectsNothing=True, newObject=True) affectsNothing=True, newObject=True)
@ -3575,6 +3645,17 @@ 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):
return not self.isPairIterator()
def isPairIterator(self):
return self.hasKeyType()
# MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface. # MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface.
class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase): class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
@ -3611,26 +3692,17 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
# object entries() # object entries()
self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object], self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
affectsNothing=True) affectsNothing=True, isIteratorAlias=self.isMaplike())
# object keys() # object keys()
self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object], self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
affectsNothing=True) affectsNothing=True)
# object values() # object values()
self.addMethod("values", members, False, BuiltinTypes[IDLBuiltinType.Types.object], self.addMethod("values", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
affectsNothing=True) 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,
@ -5436,10 +5508,13 @@ class Parser(Tokenizer):
location = self.getLocation(p, 2) location = self.getLocation(p, 2)
identifier = IDLUnresolvedIdentifier(location, "__iterable", identifier = IDLUnresolvedIdentifier(location, "__iterable",
allowDoubleUnderscore=True) allowDoubleUnderscore=True)
keyType = p[3]
valueType = None
if (len(p) > 6): if (len(p) > 6):
keyType = p[3]
valueType = p[5] valueType = p[5]
else:
keyType = None
valueType = p[3]
p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope()) p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope())
def p_Setlike(self, p): def p_Setlike(self, p):
@ -6509,7 +6584,7 @@ class Parser(Tokenizer):
if isinstance(m, IDLIterable): if isinstance(m, IDLIterable):
iterable = m iterable = m
break break
if iterable: if iterable and iterable.isPairIterator():
def simpleExtendedAttr(str): def simpleExtendedAttr(str):
return IDLExtendedAttribute(iface.location, (str, )) return IDLExtendedAttribute(iface.location, (str, ))
nextMethod = IDLMethod( nextMethod = IDLMethod(

View File

@ -22,4 +22,5 @@ interface DOMTokenList {
[Throws] [Throws]
boolean toggle(DOMString token, optional boolean force); boolean toggle(DOMString token, optional boolean force);
stringifier DOMString (); stringifier DOMString ();
iterable<DOMString?>;
}; };

View File

@ -13,4 +13,5 @@
interface NodeList { interface NodeList {
getter Node? item(unsigned long index); getter Node? item(unsigned long index);
readonly attribute unsigned long length; readonly attribute unsigned long length;
iterable<Node?>;
}; };