mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Impl Setlike and Maplike (#30237)
* MallocSizeOf for Index{Set, Map} * like as iterable in WebIDL * Codegen magic for like interfaces * TestBinding for like * Test for Setlike and Maplike test bindings * Some fixes * Switch to any.js * nit * Keep order
This commit is contained in:
parent
3df284cf54
commit
e0a6281e73
19 changed files with 1088 additions and 4 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3464,6 +3464,7 @@ dependencies = [
|
|||
"euclid",
|
||||
"http",
|
||||
"hyper_serde",
|
||||
"indexmap",
|
||||
"keyboard-types",
|
||||
"selectors",
|
||||
"serde",
|
||||
|
|
|
@ -36,6 +36,7 @@ cssparser = { workspace = true }
|
|||
euclid = { workspace = true }
|
||||
http = { workspace = true, optional = true }
|
||||
hyper_serde = { workspace = true, optional = true }
|
||||
indexmap = { workspace = true }
|
||||
keyboard-types = { workspace = true, optional = true }
|
||||
selectors = { path = "../selectors", features = ["shmem"] }
|
||||
serde = { workspace = true, optional = true }
|
||||
|
|
|
@ -436,6 +436,7 @@ macro_rules! malloc_size_of_hash_set {
|
|||
}
|
||||
|
||||
malloc_size_of_hash_set!(std::collections::HashSet<T, S>);
|
||||
malloc_size_of_hash_set!(indexmap::IndexSet<T, S>);
|
||||
|
||||
macro_rules! malloc_size_of_hash_map {
|
||||
($ty:ty) => {
|
||||
|
@ -475,6 +476,7 @@ macro_rules! malloc_size_of_hash_map {
|
|||
}
|
||||
|
||||
malloc_size_of_hash_map!(std::collections::HashMap<K, V, S>);
|
||||
malloc_size_of_hash_map!(indexmap::IndexMap<K, V, S>);
|
||||
|
||||
impl<K, V> MallocShallowSizeOf for std::collections::BTreeMap<K, V>
|
||||
where
|
||||
|
|
|
@ -3805,7 +3805,9 @@ class CGPerSignatureCall(CGThing):
|
|||
if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
|
||||
if idlNode.maplikeOrSetlikeOrIterable.isMaplike() or \
|
||||
idlNode.maplikeOrSetlikeOrIterable.isSetlike():
|
||||
raise TypeError('Maplike/Setlike methods are not supported yet')
|
||||
cgThings.append(CGMaplikeOrSetlikeMethodGenerator(descriptor,
|
||||
idlNode.maplikeOrSetlikeOrIterable,
|
||||
idlNode.identifier.name))
|
||||
else:
|
||||
cgThings.append(CGIterableMethodGenerator(descriptor,
|
||||
idlNode.maplikeOrSetlikeOrIterable,
|
||||
|
@ -6490,6 +6492,8 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
|
|||
'crate::dom::bindings::htmlconstructor::push_new_element_queue',
|
||||
'crate::dom::bindings::iterable::Iterable',
|
||||
'crate::dom::bindings::iterable::IteratorType',
|
||||
'crate::dom::bindings::like::Setlike',
|
||||
'crate::dom::bindings::like::Maplike',
|
||||
'crate::dom::bindings::namespace::NamespaceObjectClass',
|
||||
'crate::dom::bindings::namespace::create_namespace_object',
|
||||
'crate::dom::bindings::reflector::MutDomObject',
|
||||
|
@ -7862,6 +7866,77 @@ class CallbackOperation(CallbackOperationBase):
|
|||
descriptor, descriptor.interface.isSingleOperationInterface())
|
||||
|
||||
|
||||
class CGMaplikeOrSetlikeMethodGenerator(CGGeneric):
|
||||
"""
|
||||
Creates methods for *like interfaces. Unwrapping/wrapping
|
||||
will be taken care of by the usual method generation machinery in
|
||||
CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
|
||||
using CGCallGenerator.
|
||||
"""
|
||||
def __init__(self, descriptor, likeable, methodName):
|
||||
trait: str
|
||||
if likeable.isSetlike():
|
||||
trait = "Setlike"
|
||||
elif likeable.isMaplike():
|
||||
trait = "Maplike"
|
||||
else:
|
||||
raise TypeError("CGMaplikeOrSetlikeMethodGenerator is only for Setlike/Maplike")
|
||||
"""
|
||||
setlike:
|
||||
fn size(&self) -> usize;
|
||||
fn add(&self, key: Self::Key);
|
||||
fn has(&self, key: &Self::Key) -> bool;
|
||||
fn clear(&self);
|
||||
fn delete(&self, key: &Self::Key) -> bool;
|
||||
maplike:
|
||||
fn get(&self, key: Self::Key) -> Self::Value;
|
||||
fn size(&self) -> usize;
|
||||
fn set(&self, key: Self::Key, value: Self::Value);
|
||||
fn has(&self, key: &Self::Key) -> bool;
|
||||
fn clear(&self);
|
||||
fn delete(&self, key: &Self::Key) -> bool;
|
||||
like iterable:
|
||||
keys/values/entries/forEach
|
||||
"""
|
||||
# like iterables are implemented seperatly as we are actually implementing them
|
||||
if methodName in ["keys", "values", "entries", "forEach"]:
|
||||
CGIterableMethodGenerator.__init__(self, descriptor, likeable, methodName)
|
||||
elif methodName in ["size", "clear"]: # zero arguments
|
||||
CGGeneric.__init__(self, fill(
|
||||
"""
|
||||
let result = ${trt}::${method}(&*this);
|
||||
""",
|
||||
trt=trait,
|
||||
method=methodName.lower()))
|
||||
elif methodName == "add": # special case one argumet
|
||||
CGGeneric.__init__(self, fill(
|
||||
"""
|
||||
${trt}::${method}(&*this, arg0);
|
||||
// Returns itself per https://webidl.spec.whatwg.org/#es-set-add
|
||||
let result = this;
|
||||
""",
|
||||
trt=trait,
|
||||
method=methodName))
|
||||
elif methodName in ["has", "delete", "get"]: # one argument
|
||||
CGGeneric.__init__(self, fill(
|
||||
"""
|
||||
let result = ${trt}::${method}(&*this, arg0);
|
||||
""",
|
||||
trt=trait,
|
||||
method=methodName))
|
||||
elif methodName == "set": # two arguments
|
||||
CGGeneric.__init__(self, fill(
|
||||
"""
|
||||
${trt}::${method}(&*this, arg0, arg1);
|
||||
// Returns itself per https://webidl.spec.whatwg.org/#es-map-set
|
||||
let result = this;
|
||||
""",
|
||||
trt=trait,
|
||||
method=methodName))
|
||||
else:
|
||||
raise TypeError(f"Do not know how to impl *like method: {methodName}")
|
||||
|
||||
|
||||
class CGIterableMethodGenerator(CGGeneric):
|
||||
"""
|
||||
Creates methods for iterable interfaces. Unwrapping/wrapping
|
||||
|
|
|
@ -516,7 +516,11 @@ def getUnwrappedType(type):
|
|||
|
||||
|
||||
def iteratorNativeType(descriptor, infer=False):
|
||||
assert descriptor.interface.isIterable()
|
||||
iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
|
||||
assert iterableDecl.isPairIterator()
|
||||
return "IterableIterator%s" % ("" if infer else '<%s>' % descriptor.interface.identifier.name)
|
||||
assert (iterableDecl.isIterable() and iterableDecl.isPairIterator()) \
|
||||
or iterableDecl.isSetlike() or iterableDecl.isMaplike()
|
||||
res = "IterableIterator%s" % ("" if infer else '<%s>' % descriptor.interface.identifier.name)
|
||||
# todo: this hack is telling us that something is still wrong in codegen
|
||||
if iterableDecl.isSetlike() or iterableDecl.isMaplike():
|
||||
res = f"crate::dom::bindings::iterable::{res}"
|
||||
return res
|
||||
|
|
298
components/script/dom/bindings/like.rs
Normal file
298
components/script/dom/bindings/like.rs
Normal file
|
@ -0,0 +1,298 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
//! Implementation of `setlike<...>` and `maplike<..., ...>` WebIDL declarations.
|
||||
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use js::conversions::ToJSValConvertible;
|
||||
use std::cmp::Eq;
|
||||
use std::hash::Hash;
|
||||
|
||||
use super::iterable::Iterable;
|
||||
|
||||
/// Every Setlike dom_struct must implement this to provide access to underlying storage
|
||||
/// so codegen can automatically generate all setlike methods
|
||||
///
|
||||
/// In case you use a type that implements Setlike as underlying storage it's recommended to use `setlike` macro.
|
||||
// In webidl: `setlike<Key>`
|
||||
pub trait Setlike {
|
||||
/// The type of the key of the set.
|
||||
type Key: ToJSValConvertible + Clone; // clone is for impl<T: Setlike> Maplike for T
|
||||
|
||||
fn get_index(&self, index: u32) -> Option<Self::Key>;
|
||||
|
||||
fn size(&self) -> u32;
|
||||
fn add(&self, key: Self::Key);
|
||||
fn has(&self, key: Self::Key) -> bool;
|
||||
fn clear(&self);
|
||||
fn delete(&self, key: Self::Key) -> bool;
|
||||
}
|
||||
|
||||
// we can only have one iterable for T
|
||||
// so we have impl<T: Maplike> Iterable for T
|
||||
// and minimal:
|
||||
impl<T: Setlike> Maplike for T {
|
||||
type Key = <T as Setlike>::Key;
|
||||
|
||||
type Value = <T as Setlike>::Key;
|
||||
|
||||
#[inline]
|
||||
fn get_index(&self, index: u32) -> Option<(Self::Key, Self::Value)> {
|
||||
self.get_index(index).map(|k| (k.clone(), k))
|
||||
}
|
||||
|
||||
fn get(&self, _key: Self::Key) -> Option<Self::Value> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> u32 {
|
||||
self.size()
|
||||
}
|
||||
|
||||
fn set(&self, _key: Self::Key, _value: Self::Value) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn has(&self, _key: Self::Key) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn clear(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn delete(&self, _key: Self::Key) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> Setlike for DomRefCell<IndexSet<K>>
|
||||
where
|
||||
K: ToJSValConvertible + Eq + PartialEq + Hash + Clone,
|
||||
{
|
||||
type Key = K;
|
||||
|
||||
#[inline(always)]
|
||||
fn get_index(&self, index: u32) -> Option<Self::Key> {
|
||||
self.borrow().get_index(index as usize).cloned()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn size(&self) -> u32 {
|
||||
self.borrow().len() as u32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn add(&self, key: Self::Key) {
|
||||
self.borrow_mut().insert(key);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn has(&self, key: Self::Key) -> bool {
|
||||
self.borrow().contains(&key)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn clear(&self) {
|
||||
self.borrow_mut().clear()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn delete(&self, key: Self::Key) -> bool {
|
||||
self.borrow_mut().shift_remove(&key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Usage:
|
||||
/// ```rust
|
||||
/// pub struct TestBindingSetlike {
|
||||
/// // setlike<DOMString>
|
||||
/// internal: DomRefCell<IndexSet<DOMString>>,
|
||||
/// }
|
||||
/// impl Setlike for TestBindingSetlike {
|
||||
/// type Key = DOMString;
|
||||
/// setlike!(self, internal);
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! setlike {
|
||||
( $self:ident, $x:ident ) => {
|
||||
#[inline(always)]
|
||||
fn get_index(&$self, index: u32) -> Option<Self::Key> {
|
||||
$self.$x.get_index(index)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn size(&$self) -> u32 {
|
||||
$self.$x.size()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn add(&$self, key: Self::Key) {
|
||||
$self.$x.add(key)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn has(&$self, key: Self::Key) -> bool {
|
||||
$self.$x.has(key)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn clear(&$self) {
|
||||
$self.$x.clear()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn delete(&$self, key: Self::Key) -> bool {
|
||||
$self.$x.delete(key)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Every Maplike dom_struct must implement this
|
||||
/// to provide access to underlying storage
|
||||
/// so codegen can automatically generate all maplike methods
|
||||
///
|
||||
/// In case you use a type that implements Maplike as underlying storage it's recommended to use `maplike` macro.
|
||||
// In webidl: `maplike<Key, Value>`
|
||||
pub trait Maplike {
|
||||
/// The type of the key of the map.
|
||||
type Key: ToJSValConvertible;
|
||||
/// The type of the value of the map.
|
||||
type Value: ToJSValConvertible;
|
||||
|
||||
fn get_index(&self, index: u32) -> Option<(Self::Key, Self::Value)>;
|
||||
|
||||
fn get(&self, key: Self::Key) -> Option<Self::Value>;
|
||||
fn size(&self) -> u32;
|
||||
fn set(&self, key: Self::Key, value: Self::Value);
|
||||
fn has(&self, key: Self::Key) -> bool;
|
||||
fn clear(&self);
|
||||
fn delete(&self, key: Self::Key) -> bool;
|
||||
}
|
||||
|
||||
impl<T: Maplike> Iterable for T {
|
||||
type Key = T::Key;
|
||||
|
||||
type Value = T::Value;
|
||||
|
||||
#[inline]
|
||||
fn get_iterable_length(&self) -> u32 {
|
||||
self.size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_value_at_index(&self, index: u32) -> Self::Value {
|
||||
// SAFETY: we are checking bounds manually
|
||||
self.get_index(index).unwrap().1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_key_at_index(&self, index: u32) -> Self::Key {
|
||||
// SAFETY: we are checking bounds manually
|
||||
self.get_index(index).unwrap().0
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Maplike for DomRefCell<IndexMap<K, V>>
|
||||
where
|
||||
K: ToJSValConvertible + Eq + PartialEq + Hash + Clone,
|
||||
V: ToJSValConvertible + Clone,
|
||||
{
|
||||
type Key = K;
|
||||
type Value = V;
|
||||
|
||||
#[inline(always)]
|
||||
fn get_index(&self, index: u32) -> Option<(Self::Key, Self::Value)> {
|
||||
self.borrow()
|
||||
.get_index(index as usize)
|
||||
.map(|(k, v)| (k.to_owned(), v.to_owned()))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get(&self, key: Self::Key) -> Option<Self::Value> {
|
||||
self.borrow().get(&key).cloned()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn size(&self) -> u32 {
|
||||
self.borrow().len() as u32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set(&self, key: Self::Key, value: Self::Value) {
|
||||
self.borrow_mut().insert(key, value);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn has(&self, key: Self::Key) -> bool {
|
||||
self.borrow().contains_key(&key)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn clear(&self) {
|
||||
self.borrow_mut().clear()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn delete(&self, key: Self::Key) -> bool {
|
||||
self.borrow_mut().shift_remove(&key).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
/// Usage:
|
||||
/// ```rust
|
||||
/// pub struct TestBindingMaplike {
|
||||
/// // maplike<DOMString, long>
|
||||
/// internal: DomRefCell<IndexMap<DOMString, i32>>,
|
||||
/// }
|
||||
/// impl Maplike for TestBindingSetlike {
|
||||
/// type Key = DOMString;
|
||||
/// type Value = i32;
|
||||
/// maplike!(self, internal);
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! maplike {
|
||||
( $self:ident, $internal:ident ) => {
|
||||
#[inline(always)]
|
||||
fn get_index(&$self, index: u32) -> Option<(Self::Key, Self::Value)> {
|
||||
$self.$internal.get_index(index)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get(&$self, key: Self::Key) -> Option<Self::Value> {
|
||||
$self.$internal.get(key)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn size(&$self) -> u32 {
|
||||
$self.$internal.size()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set(&$self, key: Self::Key, value: Self::Value) {
|
||||
$self.$internal.set(key, value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn has(&$self, key: Self::Key) -> bool {
|
||||
$self.$internal.has(key)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn clear(&$self) {
|
||||
$self.$internal.clear()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn delete(&$self, key: Self::Key) -> bool {
|
||||
$self.$internal.delete(key)
|
||||
}
|
||||
};
|
||||
}
|
|
@ -143,6 +143,7 @@ pub mod htmlconstructor;
|
|||
pub mod inheritance;
|
||||
pub mod interface;
|
||||
pub mod iterable;
|
||||
pub mod like;
|
||||
pub mod namespace;
|
||||
pub mod num;
|
||||
pub mod principals;
|
||||
|
|
|
@ -532,8 +532,10 @@ pub mod svggraphicselement;
|
|||
pub mod svgsvgelement;
|
||||
pub mod testbinding;
|
||||
pub mod testbindingiterable;
|
||||
pub mod testbindingmaplike;
|
||||
pub mod testbindingpairiterable;
|
||||
pub mod testbindingproxy;
|
||||
pub mod testbindingsetlike;
|
||||
pub mod testrunner;
|
||||
pub mod testworklet;
|
||||
pub mod testworkletglobalscope;
|
||||
|
|
90
components/script/dom/testbindingmaplike.rs
Normal file
90
components/script/dom/testbindingmaplike.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// check-tidy: no specs after this line
|
||||
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::TestBindingMaplikeBinding::TestBindingMaplikeMethods;
|
||||
use crate::dom::bindings::error::Fallible;
|
||||
use crate::dom::bindings::like::Maplike;
|
||||
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::maplike;
|
||||
use dom_struct::dom_struct;
|
||||
use indexmap::IndexMap;
|
||||
use js::rust::HandleObject;
|
||||
|
||||
use super::bindings::error::Error;
|
||||
|
||||
/// maplike<DOMString, long>
|
||||
#[dom_struct]
|
||||
pub struct TestBindingMaplike {
|
||||
reflector: Reflector,
|
||||
#[custom_trace]
|
||||
internal: DomRefCell<IndexMap<DOMString, i32>>,
|
||||
}
|
||||
|
||||
impl TestBindingMaplike {
|
||||
fn new(global: &GlobalScope, proto: Option<HandleObject>) -> DomRoot<TestBindingMaplike> {
|
||||
reflect_dom_object_with_proto(
|
||||
Box::new(TestBindingMaplike {
|
||||
reflector: Reflector::new(),
|
||||
internal: DomRefCell::new(IndexMap::new()),
|
||||
}),
|
||||
global,
|
||||
proto,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Constructor(
|
||||
global: &GlobalScope,
|
||||
proto: Option<HandleObject>,
|
||||
) -> Fallible<DomRoot<TestBindingMaplike>> {
|
||||
Ok(TestBindingMaplike::new(global, proto))
|
||||
}
|
||||
}
|
||||
|
||||
impl TestBindingMaplikeMethods for TestBindingMaplike {
|
||||
fn SetInternal(&self, key: DOMString, value: i32) {
|
||||
self.internal.set(key, value)
|
||||
}
|
||||
|
||||
fn ClearInternal(&self) {
|
||||
self.internal.clear()
|
||||
}
|
||||
|
||||
fn DeleteInternal(&self, key: DOMString) -> bool {
|
||||
self.internal.delete(key)
|
||||
}
|
||||
|
||||
fn HasInternal(&self, key: DOMString) -> bool {
|
||||
self.internal.has(key)
|
||||
}
|
||||
|
||||
fn GetInternal(&self, key: DOMString) -> Fallible<i32> {
|
||||
// TODO: error type?
|
||||
self.internal
|
||||
.borrow()
|
||||
.get(&key)
|
||||
.ok_or_else(|| Error::Type(format!("No entry for key {key}")))
|
||||
.copied()
|
||||
}
|
||||
|
||||
fn Size(&self) -> u32 {
|
||||
self.internal.size()
|
||||
}
|
||||
}
|
||||
|
||||
// this error is wrong because if we inline Self::Key and Self::Value all errors are gone
|
||||
// TODO: FIX THIS
|
||||
#[allow(unrooted_must_root)]
|
||||
impl Maplike for TestBindingMaplike {
|
||||
type Key = DOMString;
|
||||
type Value = i32;
|
||||
|
||||
maplike!(self, internal);
|
||||
}
|
63
components/script/dom/testbindingsetlike.rs
Normal file
63
components/script/dom/testbindingsetlike.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// check-tidy: no specs after this line
|
||||
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::TestBindingSetlikeBinding::TestBindingSetlikeMethods;
|
||||
use crate::dom::bindings::error::Fallible;
|
||||
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::setlike;
|
||||
use dom_struct::dom_struct;
|
||||
use indexmap::IndexSet;
|
||||
use js::rust::HandleObject;
|
||||
|
||||
use super::bindings::like::Setlike;
|
||||
|
||||
// setlike<DOMString>
|
||||
#[dom_struct]
|
||||
pub struct TestBindingSetlike {
|
||||
reflector: Reflector,
|
||||
#[custom_trace]
|
||||
internal: DomRefCell<IndexSet<DOMString>>,
|
||||
}
|
||||
|
||||
impl TestBindingSetlike {
|
||||
fn new(global: &GlobalScope, proto: Option<HandleObject>) -> DomRoot<TestBindingSetlike> {
|
||||
reflect_dom_object_with_proto(
|
||||
Box::new(TestBindingSetlike {
|
||||
reflector: Reflector::new(),
|
||||
internal: DomRefCell::new(IndexSet::new()),
|
||||
}),
|
||||
global,
|
||||
proto,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Constructor(
|
||||
global: &GlobalScope,
|
||||
proto: Option<HandleObject>,
|
||||
) -> Fallible<DomRoot<TestBindingSetlike>> {
|
||||
Ok(TestBindingSetlike::new(global, proto))
|
||||
}
|
||||
}
|
||||
|
||||
impl TestBindingSetlikeMethods for TestBindingSetlike {
|
||||
fn Size(&self) -> u32 {
|
||||
self.internal.size()
|
||||
}
|
||||
}
|
||||
|
||||
// this error is wrong because if we inline Self::Key and Self::Value all errors are gone
|
||||
// TODO: FIX THIS
|
||||
#[allow(unrooted_must_root)]
|
||||
impl Setlike for TestBindingSetlike {
|
||||
type Key = DOMString;
|
||||
|
||||
setlike!(self, internal);
|
||||
}
|
20
components/script/dom/webidls/TestBindingMaplike.webidl
Normal file
20
components/script/dom/webidls/TestBindingMaplike.webidl
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This interface is entirely internal to Servo, and should not be accessible to
|
||||
// web pages.
|
||||
|
||||
[Pref="dom.testbinding.enabled", Exposed=(Window,Worker)]
|
||||
interface TestBindingMaplike {
|
||||
[Throws]
|
||||
constructor();
|
||||
|
||||
maplike<DOMString, long>;
|
||||
undefined setInternal(DOMString aKey, long aValue);
|
||||
undefined clearInternal();
|
||||
boolean deleteInternal(DOMString aKey);
|
||||
boolean hasInternal(DOMString aKey);
|
||||
[Throws]
|
||||
long getInternal(DOMString aKey);
|
||||
};
|
14
components/script/dom/webidls/TestBindingSetlike.webidl
Normal file
14
components/script/dom/webidls/TestBindingSetlike.webidl
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This interface is entirely internal to Servo, and should not be accessible to
|
||||
// web pages.
|
||||
|
||||
[Pref="dom.testbinding.enabled", Exposed=(Window,Worker)]
|
||||
interface TestBindingSetlike {
|
||||
[Throws]
|
||||
constructor();
|
||||
|
||||
setlike<DOMString>;
|
||||
};
|
23
tests/wpt/mozilla/meta-legacy-layout/mozilla/like.any.js.ini
Normal file
23
tests/wpt/mozilla/meta-legacy-layout/mozilla/like.any.js.ini
Normal file
|
@ -0,0 +1,23 @@
|
|||
[like.any.html]
|
||||
type: testharness
|
||||
prefs: [dom.testbinding.enabled:true]
|
||||
[Test defaulting arguments on setlike to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[Simple map creation and functionality test]
|
||||
expected: FAIL
|
||||
|
||||
[Test defaulting arguments on maplike to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[like.any.worker.html]
|
||||
type: testharness
|
||||
prefs: [dom.testbinding.enabled:true]
|
||||
[Test defaulting arguments on setlike to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[Simple map creation and functionality test]
|
||||
expected: FAIL
|
||||
|
||||
[Test defaulting arguments on maplike to undefined]
|
||||
expected: FAIL
|
|
@ -13451,6 +13451,31 @@
|
|||
{}
|
||||
]
|
||||
],
|
||||
"like.any.js": [
|
||||
"11e4d86325ce6216754a0e0b760c59beb812db85",
|
||||
[
|
||||
"mozilla/like.any.html",
|
||||
{
|
||||
"script_metadata": [
|
||||
[
|
||||
"title",
|
||||
"Setlike and Maplike bindings"
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
"mozilla/like.any.worker.html",
|
||||
{
|
||||
"script_metadata": [
|
||||
[
|
||||
"title",
|
||||
"Setlike and Maplike bindings"
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"link_rel_crash.html": [
|
||||
"9fa5a1dc04fff0f879d88ce95f4eb5181e2f9b92",
|
||||
[
|
||||
|
|
23
tests/wpt/mozilla/meta/mozilla/like.any.js.ini
Normal file
23
tests/wpt/mozilla/meta/mozilla/like.any.js.ini
Normal file
|
@ -0,0 +1,23 @@
|
|||
[like.any.html]
|
||||
type: testharness
|
||||
prefs: [dom.testbinding.enabled:true]
|
||||
[Test defaulting arguments on setlike to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[Simple map creation and functionality test]
|
||||
expected: FAIL
|
||||
|
||||
[Test defaulting arguments on maplike to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[like.any.worker.html]
|
||||
type: testharness
|
||||
prefs: [dom.testbinding.enabled:true]
|
||||
[Test defaulting arguments on setlike to undefined]
|
||||
expected: FAIL
|
||||
|
||||
[Simple map creation and functionality test]
|
||||
expected: FAIL
|
||||
|
||||
[Test defaulting arguments on maplike to undefined]
|
||||
expected: FAIL
|
308
tests/wpt/mozilla/tests/mozilla/like.any.js
Normal file
308
tests/wpt/mozilla/tests/mozilla/like.any.js
Normal file
|
@ -0,0 +1,308 @@
|
|||
// META: title=Setlike and Maplike bindings
|
||||
|
||||
function collect(iter) {
|
||||
var collection = [];
|
||||
for (element of iter) {
|
||||
collection.push(element);
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
// mochitest does not do strict true test
|
||||
function ok(t, desc) {
|
||||
if (t) {
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// tests ported from https://searchfox.org/mozilla-central/source/dom/bindings/test/test_bug1123516_maplikesetlike.html
|
||||
|
||||
var base_properties = [["has", "function", 1],
|
||||
["entries", "function", 0],
|
||||
["keys", "function", 0],
|
||||
["values", "function", 0],
|
||||
["forEach", "function", 1],
|
||||
["size", "number"]];
|
||||
var maplike_properties = base_properties.concat([["set", "function", 2]]);
|
||||
var rw_properties = [["clear", "function", 0],
|
||||
["delete", "function", 1]];
|
||||
var setlike_rw_properties = base_properties.concat(rw_properties).concat([["add", "function", 1]]);
|
||||
var maplike_rw_properties = maplike_properties.concat(rw_properties).concat([["get", "function", 1]]);
|
||||
var testExistence = function testExistence(prefix, obj, properties) {
|
||||
for (var [name, type, args] of properties) {
|
||||
// Properties are somewhere up the proto chain, hasOwnProperty won't work
|
||||
assert_not_equals(obj[name], undefined,
|
||||
`${prefix} object has property ${name}`);
|
||||
|
||||
assert_equals(typeof obj[name], type,
|
||||
`${prefix} object property ${name} is a ${type}`);
|
||||
// Check function length
|
||||
if (type == "function") {
|
||||
assert_equals(obj[name].length, args,
|
||||
`${prefix} object property ${name} is length ${args}`);
|
||||
assert_equals(obj[name].name, name,
|
||||
`${prefix} object method name is ${name}`);
|
||||
}
|
||||
|
||||
// Find where property is on proto chain, check for enumerablility there.
|
||||
var owner = obj;
|
||||
while (owner) {
|
||||
var propDesc = Object.getOwnPropertyDescriptor(owner, name);
|
||||
if (propDesc) {
|
||||
assert_true(ok(propDesc.enumerable),
|
||||
`${prefix} object property ${name} should be enumerable`);
|
||||
break;
|
||||
}
|
||||
owner = Object.getPrototypeOf(owner);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
test(function () {
|
||||
var m = new TestBindingSetlike();
|
||||
assert_true(ok(m), "SimpleSet: got a TestBindingSetlike object");
|
||||
testExistence("SimpleSet: ", m, setlike_rw_properties);
|
||||
assert_equals(m.size, 0, "SimpleSet: size should be zero");
|
||||
assert_true(!m.has("test"), "SimpleSet: maplike has should return false");
|
||||
m1 = m.add("test");
|
||||
assert_equals(m, m1, "SimpleSet: return from set should be map object");
|
||||
assert_equals(m.size, 1, "SimpleSet: size should be 1");
|
||||
assert_true(m.has("test"), "SimpleSet: maplike has should return true");
|
||||
m.add("test2");
|
||||
assert_equals(m.size, 2, "SimpleSet: size should be 2");
|
||||
testSet = ["test", "test2"];
|
||||
testIndex = 0;
|
||||
m.forEach(function (v, k, o) {
|
||||
"use strict";
|
||||
assert_equals(o, m, "SimpleSet: foreach obj is correct");
|
||||
assert_equals(k, testSet[testIndex], "SimpleSet: foreach set key: " + k + " = " + testSet[testIndex]);
|
||||
testIndex += 1;
|
||||
});
|
||||
assert_equals(testIndex, 2, "SimpleSet: foreach ran correct number of times");
|
||||
assert_true(m.has("test2"), "SimpleSet: maplike has should return true");
|
||||
assert_equals(m.delete("test2"), true, "SimpleSet: maplike deletion should return true");
|
||||
assert_equals(m.size, 1, "SimpleSet: size should be 1");
|
||||
iterable = false;
|
||||
for (let e of m) {
|
||||
iterable = true;
|
||||
assert_equals(e, "test", "SimpleSet: iterable first array element should be key");
|
||||
}
|
||||
assert_equals(m[Symbol.iterator].length, 0, "SimpleSet: @@iterator symbol is correct length");
|
||||
assert_equals(m[Symbol.iterator].name, "values", "SimpleSet: @@iterator symbol has correct name");
|
||||
assert_equals(m[Symbol.iterator], m.values, 'SimpleSet: @@iterator is an alias for "values"');
|
||||
assert_true(ok(iterable), "SimpleSet: @@iterator symbol resolved correctly");
|
||||
for (let k of m.keys()) {
|
||||
assert_equals(k, "test", "SimpleSet: first keys element should be 'test'");
|
||||
}
|
||||
for (let v of m.values()) {
|
||||
assert_equals(v, "test", "SimpleSet: first values elements should be 'test'");
|
||||
}
|
||||
for (let e of m.entries()) {
|
||||
assert_equals(e[0], "test", "SimpleSet: Entries first array element should be 'test'");
|
||||
assert_equals(e[1], "test", "SimpleSet: Entries second array element should be 'test'");
|
||||
}
|
||||
m.clear();
|
||||
assert_equals(m.size, 0, "SimpleSet: size should be 0 after clear");
|
||||
}, "Simple set creation and functionality");
|
||||
|
||||
test(function () {
|
||||
var m = new TestBindingSetlike();
|
||||
m.add();
|
||||
assert_equals(m.size, 1, "SetArgsDefault: should have 1 entry");
|
||||
m.forEach(function (v, k) {
|
||||
"use strict";
|
||||
assert_equals(typeof k, "string", "SetArgsDefault: key is a string");
|
||||
assert_equals(k, "undefined", "SetArgsDefault: key is the string undefined");
|
||||
});
|
||||
m.delete();
|
||||
assert_equals(m.size, 0, "SetArgsDefault: should have 0 entries");
|
||||
}, "Test defaulting arguments on setlike to undefined");
|
||||
|
||||
test(function () {
|
||||
// Simple map creation and functionality test
|
||||
m = new TestBindingMaplike();
|
||||
assert_true(ok(m), "SimpleMap: got a TestBindingMaplike object");
|
||||
testExistence("SimpleMap: ", m, maplike_rw_properties);
|
||||
assert_equals(m.size, 0, "SimpleMap: size should be zero");
|
||||
assert_true(!m.has("test"), "SimpleMap: maplike has should return false");
|
||||
assert_equals(m.get("test"), undefined, "SimpleMap: maplike get should return undefined on bogus lookup");
|
||||
var m1 = m.set("test", 1);
|
||||
assert_equals(m, m1, "SimpleMap: return from set should be map object");
|
||||
assert_equals(m.size, 1, "SimpleMap: size should be 1");
|
||||
assert_true(m.has("test"), "SimpleMap: maplike has should return true");
|
||||
assert_equals(m.get("test"), 1, "SimpleMap: maplike get should return value entered");
|
||||
m.set("test2", 2);
|
||||
assert_equals(m.size, 2, "SimpleMap: size should be 2");
|
||||
testSet = [["test", 1], ["test2", 2]];
|
||||
testIndex = 0;
|
||||
m.forEach(function (v, k, o) {
|
||||
"use strict";
|
||||
assert_equals(o, m, "SimpleMap: foreach obj is correct");
|
||||
assert_equals(k, testSet[testIndex][0], "SimpleMap: foreach map key: " + k + " = " + testSet[testIndex][0]);
|
||||
assert_equals(v, testSet[testIndex][1], "SimpleMap: foreach map value: " + v + " = " + testSet[testIndex][1]);
|
||||
testIndex += 1;
|
||||
});
|
||||
assert_equals(testIndex, 2, "SimpleMap: foreach ran correct number of times");
|
||||
assert_true(m.has("test2"), "SimpleMap: maplike has should return true");
|
||||
assert_equals(m.get("test2"), 2, "SimpleMap: maplike get should return value entered");
|
||||
assert_equals(m.delete("test2"), true, "SimpleMap: maplike deletion should return boolean");
|
||||
assert_equals(m.size, 1, "SimpleMap: size should be 1");
|
||||
var iterable = false;
|
||||
for (let e of m) {
|
||||
iterable = true;
|
||||
assert_equals(e[0], "test", "SimpleMap: iterable first array element should be key");
|
||||
assert_equals(e[1], 1, "SimpleMap: iterable second array element should be value");
|
||||
}
|
||||
assert_equals(m[Symbol.iterator].length, 0, "SimpleMap: @@iterator symbol is correct length");
|
||||
assert_equals(m[Symbol.iterator].name, "entries", "SimpleMap: @@iterator symbol has correct name");
|
||||
assert_equals(m[Symbol.iterator], m.entries, 'SimpleMap: @@iterator is an alias for "entries"');
|
||||
assert_true(ok(iterable), "SimpleMap: @@iterator symbol resolved correctly");
|
||||
for (let k of m.keys()) {
|
||||
assert_equals(k, "test", "SimpleMap: first keys element should be 'test'");
|
||||
}
|
||||
for (let v of m.values()) {
|
||||
assert_equals(v, 1, "SimpleMap: first values elements should be 1");
|
||||
}
|
||||
for (let e of m.entries()) {
|
||||
assert_equals(e[0], "test", "SimpleMap: entries first array element should be 'test'");
|
||||
assert_equals(e[1], 1, "SimpleMap: entries second array element should be 1");
|
||||
}
|
||||
m.clear();
|
||||
assert_equals(m.size, 0, "SimpleMap: size should be 0 after clear");
|
||||
}, "Simple map creation and functionality test");
|
||||
|
||||
test(function () {
|
||||
// Map convenience function test
|
||||
m = new TestBindingMaplike();
|
||||
assert_true(ok(m), "MapConvenience: got a TestBindingMaplike object");
|
||||
assert_equals(m.size, 0, "MapConvenience: size should be zero");
|
||||
assert_true(!m.hasInternal("test"), "MapConvenience: maplike hasInternal should return false");
|
||||
// It's fine to let getInternal to return 0 if the key doesn't exist
|
||||
// because this API can only be used internally in C++ and we'd throw
|
||||
// an error if the key doesn't exist.
|
||||
//SimpleTest.doesThrow(() => m.getInternal("test"), 0, "MapConvenience: maplike getInternal should throw if the key doesn't exist");
|
||||
m.setInternal("test", 1);
|
||||
assert_equals(m.size, 1, "MapConvenience: size should be 1");
|
||||
assert_true(m.hasInternal("test"), "MapConvenience: maplike hasInternal should return true");
|
||||
assert_equals(m.get("test"), 1, "MapConvenience: maplike get should return value entered");
|
||||
assert_equals(m.getInternal("test"), 1, "MapConvenience: maplike getInternal should return value entered");
|
||||
m.setInternal("test2", 2);
|
||||
assert_equals(m.size, 2, "size should be 2");
|
||||
assert_true(m.hasInternal("test2"), "MapConvenience: maplike hasInternal should return true");
|
||||
assert_equals(m.get("test2"), 2, "MapConvenience: maplike get should return value entered");
|
||||
assert_equals(m.getInternal("test2"), 2, "MapConvenience: maplike getInternal should return value entered");
|
||||
assert_equals(m.deleteInternal("test2"), true, "MapConvenience: maplike deleteInternal should return true");
|
||||
assert_equals(m.size, 1, "MapConvenience: size should be 1");
|
||||
m.clearInternal();
|
||||
assert_equals(m.size, 0, "MapConvenience: size should be 0 after clearInternal");
|
||||
}, "Map convenience function test");
|
||||
|
||||
// JS implemented map creation convenience function test
|
||||
test(function () {
|
||||
// Test this override for forEach
|
||||
m = new TestBindingMaplike();
|
||||
m.set("test", 1);
|
||||
m.forEach(function (v, k, o) {
|
||||
"use strict";
|
||||
assert_equals(o, m, "ForEachThisOverride: foreach obj is correct");
|
||||
assert_equals(this, 5, "ForEachThisOverride: 'this' value should be correct");
|
||||
}, 5);
|
||||
}, "Test this override for forEach");
|
||||
|
||||
test(function () {
|
||||
// Test defaulting arguments on maplike to undefined
|
||||
m = new TestBindingMaplike();
|
||||
m.set();
|
||||
assert_equals(m.size, 1, "MapArgsDefault: should have 1 entry");
|
||||
m.forEach(function (v, k) {
|
||||
"use strict";
|
||||
assert_equals(typeof k, "string", "MapArgsDefault: key is a string");
|
||||
assert_equals(k, "undefined", "MapArgsDefault: key is the string undefined");
|
||||
assert_equals(v, 0, "MapArgsDefault: value is 0");
|
||||
});
|
||||
assert_equals(m.get(), 0, "MapArgsDefault: no argument to get() returns correct value");
|
||||
m.delete();
|
||||
assert_equals(m.size, 0, "MapArgsDefault: should have 0 entries");
|
||||
}, "Test defaulting arguments on maplike to undefined");
|
||||
|
||||
// some iterable test ported to *like interfaces
|
||||
test(function () {
|
||||
var t = new TestBindingSetlike();
|
||||
var empty = true;
|
||||
t.forEach(function () { empty = false; });
|
||||
assert_true(empty);
|
||||
}, "Empty setlike");
|
||||
|
||||
test(function () {
|
||||
var t = new TestBindingSetlike();
|
||||
function is_iterator(o) {
|
||||
return o[Symbol.iterator]() === o;
|
||||
}
|
||||
assert_true(is_iterator(t.keys()));
|
||||
assert_true(is_iterator(t.values()));
|
||||
assert_true(is_iterator(t.entries()));
|
||||
}, "Setlike are iterators");
|
||||
|
||||
test(function () {
|
||||
var t = new TestBindingSetlike();
|
||||
t.add("first");
|
||||
t.add("second");
|
||||
t.add("third");
|
||||
assert_array_equals(collect(t.keys()), collect(t.values()));
|
||||
assert_array_equals(collect(t.values()), ["first", "second", "third"]);
|
||||
var expected = [["first", "first"], ["second", "second"], ["third", "third"]];
|
||||
var i = 0;
|
||||
for (entry of t.entries()) {
|
||||
assert_array_equals(entry, expected[i++]);
|
||||
}
|
||||
|
||||
t.add("fourth");
|
||||
assert_array_equals(collect(t.keys()), collect(t.values()));
|
||||
assert_array_equals(collect(t.values()), ["first", "second", "third", "fourth"]);
|
||||
var expected = [["first", "first"], ["second", "second"], ["third", "third"], ["fourth", "fourth"]];
|
||||
var i = 0;
|
||||
for (entry of t.entries()) {
|
||||
assert_array_equals(entry, expected[i++]);
|
||||
}
|
||||
}, "Iterators iterate over values");
|
||||
|
||||
test(function () {
|
||||
var t = new TestBindingMaplike();
|
||||
var empty = true;
|
||||
t.forEach(function () { empty = false; });
|
||||
assert_true(empty);
|
||||
}, "Empty maplike");
|
||||
|
||||
test(function () {
|
||||
var t = new TestBindingMaplike();
|
||||
function is_iterator(o) {
|
||||
return o[Symbol.iterator]() === o;
|
||||
}
|
||||
assert_true(is_iterator(t.keys()));
|
||||
assert_true(is_iterator(t.values()));
|
||||
assert_true(is_iterator(t.entries()));
|
||||
}, "Maplike are iterators");
|
||||
|
||||
test(function () {
|
||||
var t = new TestBindingMaplike();
|
||||
t.set("first", 0);
|
||||
t.set("second", 1);
|
||||
t.set("third", 2);
|
||||
assert_array_equals(collect(t.keys()), ["first", "second", "third"]);
|
||||
assert_array_equals(collect(t.values()), [0, 1, 2]);
|
||||
var expected = [["first", 0], ["second", 1], ["third", 2]];
|
||||
var i = 0;
|
||||
for (entry of t.entries()) {
|
||||
assert_array_equals(entry, expected[i++]);
|
||||
}
|
||||
|
||||
t.set("fourth", 3);
|
||||
assert_array_equals(collect(t.keys()), ["first", "second", "third", "fourth"]);
|
||||
assert_array_equals(collect(t.values()), [0, 1, 2, 3]);
|
||||
var expected = [["first", 0], ["second", 1], ["third", 2], ["fourth", 3]];
|
||||
var i = 0;
|
||||
for (entry of t.entries()) {
|
||||
assert_array_equals(entry, expected[i++]);
|
||||
}
|
||||
}, "Maplike iterate over key/value pairs");
|
61
third_party/WebIDL/WebIDL.py
vendored
61
third_party/WebIDL/WebIDL.py
vendored
|
@ -9022,6 +9022,67 @@ class Parser(Tokenizer):
|
|||
itr_iface.asyncIterableInterface = iface
|
||||
self._productions.append(itr_iface)
|
||||
iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
|
||||
if not iterable:
|
||||
# We haven't run finish() on the interface yet, so we don't know
|
||||
# whether our interface is maplike/setlike/iterable or not. This
|
||||
# means we have to loop through the members to see if we have an
|
||||
# iterable member.
|
||||
for m in iface.members:
|
||||
if isinstance(m, IDLMaplikeOrSetlike):
|
||||
iterable = m
|
||||
break
|
||||
if iterable and (iterable.isSetlike() or iterable.isMaplike()):
|
||||
|
||||
def simpleExtendedAttr(str):
|
||||
return IDLExtendedAttribute(iface.location, (str,))
|
||||
|
||||
if isinstance(iterable, IDLAsyncIterable):
|
||||
nextReturnType = IDLPromiseType(
|
||||
iterable.location, BuiltinTypes[IDLBuiltinType.Types.any]
|
||||
)
|
||||
else:
|
||||
nextReturnType = BuiltinTypes[IDLBuiltinType.Types.object]
|
||||
nextMethod = IDLMethod(
|
||||
iterable.location,
|
||||
IDLUnresolvedIdentifier(iterable.location, "next"),
|
||||
nextReturnType,
|
||||
[],
|
||||
)
|
||||
nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
|
||||
|
||||
methods = [nextMethod]
|
||||
|
||||
if iterable.isSetlike():
|
||||
itr_suffix = "Setlike"
|
||||
else:
|
||||
itr_suffix = "Maplike"
|
||||
itr_ident = IDLUnresolvedIdentifier(
|
||||
iface.location, iface.identifier.name + itr_suffix
|
||||
)
|
||||
classNameOverride = iface.identifier.name + " " + itr_suffix
|
||||
itr_iface = IDLInterface(
|
||||
iface.location,
|
||||
self.globalScope(),
|
||||
itr_ident,
|
||||
None,
|
||||
methods,
|
||||
isKnownNonPartial=True,
|
||||
classNameOverride=classNameOverride,
|
||||
)
|
||||
itr_iface.addExtendedAttributes(
|
||||
[simpleExtendedAttr("LegacyNoInterfaceObject")]
|
||||
)
|
||||
# Make sure the exposure set for the iterator interface is the
|
||||
# same as the exposure set for the iterable interface, because
|
||||
# we're going to generate methods on the iterable that return
|
||||
# instances of the iterator.
|
||||
itr_iface._exposureGlobalNames = set(iface._exposureGlobalNames)
|
||||
# Always append generated iterable interfaces after the
|
||||
# interface they're a member of, otherwise nativeType generation
|
||||
# won't work correctly.
|
||||
itr_iface.iterableInterface = iface
|
||||
self._productions.append(itr_iface)
|
||||
iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
|
||||
|
||||
# Make sure we finish IDLIncludesStatements before we finish the
|
||||
# IDLInterfaces.
|
||||
|
|
72
third_party/WebIDL/like-as-iterable.patch
vendored
Normal file
72
third_party/WebIDL/like-as-iterable.patch
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
diff --git a/third_party/WebIDL/WebIDL.py b/third_party/WebIDL/WebIDL.py
|
||||
index 2366e3f702..e1d973f5fe 100644
|
||||
--- a/third_party/WebIDL/WebIDL.py
|
||||
+++ b/third_party/WebIDL/WebIDL.py
|
||||
@@ -9022,6 +9022,67 @@ class Parser(Tokenizer):
|
||||
itr_iface.asyncIterableInterface = iface
|
||||
self._productions.append(itr_iface)
|
||||
iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
|
||||
+ if not iterable:
|
||||
+ # We haven't run finish() on the interface yet, so we don't know
|
||||
+ # whether our interface is maplike/setlike/iterable or not. This
|
||||
+ # means we have to loop through the members to see if we have an
|
||||
+ # iterable member.
|
||||
+ for m in iface.members:
|
||||
+ if isinstance(m, IDLMaplikeOrSetlike):
|
||||
+ iterable = m
|
||||
+ break
|
||||
+ if iterable and (iterable.isSetlike() or iterable.isMaplike()):
|
||||
+
|
||||
+ def simpleExtendedAttr(str):
|
||||
+ return IDLExtendedAttribute(iface.location, (str,))
|
||||
+
|
||||
+ if isinstance(iterable, IDLAsyncIterable):
|
||||
+ nextReturnType = IDLPromiseType(
|
||||
+ iterable.location, BuiltinTypes[IDLBuiltinType.Types.any]
|
||||
+ )
|
||||
+ else:
|
||||
+ nextReturnType = BuiltinTypes[IDLBuiltinType.Types.object]
|
||||
+ nextMethod = IDLMethod(
|
||||
+ iterable.location,
|
||||
+ IDLUnresolvedIdentifier(iterable.location, "next"),
|
||||
+ nextReturnType,
|
||||
+ [],
|
||||
+ )
|
||||
+ nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
|
||||
+
|
||||
+ methods = [nextMethod]
|
||||
+
|
||||
+ if iterable.isSetlike():
|
||||
+ itr_suffix = "Setlike"
|
||||
+ else:
|
||||
+ itr_suffix = "Maplike"
|
||||
+ itr_ident = IDLUnresolvedIdentifier(
|
||||
+ iface.location, iface.identifier.name + itr_suffix
|
||||
+ )
|
||||
+ classNameOverride = iface.identifier.name + " " + itr_suffix
|
||||
+ itr_iface = IDLInterface(
|
||||
+ iface.location,
|
||||
+ self.globalScope(),
|
||||
+ itr_ident,
|
||||
+ None,
|
||||
+ methods,
|
||||
+ isKnownNonPartial=True,
|
||||
+ classNameOverride=classNameOverride,
|
||||
+ )
|
||||
+ itr_iface.addExtendedAttributes(
|
||||
+ [simpleExtendedAttr("LegacyNoInterfaceObject")]
|
||||
+ )
|
||||
+ # Make sure the exposure set for the iterator interface is the
|
||||
+ # same as the exposure set for the iterable interface, because
|
||||
+ # we're going to generate methods on the iterable that return
|
||||
+ # instances of the iterator.
|
||||
+ itr_iface._exposureGlobalNames = set(iface._exposureGlobalNames)
|
||||
+ # Always append generated iterable interfaces after the
|
||||
+ # interface they're a member of, otherwise nativeType generation
|
||||
+ # won't work correctly.
|
||||
+ itr_iface.iterableInterface = iface
|
||||
+ self._productions.append(itr_iface)
|
||||
+ iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
|
||||
|
||||
# Make sure we finish IDLIncludesStatements before we finish the
|
||||
# IDLInterfaces.
|
1
third_party/WebIDL/update.sh
vendored
1
third_party/WebIDL/update.sh
vendored
|
@ -5,6 +5,7 @@ patch < callback-location.patch
|
|||
patch < union-typedef.patch
|
||||
patch < inline.patch
|
||||
patch < readable-stream.patch
|
||||
patch < like-as-iterable.patch
|
||||
|
||||
wget https://hg.mozilla.org/mozilla-central/archive/tip.zip/dom/bindings/parser/tests/ -O tests.zip
|
||||
rm -r tests
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue