mirror of
https://github.com/classilla/tenfourfox.git
synced 2025-04-13 23:37:06 +00:00
#488: basic support for DOM iterables, {NodeList,DOMTokenList}.forEach M1216751 M1290636
This commit is contained in:
parent
83c11df480
commit
9470d4fb94
@ -1136,11 +1136,13 @@ class CGHeaders(CGWrapper):
|
||||
if desc.interface.maplikeOrSetlikeOrIterable:
|
||||
# We need ToJSValue.h for maplike/setlike type conversions
|
||||
bindingHeaders.add("mozilla/dom/ToJSValue.h")
|
||||
# Add headers for the key and value types of the maplike, since
|
||||
# they'll be needed for convenience functions
|
||||
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
|
||||
desc, None))
|
||||
if desc.interface.maplikeOrSetlikeOrIterable.valueType:
|
||||
# Add headers for the key and value types of the
|
||||
# maplike/setlike/iterable, since they'll be needed for
|
||||
# convenience functions
|
||||
if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
|
||||
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
|
||||
desc, None))
|
||||
if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
|
||||
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType,
|
||||
desc, None))
|
||||
|
||||
@ -2269,34 +2271,50 @@ class MethodDefiner(PropertyDefiner):
|
||||
"condition": MemberCondition()
|
||||
})
|
||||
|
||||
# Generate the maplike/setlike iterator, if one wasn't already
|
||||
# generated by a method. If we already have an @@iterator symbol, fail.
|
||||
if descriptor.interface.maplikeOrSetlikeOrIterable:
|
||||
if hasIterator(methods, self.regular):
|
||||
raise TypeError("Cannot have maplike/setlike/iterable interface with "
|
||||
"other members that generate @@iterator "
|
||||
"on interface %s, such as indexed getters "
|
||||
"or aliased functions." %
|
||||
self.descriptor.interface.identifier.name)
|
||||
for m in methods:
|
||||
if (m.isMaplikeOrSetlikeOrIterableMethod() and
|
||||
(((m.maplikeOrSetlikeOrIterable.isMaplike() or
|
||||
(m.maplikeOrSetlikeOrIterable.isIterable() and
|
||||
m.maplikeOrSetlikeOrIterable.hasValueType())) and
|
||||
m.identifier.name == "entries") or
|
||||
(((m.maplikeOrSetlikeOrIterable.isSetlike() or
|
||||
(m.maplikeOrSetlikeOrIterable.isIterable() and
|
||||
not m.maplikeOrSetlikeOrIterable.hasValueType()))) and
|
||||
m.identifier.name == "values"))):
|
||||
self.regular.append({
|
||||
"name": "@@iterator",
|
||||
"methodName": m.identifier.name,
|
||||
"length": methodLength(m),
|
||||
"flags": "0",
|
||||
"condition": PropertyDefiner.getControllingCondition(m,
|
||||
descriptor),
|
||||
})
|
||||
break
|
||||
# Generate the keys/values/entries aliases for value iterables.
|
||||
maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
|
||||
if (not static and
|
||||
not unforgeable and
|
||||
maplikeOrSetlikeOrIterable and
|
||||
maplikeOrSetlikeOrIterable.isIterable() and
|
||||
maplikeOrSetlikeOrIterable.isValueIterator()):
|
||||
# Add our keys/values/entries/forEach
|
||||
self.regular.append({
|
||||
"name": "keys",
|
||||
"methodInfo": False,
|
||||
"selfHostedName": "ArrayKeys",
|
||||
"length": 0,
|
||||
"flags": "JSPROP_ENUMERATE",
|
||||
"condition": PropertyDefiner.getControllingCondition(m,
|
||||
descriptor)
|
||||
})
|
||||
self.regular.append({
|
||||
"name": "values",
|
||||
"methodInfo": False,
|
||||
"selfHostedName": "ArrayValues",
|
||||
"length": 0,
|
||||
"flags": "JSPROP_ENUMERATE",
|
||||
"condition": PropertyDefiner.getControllingCondition(m,
|
||||
descriptor)
|
||||
})
|
||||
self.regular.append({
|
||||
"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:
|
||||
stringifier = descriptor.operations['Stringifier']
|
||||
@ -13008,8 +13026,9 @@ class CGForwardDeclarations(CGWrapper):
|
||||
# arguments to helper functions, and they'll need to be forward
|
||||
# declared in the header.
|
||||
if d.interface.maplikeOrSetlikeOrIterable:
|
||||
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
|
||||
config)
|
||||
if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
|
||||
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
|
||||
config)
|
||||
if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
|
||||
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.valueType,
|
||||
config)
|
||||
@ -15735,6 +15754,31 @@ class CGIterableMethodGenerator(CGGeneric):
|
||||
using CGCallGenerator.
|
||||
"""
|
||||
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(
|
||||
"""
|
||||
typedef ${iterClass} itrType;
|
||||
|
@ -893,8 +893,5 @@ def getAllTypes(descriptors, dictionaries, callbacks):
|
||||
def iteratorNativeType(descriptor):
|
||||
assert descriptor.interface.isIterable()
|
||||
iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
|
||||
if iterableDecl.valueType is None:
|
||||
iterClass = "OneTypeIterableIterator"
|
||||
else:
|
||||
iterClass = "TwoTypeIterableIterator"
|
||||
return "mozilla::dom::%s<%s>" % (iterClass, descriptor.nativeType)
|
||||
assert iterableDecl.isPairIterator()
|
||||
return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType
|
||||
|
@ -6,19 +6,17 @@
|
||||
|
||||
/**
|
||||
* The IterableIterator class is used for WebIDL interfaces that have a
|
||||
* iterable<> member defined. It handles the ES6 Iterator-like functions that
|
||||
* are generated for the iterable interface.
|
||||
* iterable<> member defined with two types (so a pair iterator). It handles
|
||||
* the ES6 Iterator-like functions that are generated for the iterable
|
||||
* interface.
|
||||
*
|
||||
* For iterable interfaces, the implementation class will need to
|
||||
* implement these two functions:
|
||||
* For iterable interfaces with a pair iterator, the implementation class will
|
||||
* need to implement these two functions:
|
||||
*
|
||||
* - size_t GetIterableLength()
|
||||
* - Returns the number of elements available to iterate over
|
||||
* - [type] GetValueAtIndex(size_t 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)
|
||||
* - Returns the key at the requested index
|
||||
*
|
||||
@ -60,13 +58,77 @@ protected:
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class IterableIterator : public IterableIteratorBase
|
||||
class IterableIterator final : public IterableIteratorBase
|
||||
{
|
||||
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)
|
||||
, mIteratorType(aIteratorType)
|
||||
, mWrapFunc(aWrapFunc)
|
||||
, mIndex(0)
|
||||
{
|
||||
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:
|
||||
@ -128,161 +190,6 @@ protected:
|
||||
|
||||
// Binding Implementation object that we're iterating over.
|
||||
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.
|
||||
IterableIteratorType mIteratorType;
|
||||
// Function pointer to binding-type-specific Wrap() call for this iterator.
|
||||
|
@ -1089,7 +1089,7 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
|
||||
def validate(self):
|
||||
# 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.
|
||||
if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
|
||||
raise WebIDLError(
|
||||
@ -1108,6 +1108,8 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
self.identifier.name,
|
||||
locations)
|
||||
|
||||
indexedGetter = None
|
||||
hasLengthAttribute = False
|
||||
for member in self.members:
|
||||
member.validate()
|
||||
|
||||
@ -1118,8 +1120,13 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
[self.location, member.location])
|
||||
|
||||
# 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.identifier.name == "length" and
|
||||
member.type.isInteger()):
|
||||
hasLengthAttribute = True
|
||||
|
||||
iface = self
|
||||
attr = member
|
||||
putForwards = attr.getExtendedAttribute("PutForwards")
|
||||
@ -1157,8 +1164,11 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
putForwards = attr.getExtendedAttribute("PutForwards")
|
||||
|
||||
# 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.isGetter() and member.isIndexed():
|
||||
indexedGetter = member
|
||||
|
||||
for alias in member.aliases:
|
||||
if self.isOnGlobalProtoChain():
|
||||
raise WebIDLError("[Alias] must not be used on a "
|
||||
@ -1219,6 +1229,35 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
"exposed conditionally",
|
||||
[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):
|
||||
return True
|
||||
|
||||
@ -3408,7 +3447,10 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
|
||||
|
||||
def __init__(self, location, identifier, ifaceType, keyType, valueType, 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']
|
||||
if valueType is not None:
|
||||
assert isinstance(valueType, IDLType)
|
||||
@ -3427,6 +3469,9 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
|
||||
def isIterable(self):
|
||||
return self.maplikeOrSetlikeOrIterableType == "iterable"
|
||||
|
||||
def hasKeyType(self):
|
||||
return self.keyType is not None
|
||||
|
||||
def hasValueType(self):
|
||||
return self.valueType is not None
|
||||
|
||||
@ -3451,7 +3496,8 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
|
||||
[self.location, member.location])
|
||||
|
||||
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.
|
||||
|
||||
@ -3511,16 +3557,20 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
|
||||
if newObject:
|
||||
method.addExtendedAttributes(
|
||||
[IDLExtendedAttribute(self.location, ("NewObject",))])
|
||||
if isIteratorAlias:
|
||||
method.addExtendedAttributes(
|
||||
[IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
|
||||
members.append(method)
|
||||
|
||||
def resolve(self, parentScope):
|
||||
self.keyType.resolveType(parentScope)
|
||||
if self.keyType:
|
||||
self.keyType.resolveType(parentScope)
|
||||
if self.valueType:
|
||||
self.valueType.resolveType(parentScope)
|
||||
|
||||
def 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)
|
||||
|
||||
assert not isinstance(t, IDLUnresolvedType)
|
||||
@ -3542,9 +3592,23 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
|
||||
IDLInterfaceMember.handleExtendedAttribute(self, attr)
|
||||
|
||||
def _getDependentObjects(self):
|
||||
deps = set()
|
||||
if self.keyType:
|
||||
deps.add(self.keyType)
|
||||
if self.valueType:
|
||||
return set([self.keyType, self.valueType])
|
||||
return set([self.keyType])
|
||||
deps.add(self.valueType)
|
||||
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
|
||||
# (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
|
||||
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()
|
||||
self.addMethod("entries", members, False, self.iteratorType,
|
||||
affectsNothing=True, newObject=True)
|
||||
affectsNothing=True, newObject=True,
|
||||
isIteratorAlias=True)
|
||||
# object keys()
|
||||
self.addMethod("keys", members, False, self.iteratorType,
|
||||
affectsNothing=True, newObject=True)
|
||||
@ -3575,6 +3645,17 @@ class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
|
||||
self.addMethod("values", members, False, self.iteratorType,
|
||||
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.
|
||||
class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
|
||||
|
||||
@ -3611,26 +3692,17 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
|
||||
|
||||
# object entries()
|
||||
self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||
affectsNothing=True)
|
||||
affectsNothing=True, isIteratorAlias=self.isMaplike())
|
||||
# object keys()
|
||||
self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||
affectsNothing=True)
|
||||
# object values()
|
||||
self.addMethod("values", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||
affectsNothing=True)
|
||||
affectsNothing=True, isIteratorAlias=self.isSetlike())
|
||||
|
||||
# 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],
|
||||
foreachArguments)
|
||||
self.getForEachArguments())
|
||||
|
||||
def getKeyArg():
|
||||
return IDLArgument(self.location,
|
||||
@ -5436,10 +5508,13 @@ class Parser(Tokenizer):
|
||||
location = self.getLocation(p, 2)
|
||||
identifier = IDLUnresolvedIdentifier(location, "__iterable",
|
||||
allowDoubleUnderscore=True)
|
||||
keyType = p[3]
|
||||
valueType = None
|
||||
if (len(p) > 6):
|
||||
keyType = p[3]
|
||||
valueType = p[5]
|
||||
else:
|
||||
keyType = None
|
||||
valueType = p[3]
|
||||
|
||||
p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope())
|
||||
|
||||
def p_Setlike(self, p):
|
||||
@ -6509,7 +6584,7 @@ class Parser(Tokenizer):
|
||||
if isinstance(m, IDLIterable):
|
||||
iterable = m
|
||||
break
|
||||
if iterable:
|
||||
if iterable and iterable.isPairIterator():
|
||||
def simpleExtendedAttr(str):
|
||||
return IDLExtendedAttribute(iface.location, (str, ))
|
||||
nextMethod = IDLMethod(
|
||||
|
@ -22,4 +22,5 @@ interface DOMTokenList {
|
||||
[Throws]
|
||||
boolean toggle(DOMString token, optional boolean force);
|
||||
stringifier DOMString ();
|
||||
iterable<DOMString?>;
|
||||
};
|
||||
|
@ -13,4 +13,5 @@
|
||||
interface NodeList {
|
||||
getter Node? item(unsigned long index);
|
||||
readonly attribute unsigned long length;
|
||||
iterable<Node?>;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user