mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-25 20:29:33 +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:
|
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
|
||||||
|
# convenience functions
|
||||||
|
if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
|
||||||
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
|
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
|
||||||
desc, None))
|
desc, None))
|
||||||
if desc.interface.maplikeOrSetlikeOrIterable.valueType:
|
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)
|
|
||||||
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({
|
self.regular.append({
|
||||||
"name": "@@iterator",
|
"name": "keys",
|
||||||
"methodName": m.identifier.name,
|
"methodInfo": False,
|
||||||
"length": methodLength(m),
|
"selfHostedName": "ArrayKeys",
|
||||||
"flags": "0",
|
"length": 0,
|
||||||
|
"flags": "JSPROP_ENUMERATE",
|
||||||
"condition": PropertyDefiner.getControllingCondition(m,
|
"condition": PropertyDefiner.getControllingCondition(m,
|
||||||
descriptor),
|
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)
|
||||||
})
|
})
|
||||||
break
|
|
||||||
|
|
||||||
if not static:
|
if not static:
|
||||||
stringifier = descriptor.operations['Stringifier']
|
stringifier = descriptor.operations['Stringifier']
|
||||||
|
@ -13008,6 +13026,7 @@ 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:
|
||||||
|
if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
|
||||||
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
|
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
|
||||||
config)
|
config)
|
||||||
if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
|
if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
|
||||||
|
@ -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;
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
if keyType is not None:
|
||||||
assert isinstance(keyType, IDLType)
|
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):
|
||||||
|
if self.keyType:
|
||||||
self.keyType.resolveType(parentScope)
|
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(
|
||||||
|
|
|
@ -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?>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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?>;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user