From 3cf4ef61efce446d46634eecfee0383296d5a7b5 Mon Sep 17 00:00:00 2001 From: Mukilan Thiyagarajan Date: Thu, 27 Feb 2025 12:34:11 +0530 Subject: [PATCH] bindings: Fix support for interface members in setlike/maplike. (#35651) #30151 added support for setlike and maplike declarations in WebIDL, but the tests only validated if generator code worked with 'DOMString' as the key type. The support for interface type as members was broken as `DomRoot` didn't satify the bounds `Eq` and `Hash` needed by the `Key` and `Value` types in `Setlike` and `Maplike` traits respectively. In addition, the splitting of bindings into a separate 'script_bindings' crate had also broken support for this in CodegenRust.py, as the types used within the definition of `DomTraits` were not referenced using `Self::`. This patch fixes the WebIDL code generator by doing a simple string replacement on the return value of `getRetvalDeclarationForType` so that the proper `Self::` is used. I'm not not sure if there is a better approach to this as it seems most logic in CodegenRust.py uses the `D::` prefix that is expected to be available only when compiling `script` crate and not `script_bindings`. This patch also adds the missing trait implementations for `DomRoot` and ensures that the generated code works for both members of primitive and interface types by splitting the existing `TestBinding{Map,Set}Like` interfaces into `TestBinding{Map,Set}LikeWith{Primitive,Interface}` tests. Fixes #35542. Signed-off-by: Mukilan Thiyagarajan --- components/script/dom/mod.rs | 6 +- .../dom/testbindingmaplikewithinterface.rs | 100 ++++ ....rs => testbindingmaplikewithprimitive.rs} | 20 +- .../dom/testbindingsetlikewithinterface.rs | 71 +++ ....rs => testbindingsetlikewithprimitive.rs} | 20 +- .../script_bindings/codegen/CodegenRust.py | 22 +- components/script_bindings/root.rs | 8 + .../TestBindingMaplikeWithInterface.webidl | 20 + ...=> TestBindingMaplikeWithPrimitive.webidl} | 2 +- .../TestBindingSetlikeWithInterface.webidl | 14 + ...=> TestBindingSetlikeWithPrimitive.webidl} | 2 +- .../mozilla/like.any.js.ini | 18 +- tests/wpt/mozilla/meta/MANIFEST.json | 2 +- .../wpt/mozilla/meta/mozilla/like.any.js.ini | 19 +- tests/wpt/mozilla/tests/mozilla/like.any.js | 512 ++++++++++-------- 15 files changed, 570 insertions(+), 266 deletions(-) create mode 100644 components/script/dom/testbindingmaplikewithinterface.rs rename components/script/dom/{testbindingmaplike.rs => testbindingmaplikewithprimitive.rs} (78%) create mode 100644 components/script/dom/testbindingsetlikewithinterface.rs rename components/script/dom/{testbindingsetlike.rs => testbindingsetlikewithprimitive.rs} (72%) create mode 100644 components/script_bindings/webidls/TestBindingMaplikeWithInterface.webidl rename components/script_bindings/webidls/{TestBindingMaplike.webidl => TestBindingMaplikeWithPrimitive.webidl} (93%) create mode 100644 components/script_bindings/webidls/TestBindingSetlikeWithInterface.webidl rename components/script_bindings/webidls/{TestBindingSetlike.webidl => TestBindingSetlikeWithPrimitive.webidl} (90%) diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 73320e527b4..340734bfe5a 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -544,10 +544,12 @@ pub(crate) mod svggraphicselement; pub(crate) mod svgsvgelement; pub(crate) mod testbinding; pub(crate) mod testbindingiterable; -pub(crate) mod testbindingmaplike; +pub(crate) mod testbindingmaplikewithinterface; +pub(crate) mod testbindingmaplikewithprimitive; pub(crate) mod testbindingpairiterable; pub(crate) mod testbindingproxy; -pub(crate) mod testbindingsetlike; +pub(crate) mod testbindingsetlikewithinterface; +pub(crate) mod testbindingsetlikewithprimitive; pub(crate) mod testns; pub(crate) mod testworklet; pub(crate) mod testworkletglobalscope; diff --git a/components/script/dom/testbindingmaplikewithinterface.rs b/components/script/dom/testbindingmaplikewithinterface.rs new file mode 100644 index 00000000000..1297f89e7fd --- /dev/null +++ b/components/script/dom/testbindingmaplikewithinterface.rs @@ -0,0 +1,100 @@ +/* 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 dom_struct::dom_struct; +use indexmap::IndexMap; +use js::rust::HandleObject; + +use super::bindings::error::Error; +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::TestBindingMaplikeWithInterfaceBinding::TestBindingMaplikeWithInterfaceMethods; +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::dom::testbinding::TestBinding; +use crate::maplike; +use crate::script_runtime::CanGc; + +/// maplike +#[dom_struct] +pub(crate) struct TestBindingMaplikeWithInterface { + reflector: Reflector, + #[custom_trace] + internal: DomRefCell>>, +} + +impl TestBindingMaplikeWithInterface { + fn new( + global: &GlobalScope, + proto: Option, + can_gc: CanGc, + ) -> DomRoot { + reflect_dom_object_with_proto( + Box::new(TestBindingMaplikeWithInterface { + reflector: Reflector::new(), + internal: DomRefCell::new(IndexMap::new()), + }), + global, + proto, + can_gc, + ) + } +} + +impl TestBindingMaplikeWithInterfaceMethods + for TestBindingMaplikeWithInterface +{ + fn Constructor( + global: &GlobalScope, + proto: Option, + can_gc: CanGc, + ) -> Fallible> { + Ok(TestBindingMaplikeWithInterface::new(global, proto, can_gc)) + } + + fn SetInternal(&self, key: DOMString, value: &TestBinding) { + let value = DomRoot::from_ref(value); + 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> { + // TODO: error type? + self.internal + .borrow() + .get(&key) + .ok_or_else(|| Error::Type(format!("No entry for key {key}"))) + .cloned() + } + + 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 +#[cfg_attr(crown, allow(crown::unrooted_must_root))] +impl Maplike for TestBindingMaplikeWithInterface { + type Key = DOMString; + type Value = DomRoot; + + maplike!(self, internal); +} diff --git a/components/script/dom/testbindingmaplike.rs b/components/script/dom/testbindingmaplikewithprimitive.rs similarity index 78% rename from components/script/dom/testbindingmaplike.rs rename to components/script/dom/testbindingmaplikewithprimitive.rs index d055c68e12e..4768bb246c0 100644 --- a/components/script/dom/testbindingmaplike.rs +++ b/components/script/dom/testbindingmaplikewithprimitive.rs @@ -10,7 +10,7 @@ use js::rust::HandleObject; use super::bindings::error::Error; use crate::dom::bindings::cell::DomRefCell; -use crate::dom::bindings::codegen::Bindings::TestBindingMaplikeBinding::TestBindingMaplikeMethods; +use crate::dom::bindings::codegen::Bindings::TestBindingMaplikeWithPrimitiveBinding::TestBindingMaplikeWithPrimitiveMethods; use crate::dom::bindings::error::Fallible; use crate::dom::bindings::like::Maplike; use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector}; @@ -22,20 +22,20 @@ use crate::script_runtime::CanGc; /// maplike #[dom_struct] -pub(crate) struct TestBindingMaplike { +pub(crate) struct TestBindingMaplikeWithPrimitive { reflector: Reflector, #[custom_trace] internal: DomRefCell>, } -impl TestBindingMaplike { +impl TestBindingMaplikeWithPrimitive { fn new( global: &GlobalScope, proto: Option, can_gc: CanGc, - ) -> DomRoot { + ) -> DomRoot { reflect_dom_object_with_proto( - Box::new(TestBindingMaplike { + Box::new(TestBindingMaplikeWithPrimitive { reflector: Reflector::new(), internal: DomRefCell::new(IndexMap::new()), }), @@ -46,13 +46,15 @@ impl TestBindingMaplike { } } -impl TestBindingMaplikeMethods for TestBindingMaplike { +impl TestBindingMaplikeWithPrimitiveMethods + for TestBindingMaplikeWithPrimitive +{ fn Constructor( global: &GlobalScope, proto: Option, can_gc: CanGc, - ) -> Fallible> { - Ok(TestBindingMaplike::new(global, proto, can_gc)) + ) -> Fallible> { + Ok(TestBindingMaplikeWithPrimitive::new(global, proto, can_gc)) } fn SetInternal(&self, key: DOMString, value: i32) { @@ -88,7 +90,7 @@ impl TestBindingMaplikeMethods for TestBindingMaplike { // this error is wrong because if we inline Self::Key and Self::Value all errors are gone // TODO: FIX THIS #[cfg_attr(crown, allow(crown::unrooted_must_root))] -impl Maplike for TestBindingMaplike { +impl Maplike for TestBindingMaplikeWithPrimitive { type Key = DOMString; type Value = i32; diff --git a/components/script/dom/testbindingsetlikewithinterface.rs b/components/script/dom/testbindingsetlikewithinterface.rs new file mode 100644 index 00000000000..26439b20103 --- /dev/null +++ b/components/script/dom/testbindingsetlikewithinterface.rs @@ -0,0 +1,71 @@ +/* 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 dom_struct::dom_struct; +use indexmap::IndexSet; +use js::rust::HandleObject; + +use super::bindings::like::Setlike; +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::TestBindingSetlikeWithInterfaceBinding::TestBindingSetlikeWithInterfaceMethods; +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::globalscope::GlobalScope; +use crate::dom::testbinding::TestBinding; +use crate::script_runtime::CanGc; +use crate::setlike; + +// setlike +#[dom_struct] +pub(crate) struct TestBindingSetlikeWithInterface { + reflector: Reflector, + #[custom_trace] + internal: DomRefCell>>, +} + +impl TestBindingSetlikeWithInterface { + fn new( + global: &GlobalScope, + proto: Option, + can_gc: CanGc, + ) -> DomRoot { + reflect_dom_object_with_proto( + Box::new(TestBindingSetlikeWithInterface { + reflector: Reflector::new(), + internal: DomRefCell::new(IndexSet::new()), + }), + global, + proto, + can_gc, + ) + } +} + +impl TestBindingSetlikeWithInterfaceMethods + for TestBindingSetlikeWithInterface +{ + fn Constructor( + global: &GlobalScope, + proto: Option, + can_gc: CanGc, + ) -> Fallible> { + Ok(TestBindingSetlikeWithInterface::new(global, proto, can_gc)) + } + + 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 +#[cfg_attr(crown, allow(crown::unrooted_must_root))] +impl Setlike for TestBindingSetlikeWithInterface { + type Key = DomRoot; + + setlike!(self, internal); +} diff --git a/components/script/dom/testbindingsetlike.rs b/components/script/dom/testbindingsetlikewithprimitive.rs similarity index 72% rename from components/script/dom/testbindingsetlike.rs rename to components/script/dom/testbindingsetlikewithprimitive.rs index d6fb9e05fd7..f1467b18b15 100644 --- a/components/script/dom/testbindingsetlike.rs +++ b/components/script/dom/testbindingsetlikewithprimitive.rs @@ -10,7 +10,7 @@ use js::rust::HandleObject; use super::bindings::like::Setlike; use crate::dom::bindings::cell::DomRefCell; -use crate::dom::bindings::codegen::Bindings::TestBindingSetlikeBinding::TestBindingSetlikeMethods; +use crate::dom::bindings::codegen::Bindings::TestBindingSetlikeWithPrimitiveBinding::TestBindingSetlikeWithPrimitiveMethods; use crate::dom::bindings::error::Fallible; use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector}; use crate::dom::bindings::root::DomRoot; @@ -21,20 +21,20 @@ use crate::setlike; // setlike #[dom_struct] -pub(crate) struct TestBindingSetlike { +pub(crate) struct TestBindingSetlikeWithPrimitive { reflector: Reflector, #[custom_trace] internal: DomRefCell>, } -impl TestBindingSetlike { +impl TestBindingSetlikeWithPrimitive { fn new( global: &GlobalScope, proto: Option, can_gc: CanGc, - ) -> DomRoot { + ) -> DomRoot { reflect_dom_object_with_proto( - Box::new(TestBindingSetlike { + Box::new(TestBindingSetlikeWithPrimitive { reflector: Reflector::new(), internal: DomRefCell::new(IndexSet::new()), }), @@ -45,13 +45,15 @@ impl TestBindingSetlike { } } -impl TestBindingSetlikeMethods for TestBindingSetlike { +impl TestBindingSetlikeWithPrimitiveMethods + for TestBindingSetlikeWithPrimitive +{ fn Constructor( global: &GlobalScope, proto: Option, can_gc: CanGc, - ) -> Fallible> { - Ok(TestBindingSetlike::new(global, proto, can_gc)) + ) -> Fallible> { + Ok(TestBindingSetlikeWithPrimitive::new(global, proto, can_gc)) } fn Size(&self) -> u32 { @@ -62,7 +64,7 @@ impl TestBindingSetlikeMethods for TestBindingSetlike { // this error is wrong because if we inline Self::Key and Self::Value all errors are gone // TODO: FIX THIS #[cfg_attr(crown, allow(crown::unrooted_must_root))] -impl Setlike for TestBindingSetlike { +impl Setlike for TestBindingSetlikeWithPrimitive { type Key = DOMString; setlike!(self, internal); diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py index 8ad55f4657e..ffd2589376a 100644 --- a/components/script_bindings/codegen/CodegenRust.py +++ b/components/script_bindings/codegen/CodegenRust.py @@ -2688,6 +2688,10 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, ] joinedTraits = ' + '.join(traits) elements = [CGGeneric(f"pub(crate) trait DomTypes: {joinedTraits} where Self: 'static {{\n")] + + def fixupInterfaceTypeReferences(typename): + return typename.replace("D::", "Self::") + for descriptor in descriptors: iface_name = descriptor.interface.identifier.name traits = [] @@ -2711,11 +2715,17 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable if iterableDecl: if iterableDecl.isMaplike(): - keytype = getRetvalDeclarationForType(iterableDecl.keyType, None).define() - valuetype = getRetvalDeclarationForType(iterableDecl.valueType, None).define() + keytype = fixupInterfaceTypeReferences( + getRetvalDeclarationForType(iterableDecl.keyType, descriptor).define() + ) + valuetype = fixupInterfaceTypeReferences( + getRetvalDeclarationForType(iterableDecl.valueType, descriptor).define() + ) traits += [f"crate::dom::bindings::like::Maplike"] if iterableDecl.isSetlike(): - keytype = getRetvalDeclarationForType(iterableDecl.keyType, None).define() + keytype = fixupInterfaceTypeReferences( + getRetvalDeclarationForType(iterableDecl.keyType, descriptor).define() + ) traits += [f"crate::dom::bindings::like::Setlike"] if iterableDecl.hasKeyType(): traits += [ @@ -2769,7 +2779,11 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, CGGeneric(f" type {firstCap(iface_name)}: {' + '.join(traits)};\n") ] elements += [CGGeneric("}\n")] - return CGList([CGGeneric("use crate::dom::bindings::str::DOMString;\n")] + elements) + imports = [ + CGGeneric("use crate::dom::bindings::root::DomRoot;\n"), + CGGeneric("use crate::dom::bindings::str::DOMString;\n"), + ] + return CGList(imports + elements) def DomTypeHolder(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, config): diff --git a/components/script_bindings/root.rs b/components/script_bindings/root.rs index c18c30cfae7..c573e163882 100644 --- a/components/script_bindings/root.rs +++ b/components/script_bindings/root.rs @@ -351,6 +351,14 @@ where } } +impl Eq for DomRoot {} + +impl Hash for DomRoot { + fn hash(&self, state: &mut H) { + self.value.hash(state); + } +} + impl Clone for DomRoot where T: DomObject, diff --git a/components/script_bindings/webidls/TestBindingMaplikeWithInterface.webidl b/components/script_bindings/webidls/TestBindingMaplikeWithInterface.webidl new file mode 100644 index 00000000000..1bdd45d7698 --- /dev/null +++ b/components/script_bindings/webidls/TestBindingMaplikeWithInterface.webidl @@ -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 TestBindingMaplikeWithInterface { + [Throws] + constructor(); + + maplike; + undefined setInternal(DOMString aKey, TestBinding aValue); + undefined clearInternal(); + boolean deleteInternal(DOMString aKey); + boolean hasInternal(DOMString aKey); + [Throws] + TestBinding getInternal(DOMString aKey); +}; diff --git a/components/script_bindings/webidls/TestBindingMaplike.webidl b/components/script_bindings/webidls/TestBindingMaplikeWithPrimitive.webidl similarity index 93% rename from components/script_bindings/webidls/TestBindingMaplike.webidl rename to components/script_bindings/webidls/TestBindingMaplikeWithPrimitive.webidl index 8debb804a3e..f0e77fbcae4 100644 --- a/components/script_bindings/webidls/TestBindingMaplike.webidl +++ b/components/script_bindings/webidls/TestBindingMaplikeWithPrimitive.webidl @@ -6,7 +6,7 @@ // web pages. [Pref="dom_testbinding_enabled", Exposed=(Window,Worker)] -interface TestBindingMaplike { +interface TestBindingMaplikeWithPrimitive { [Throws] constructor(); diff --git a/components/script_bindings/webidls/TestBindingSetlikeWithInterface.webidl b/components/script_bindings/webidls/TestBindingSetlikeWithInterface.webidl new file mode 100644 index 00000000000..12254b62077 --- /dev/null +++ b/components/script_bindings/webidls/TestBindingSetlikeWithInterface.webidl @@ -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 TestBindingSetlikeWithInterface { + [Throws] + constructor(); + + setlike; +}; diff --git a/components/script_bindings/webidls/TestBindingSetlike.webidl b/components/script_bindings/webidls/TestBindingSetlikeWithPrimitive.webidl similarity index 90% rename from components/script_bindings/webidls/TestBindingSetlike.webidl rename to components/script_bindings/webidls/TestBindingSetlikeWithPrimitive.webidl index 16b264bb9f6..7f675a62187 100644 --- a/components/script_bindings/webidls/TestBindingSetlike.webidl +++ b/components/script_bindings/webidls/TestBindingSetlikeWithPrimitive.webidl @@ -6,7 +6,7 @@ // web pages. [Pref="dom_testbinding_enabled", Exposed=(Window,Worker)] -interface TestBindingSetlike { +interface TestBindingSetlikeWithPrimitive { [Throws] constructor(); diff --git a/tests/wpt/mozilla/meta-legacy-layout/mozilla/like.any.js.ini b/tests/wpt/mozilla/meta-legacy-layout/mozilla/like.any.js.ini index a4040cad82c..a7f974265a7 100644 --- a/tests/wpt/mozilla/meta-legacy-layout/mozilla/like.any.js.ini +++ b/tests/wpt/mozilla/meta-legacy-layout/mozilla/like.any.js.ini @@ -1,23 +1,29 @@ [like.any.html] type: testharness prefs: [dom_testbinding_enabled:true] - [Test defaulting arguments on setlike to undefined] + [setlike - Default arguments for r/w methods is undefined] expected: FAIL - [Simple map creation and functionality test] + [maplike - 'get' with a bogus key should return undefined] expected: FAIL - [Test defaulting arguments on maplike to undefined] + [maplike - 'get' with a bogus key should return undefined] + expected: FAIL + + [maplike - Default arguments for r/w methods is undefined] expected: FAIL [like.any.worker.html] type: testharness prefs: [dom_testbinding_enabled:true] - [Test defaulting arguments on setlike to undefined] + [setlike - Default arguments for r/w methods is undefined] expected: FAIL - [Simple map creation and functionality test] + [maplike - 'get' with a bogus key should return undefined] expected: FAIL - [Test defaulting arguments on maplike to undefined] + [maplike - 'get' with a bogus key should return undefined] + expected: FAIL + + [maplike - Default arguments for r/w methods is undefined] expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 7f63b5e2079..e23bcd9ea24 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -13538,7 +13538,7 @@ ] ], "like.any.js": [ - "11e4d86325ce6216754a0e0b760c59beb812db85", + "30d8007fb5cbef2847e36fe9b4cb9e6535aaf1e0", [ "mozilla/like.any.html", { diff --git a/tests/wpt/mozilla/meta/mozilla/like.any.js.ini b/tests/wpt/mozilla/meta/mozilla/like.any.js.ini index a4040cad82c..8cf36465a0e 100644 --- a/tests/wpt/mozilla/meta/mozilla/like.any.js.ini +++ b/tests/wpt/mozilla/meta/mozilla/like.any.js.ini @@ -1,23 +1,30 @@ [like.any.html] type: testharness prefs: [dom_testbinding_enabled:true] - [Test defaulting arguments on setlike to undefined] + [setlike - Default arguments for r/w methods is undefined] expected: FAIL - [Simple map creation and functionality test] + [maplike - 'get' with a bogus key should return undefined] expected: FAIL - [Test defaulting arguments on maplike to undefined] + [maplike - 'get' with a bogus key should return undefined] expected: FAIL + [maplike - Default arguments for r/w methods is undefined] + expected: FAIL + + [like.any.worker.html] type: testharness prefs: [dom_testbinding_enabled:true] - [Test defaulting arguments on setlike to undefined] + [setlike - Default arguments for r/w methods is undefined] expected: FAIL - [Simple map creation and functionality test] + [maplike - 'get' with a bogus key should return undefined] expected: FAIL - [Test defaulting arguments on maplike to undefined] + [maplike - 'get' with a bogus key should return undefined] + expected: FAIL + + [maplike - Default arguments for r/w methods is undefined] expected: FAIL diff --git a/tests/wpt/mozilla/tests/mozilla/like.any.js b/tests/wpt/mozilla/tests/mozilla/like.any.js index 11e4d86325c..30d8007fb5c 100644 --- a/tests/wpt/mozilla/tests/mozilla/like.any.js +++ b/tests/wpt/mozilla/tests/mozilla/like.any.js @@ -59,250 +59,308 @@ var testExistence = function testExistence(prefix, obj, properties) { } }; -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"); +let setLikeTests = [ + { + setConstructor: TestBindingSetlikeWithPrimitive, + testValues: ["first", "second", "third", "fourth"], + memberType: "string" + }, + { + setConstructor: TestBindingSetlikeWithInterface, + testValues: [new TestBinding(), new TestBinding(), new TestBinding(), new TestBinding()], + memberType: "TestBinding" + }, +]; + +for (const { setConstructor, testValues, memberType } of setLikeTests) { + test(function () { + var s = new setConstructor(); + assert_true(ok(s), `got a ${setConstructor.name} object`); + testExistence(setConstructor.name, s, setlike_rw_properties); + assert_equals(s.size, 0, "size of new set should be zero"); + const testValue1 = testValues[0]; + assert_true(!s.has(testValue1), "has() should return false for bogus value"); + s1 = s.add(testValue1); + assert_equals(s, s1, "set.add() should be a chainable method"); + assert_equals(s.size, 1, "size should be 1"); + assert_true(s.has(testValue1), "has() should return true for the first test value"); + const testValue2 = testValues[1]; + s.add(testValue2); + assert_equals(s.size, 2, "size should be 2"); + testIndex = 0; + s.forEach(function (v, k, o) { + "use strict"; + assert_equals(o, s, "forEach obj is correct"); + assert_equals(k, testValues[testIndex], "forEach set key: " + k + " = " + testValues[testIndex]); + testIndex += 1; + }); + assert_equals(testIndex, 2, "forEach ran correct number of times"); + assert_true(s.has(testValue2), "maplike has should return true for second key"); + assert_equals(s.delete(testValue2), true, "maplike deletion should return true"); + assert_equals(s.size, 1, "size should be 1"); + iterable = false; + for (let e of s) { + iterable = true; + assert_equals(e, testValue1, "iterable first array element should be first test key"); + } + assert_equals(s[Symbol.iterator].length, 0, "@@iterator symbol is correct length"); + assert_equals(s[Symbol.iterator].name, "values", "@@iterator symbol has correct name"); + assert_equals(s[Symbol.iterator], s.values, '@@iterator is an alias for "values"'); + assert_true(ok(iterable), " @@iterator symbol resolved correctly"); + for (let k of s.keys()) { + assert_equals(k, testValue1, "first element of keys() should be the first test key"); + } + for (let v of s.values()) { + assert_equals(v, testValue1, "first element of values() should be the first test value"); + } + for (let e of s.entries()) { + assert_equals(e[0], testValue1, "first element of entries() should be the first test value"); + assert_equals(e[1], testValue1, "second element of entries() should be the second test value"); + } + s.clear(); + assert_equals(s.size, 0, "size should be 0 after clear"); + }, `setlike<${memberType}> - Basic set operations`); + + test(function () { + // Test this override for forEach + s = new setConstructor(); + s.add(testValues[0]); + s.forEach(function (v, k, o) { + "use strict"; + assert_equals(o, s, "forEach obj is correct"); + assert_equals(this, 5, "'this' value should be correct"); + }, 5); + }, `setke<${memberType}> - Test 'this' override for 'forEach'`); + + // some iterable test ported to *like interfaces + test(function () { + var s = new setConstructor(); + var empty = true; + s.forEach(function () { empty = false; }); + assert_true(empty); + }, `Empty setlike<${memberType}>`); + + test(function () { + var s = new setConstructor(); + function is_iterator(o) { + return o[Symbol.iterator]() === o; + } + assert_true(is_iterator(s.keys())); + assert_true(is_iterator(s.values())); + assert_true(is_iterator(s.entries())); + }, `setlike<${memberType}> are iterators`); + + test(function () { + var s = new setConstructor(); + s.add(testValues[0]); + s.add(testValues[1]); + s.add(testValues[2]); + assert_array_equals(collect(s.keys()), collect(s.values())); + assert_array_equals(collect(s.values()), testValues.slice(0, 3)); + var i = 0; + for (entry of s.entries()) { + assert_array_equals(entry, [testValues[i], testValues[i]]); + i += 1; + } + + s.add(testValues[3]); + assert_array_equals(collect(s.keys()), collect(s.values())); + assert_array_equals(collect(s.values()), testValues); + var i = 0; + for (entry of s.entries()) { + assert_array_equals(entry, [testValues[i], testValues[i]]); + i += 1; + } + }, `setlike<${memberType}> - Iterators iterate over values`); +} test(function () { - var m = new TestBindingSetlike(); + var m = new TestBindingSetlikeWithPrimitive(); m.add(); - assert_equals(m.size, 1, "SetArgsDefault: should have 1 entry"); + assert_equals(m.size, 1, "set 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"); + assert_equals(typeof k, "string", "key must be a string"); + assert_equals(k, "undefined", "key is the string undefined"); }); m.delete(); - assert_equals(m.size, 0, "SetArgsDefault: should have 0 entries"); -}, "Test defaulting arguments on setlike to undefined"); + assert_equals(m.size, 0, "after deleting key, set should have 0 entries"); +}, "setlike - Default arguments for r/w methods is 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"); +let mapLikeTests = [ + { + mapConstructor: TestBindingMaplikeWithPrimitive, + testEntries: [["first", 1], ["second", 2], ["third", 3], ["fourth", 4]], + valueType: "number" + }, + { + mapConstructor: TestBindingMaplikeWithInterface, + testEntries: [ + ["first", new TestBinding()], + ["second", new TestBinding()], + ["third", new TestBinding()], + ["fourth", new TestBinding()], + ], + valueType: "TestBinding" + }, +]; -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"); +for (const { mapConstructor, testEntries, valueType } of mapLikeTests) { + test(function () { + m = new mapConstructor(); + assert_true(ok(m), `got a ${mapConstructor.name} object`); + assert_equals(m.get("test"), undefined, "get(bogusKey) is undefined"); + }, `maplike - 'get' with a bogus key should return undefined`); -// 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 () { + // Simple map creation and functionality test + m = new mapConstructor(); + assert_true(ok(m), `got a ${mapConstructor.name} object`); + testExistence(mapConstructor.name, m, maplike_rw_properties); + assert_equals(m.size, 0, "size of new map should be zero"); + let [testKey1, testValue1] = testEntries[0]; + assert_true(!m.has(testKey1), "maplike has should return false for bogus key"); + var m1 = m.set(testKey1, testValue1); + assert_equals(m, m1, "map.set should be a chainable method"); + assert_equals(m.size, 1, "size should be 1"); + assert_true(m.has(testKey1), "has() should return true for key already added"); + assert_equals(m.get(testKey1), testValue1, "get(testKey1) should return the same value provided to set()"); + let [testKey2, testValue2] = testEntries[1]; + m.set(testKey2, testValue2); + assert_equals(m.size, 2, "size should be 2"); + testSet = testEntries.slice(0, 2); + testIndex = 0; + m.forEach(function (v, k, o) { + "use strict"; + assert_equals(o, m, "forEach obj is correct"); + assert_equals(k, testSet[testIndex][0], "forEach map key: " + k + " = " + testSet[testIndex][0]); + assert_equals(v, testSet[testIndex][1], "forEach map value: " + v + " = " + testSet[testIndex][1]); + testIndex += 1; + }); + assert_equals(testIndex, 2, "forEach ran correct number of times"); + assert_true(m.has(testKey2), "has() should return true for second test key"); + assert_equals(m.get(testKey2), testValue2, "get(testKey2) should return the same value provided to set()"); + assert_equals(m.delete(testKey2), true, "maplike deletion should return boolean"); + assert_equals(m.size, 1, "size should be 1"); + var iterable = false; + for (let e of m) { + iterable = true; + assert_equals(e[0], testKey1, "iterable's first array element should be the first test key"); + assert_equals(e[1], testValue1, "iterable' second array element should be the first test value"); + } + assert_equals(m[Symbol.iterator].length, 0, "@@iterator symbol is correct length"); + assert_equals(m[Symbol.iterator].name, "entries", "@@iterator symbol has correct name"); + assert_equals(m[Symbol.iterator], m.entries, '@@iterator is an alias for "entries"'); + assert_true(ok(iterable), "@@iterator symbol resolved correctly"); + for (let k of m.keys()) { + assert_equals(k, testKey1, "first element of keys() should be the first test key"); + } + for (let v of m.values()) { + assert_equals(v, testValue1, "first element of values() should be 1"); + } + for (let e of m.entries()) { + assert_equals(e[0], testKey1, "first element of entries() should have the first test key"); + assert_equals(e[1], testValue1, "first element of entries() should have the second test value"); + } + m.clear(); + assert_equals(m.size, 0, "size should be 0 after clear"); + }, `maplike - Simple map creation and functionality test`); + + test(function () { + // Map convenience function test + m = new mapConstructor(); + assert_true(ok(m), `got a ${mapConstructor.name} object`); + assert_equals(m.size, 0, "size should be zero"); + assert_true(!m.hasInternal("test"), "hasInternal() should return false for bogus key"); + // 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"); + let [testKey1, testValue1] = testEntries[0]; + m.setInternal(testKey1, testValue1); + assert_equals(m.size, 1, "size should be 1 after adding first test key/value"); + assert_true(m.hasInternal(testKey1), "hasInternal() should return true"); + assert_equals(m.get(testKey1), testValue1, "get() should return the value set using setInternal()"); + assert_equals(m.getInternal(testKey1), testValue1, "getInternal() should return the value set using setInternal()"); + let [testKey2, testValue2] = testEntries[1]; + m.setInternal(testKey2, testValue2); + assert_equals(m.size, 2, "size should be 2"); + assert_true(m.hasInternal(testKey2), "hasInternal() should return true for newly added second test key"); + assert_equals(m.get(testKey2), testValue2, "get(testKey2) should return the value set using setInternal"); + assert_equals(m.getInternal(testKey2), testValue2, "getInternal(testKey2) should return the value set using setInternal()"); + assert_equals(m.deleteInternal(testKey2), true, "deleteInternal should return true when deleting existing key"); + assert_equals(m.size, 1, "size should be 1"); + m.clearInternal(); + assert_equals(m.size, 0, "size should be 0 after clearInternal"); + }, `Convenience methods for maplike`); + + // JS implemented map creation convenience function test + test(function () { + // Test this override for forEach + m = new mapConstructor(); + m.set(testEntries[0][0], testEntries[1][1]); + m.forEach(function (v, k, o) { + "use strict"; + assert_equals(o, m, "forEach obj is correct"); + assert_equals(this, 5, "'this' value should be correct"); + }, 5); + }, `maplike - Test 'this' override for 'forEach'`); + + // some iterable test ported to *like interfaces + test(function () { + var t = new mapConstructor(); + var empty = true; + t.forEach(function () { empty = false; }); + assert_true(empty); + }, `maplike - Empty maplike`); + + test(function () { + var t = new mapConstructor(); + 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 - Maplike are iterators`); + + test(function () { + var t = new mapConstructor(); + t.set(testEntries[0][0], testEntries[0][1]); + t.set(testEntries[1][0], testEntries[1][1]); + t.set(testEntries[2][0], testEntries[2][1]); + assert_array_equals(collect(t.keys()), [testEntries[0][0], testEntries[1][0], testEntries[2][0]]); + assert_array_equals(collect(t.values()), [testEntries[0][1], testEntries[1][1], testEntries[2][1]]); + var expected = testEntries.slice(0, 3); + var i = 0; + for (entry of t.entries()) { + assert_array_equals(entry, expected[i++]); + } + + t.set(testEntries[3][0], testEntries[3][1]); + assert_array_equals(collect(t.keys()), testEntries.map(entry => entry[0])); + assert_array_equals(collect(t.values()), testEntries.map(entry => entry[1])); + var expected = testEntries.slice(); + var i = 0; + for (entry of t.entries()) { + assert_array_equals(entry, expected[i++]); + } + }, `maplike - Maplike iteratable over key/value pairs`); +} test(function () { // Test defaulting arguments on maplike to undefined - m = new TestBindingMaplike(); + m = new TestBindingMaplikeWithPrimitive(); m.set(); - assert_equals(m.size, 1, "MapArgsDefault: should have 1 entry"); + assert_equals(m.size, 1, "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(typeof k, "string", "defaulted key must be a string"); + assert_equals(k, "undefined", "defaulted key must be the string undefined"); + assert_equals(v, 0, "defaulted value must be 0"); }); - assert_equals(m.get(), 0, "MapArgsDefault: no argument to get() returns correct value"); + assert_equals(m.get(), 0, "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"); +}, "maplike - Default arguments for r/w methods is 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");