From 4ad1a8ddccc29c8502ac43066922b40102e166a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 14 Jan 2016 04:41:04 +0100 Subject: [PATCH 1/5] webidl: Implement sequences in unions Unblocks #9053 --- .../dom/bindings/codegen/CodegenRust.py | 52 +++++++++++-------- components/script/dom/testbinding.rs | 7 +++ .../script/dom/webidls/TestBinding.webidl | 4 ++ 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 2a04db77cc9..643c396febc 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -1221,7 +1221,7 @@ def getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs): return "StringificationBehavior::Default" else: return treatAs[treatNullAs] - if type.isInteger(): + if type.isPrimitive() and type.isInteger(): if isEnforceRange: return "ConversionBehavior::EnforceRange" elif isClamp: @@ -3571,8 +3571,8 @@ def getUnionTypeTemplateVars(type, descriptorProvider): # for getJSToNativeConversionInfo. # Also, for dictionaries we would need to handle conversion of # null/undefined to the dictionary correctly. - if type.isDictionary() or type.isSequence(): - raise TypeError("Can't handle dictionaries or sequences in unions") + if type.isDictionary(): + raise TypeError("Can't handle dictionaries in unions") if type.isGeckoInterface(): name = type.inner.identifier.name @@ -3580,7 +3580,11 @@ def getUnionTypeTemplateVars(type, descriptorProvider): elif type.isEnum(): name = type.inner.identifier.name typeName = name - elif type.isArray() or type.isSequence(): + elif type.isSequence(): + name = type.name + inner = getUnionTypeTemplateVars(type.unroll(), descriptorProvider) + typeName = "Vec<" + inner["typeName"] + ">" + elif type.isArray(): name = str(type) # XXXjdm dunno about typeName here typeName = "/*" + type.name + "*/" @@ -3664,22 +3668,22 @@ class CGUnionConversionStruct(CGThing): names = [] conversions = [] + def get_name(memberType): + if self.type.isGeckoInterface(): + return memberType.inner.identifier.name + + return memberType.name + + def get_match(name): + return ( + "match %s::TryConvertTo%s(cx, value) {\n" + " Err(_) => return Err(()),\n" + " Ok(Some(value)) => return Ok(%s::e%s(value)),\n" + " Ok(None) => (),\n" + "}\n") % (self.type, name, self.type, name) + interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes) if len(interfaceMemberTypes) > 0: - def get_name(memberType): - if self.type.isGeckoInterface(): - return memberType.inner.identifier.name - - return memberType.name - - def get_match(name): - return ( - "match %s::TryConvertTo%s(cx, value) {\n" - " Err(_) => return Err(()),\n" - " Ok(Some(value)) => return Ok(%s::e%s(value)),\n" - " Ok(None) => (),\n" - "}\n") % (self.type, name, self.type, name) - typeNames = [get_name(memberType) for memberType in interfaceMemberTypes] interfaceObject = CGList(CGGeneric(get_match(typeName)) for typeName in typeNames) names.extend(typeNames) @@ -3689,7 +3693,9 @@ class CGUnionConversionStruct(CGThing): arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes) if len(arrayObjectMemberTypes) > 0: assert len(arrayObjectMemberTypes) == 1 - raise TypeError("Can't handle arrays or sequences in unions.") + typeName = arrayObjectMemberTypes[0].name + arrayObject = CGGeneric(get_match(typeName)) + names.append(typeName) else: arrayObject = None @@ -3727,8 +3733,12 @@ class CGUnionConversionStruct(CGThing): hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object if hasObjectTypes: - assert interfaceObject - templateBody = CGList([interfaceObject], "\n") + assert interfaceObject or arrayObject + templateBody = CGList([], "\n") + if interfaceObject: + templateBody.append(interfaceObject) + if arrayObject: + templateBody.append(arrayObject) conversions.append(CGIfWrapper("value.get().is_object()", templateBody)) otherMemberTypes = [ diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs index 687d68a5eb9..473f96f5704 100644 --- a/components/script/dom/testbinding.rs +++ b/components/script/dom/testbinding.rs @@ -9,6 +9,7 @@ use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::TestBindingBinding::{self, TestBindingMethods, TestEnum}; use dom::bindings::codegen::UnionTypes::{BlobOrString, EventOrString}; use dom::bindings::codegen::UnionTypes::{EventOrUSVString, HTMLElementOrLong}; +use dom::bindings::codegen::UnionTypes::{StringOrLongSequence, StringOrStringSequence}; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; use dom::bindings::js::Root; @@ -188,6 +189,8 @@ impl TestBindingMethods for TestBinding { fn ReceiveObject(&self, _: *mut JSContext) -> *mut JSObject { panic!() } fn ReceiveUnion(&self) -> HTMLElementOrLong { HTMLElementOrLong::eLong(0) } fn ReceiveUnion2(&self) -> EventOrString { EventOrString::eString(DOMString::new()) } + fn ReceiveUnion3(&self) -> StringOrLongSequence { StringOrLongSequence::eLongSequence(vec![]) } + fn ReceiveUnion4(&self) -> StringOrStringSequence { StringOrStringSequence::eStringSequence(vec![]) } fn ReceiveSequence(&self) -> Vec { vec![1] } fn ReceiveNullableBoolean(&self) -> Option { Some(false) } @@ -217,6 +220,9 @@ impl TestBindingMethods for TestBinding { fn ReceiveNullableUnion2(&self) -> Option { Some(EventOrString::eString(DOMString::new())) } + fn ReceiveNullableUnion3(&self) -> Option { + Some(StringOrLongSequence::eString(DOMString::new())) + } fn ReceiveNullableSequence(&self) -> Option> { Some(vec![1]) } fn PassBoolean(&self, _: bool) {} @@ -240,6 +246,7 @@ impl TestBindingMethods for TestBinding { fn PassUnion(&self, _: HTMLElementOrLong) {} fn PassUnion2(&self, _: EventOrString) {} fn PassUnion3(&self, _: BlobOrString) {} + fn PassUnion4(&self, _: StringOrStringSequence) {} fn PassAny(&self, _: *mut JSContext, _: HandleValue) {} fn PassObject(&self, _: *mut JSContext, _: *mut JSObject) {} fn PassCallbackFunction(&self, _: Rc) {} diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl index 7934182621e..ee3250427fe 100644 --- a/components/script/dom/webidls/TestBinding.webidl +++ b/components/script/dom/webidls/TestBinding.webidl @@ -149,6 +149,8 @@ interface TestBinding { object receiveObject(); (HTMLElement or long) receiveUnion(); (Event or DOMString) receiveUnion2(); + (DOMString or sequence) receiveUnion3(); + (DOMString or sequence) receiveUnion4(); sequence receiveSequence(); byte? receiveNullableByte(); @@ -172,6 +174,7 @@ interface TestBinding { object? receiveNullableObject(); (HTMLElement or long)? receiveNullableUnion(); (Event or DOMString)? receiveNullableUnion2(); + (DOMString or sequence)? receiveNullableUnion3(); sequence? receiveNullableSequence(); void passBoolean(boolean arg); @@ -195,6 +198,7 @@ interface TestBinding { void passUnion((HTMLElement or long) arg); void passUnion2((Event or DOMString) data); void passUnion3((Blob or DOMString) data); + void passUnion4((DOMString or sequence) seq); void passAny(any arg); void passObject(object arg); void passCallbackFunction(Function fun); From 3327f00bce1b5b302b0dfe8453a35b186029f0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 15 Jan 2016 13:40:02 +0100 Subject: [PATCH 2/5] script: Implement FromJSValConvertible for Root --- components/script/dom/bindings/conversions.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index 1509276db31..0ddee79814b 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -94,6 +94,17 @@ impl> FromJSValConvertible for Finite } } +impl FromJSValConvertible for Root { + type Config = (); + + unsafe fn from_jsval(_cx: *mut JSContext, + value: HandleValue, + _config: Self::Config) + -> Result, ()> { + root_from_handlevalue(value) + } +} + /// Convert the given `jsid` to a `DOMString`. Fails if the `jsid` is not a /// string, or if the string does not contain valid UTF-16. pub fn jsid_to_str(cx: *mut JSContext, id: HandleId) -> DOMString { From a76efa5eca50300a42c2a1fb8ac53b77e852d7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 15 Jan 2016 13:40:43 +0100 Subject: [PATCH 3/5] webidl: Add test for interface sequences Sequence interfaces return values worked before, but had no test. Sequence interface arguments didn't work until the previous commit. --- components/script/dom/testbinding.rs | 4 ++++ components/script/dom/webidls/TestBinding.webidl | 2 ++ 2 files changed, 6 insertions(+) diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs index 473f96f5704..6854ef493ee 100644 --- a/components/script/dom/testbinding.rs +++ b/components/script/dom/testbinding.rs @@ -192,6 +192,9 @@ impl TestBindingMethods for TestBinding { fn ReceiveUnion3(&self) -> StringOrLongSequence { StringOrLongSequence::eLongSequence(vec![]) } fn ReceiveUnion4(&self) -> StringOrStringSequence { StringOrStringSequence::eStringSequence(vec![]) } fn ReceiveSequence(&self) -> Vec { vec![1] } + fn ReceiveInterfaceSequence(&self) -> Vec> { + vec![Blob::new(self.global().r(), Vec::new(), "")] + } fn ReceiveNullableBoolean(&self) -> Option { Some(false) } fn ReceiveNullableByte(&self) -> Option { Some(0) } @@ -253,6 +256,7 @@ impl TestBindingMethods for TestBinding { fn PassCallbackInterface(&self, _: Rc) {} fn PassSequence(&self, _: Vec) {} fn PassStringSequence(&self, _: Vec) {} + fn PassInterfaceSequence(&self, _: Vec>) {} fn PassNullableBoolean(&self, _: Option) {} fn PassNullableByte(&self, _: Option) {} diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl index ee3250427fe..4863b1ce0b0 100644 --- a/components/script/dom/webidls/TestBinding.webidl +++ b/components/script/dom/webidls/TestBinding.webidl @@ -152,6 +152,7 @@ interface TestBinding { (DOMString or sequence) receiveUnion3(); (DOMString or sequence) receiveUnion4(); sequence receiveSequence(); + sequence receiveInterfaceSequence(); byte? receiveNullableByte(); boolean? receiveNullableBoolean(); @@ -205,6 +206,7 @@ interface TestBinding { void passCallbackInterface(EventListener listener); void passSequence(sequence seq); void passStringSequence(sequence seq); + void passInterfaceSequence(sequence seq); void passNullableBoolean(boolean? arg); void passNullableByte(byte? arg); From 8859c617a8ce0e0c16ea15cdef0397907f433fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 15 Jan 2016 13:41:41 +0100 Subject: [PATCH 4/5] webidl: Add test for interface and sequence in the same union --- components/script/dom/testbinding.rs | 2 ++ components/script/dom/webidls/TestBinding.webidl | 1 + 2 files changed, 3 insertions(+) diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs index 6854ef493ee..31fa20f0409 100644 --- a/components/script/dom/testbinding.rs +++ b/components/script/dom/testbinding.rs @@ -7,6 +7,7 @@ use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener; use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::TestBindingBinding::{self, TestBindingMethods, TestEnum}; +use dom::bindings::codegen::UnionTypes::{BlobOrBlobSequence}; use dom::bindings::codegen::UnionTypes::{BlobOrString, EventOrString}; use dom::bindings::codegen::UnionTypes::{EventOrUSVString, HTMLElementOrLong}; use dom::bindings::codegen::UnionTypes::{StringOrLongSequence, StringOrStringSequence}; @@ -191,6 +192,7 @@ impl TestBindingMethods for TestBinding { fn ReceiveUnion2(&self) -> EventOrString { EventOrString::eString(DOMString::new()) } fn ReceiveUnion3(&self) -> StringOrLongSequence { StringOrLongSequence::eLongSequence(vec![]) } fn ReceiveUnion4(&self) -> StringOrStringSequence { StringOrStringSequence::eStringSequence(vec![]) } + fn ReceiveUnion5(&self) -> BlobOrBlobSequence { BlobOrBlobSequence::eBlobSequence(vec![]) } fn ReceiveSequence(&self) -> Vec { vec![1] } fn ReceiveInterfaceSequence(&self) -> Vec> { vec![Blob::new(self.global().r(), Vec::new(), "")] diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl index 4863b1ce0b0..65ab4cf886c 100644 --- a/components/script/dom/webidls/TestBinding.webidl +++ b/components/script/dom/webidls/TestBinding.webidl @@ -151,6 +151,7 @@ interface TestBinding { (Event or DOMString) receiveUnion2(); (DOMString or sequence) receiveUnion3(); (DOMString or sequence) receiveUnion4(); + (Blob or sequence) receiveUnion5(); sequence receiveSequence(); sequence receiveInterfaceSequence(); From 950821989f57933a1b83623e1a2aea3dccd6c52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 15 Jan 2016 14:17:31 +0100 Subject: [PATCH 5/5] webidl: Refactor isSequence in getJSToNativeConversionInfo That way it does not depend on the return value for the same type. This hopefully makes the code more clear, and avoids errors if something changes in the future (for example, we could want to pass slices as sequence arguments). --- components/script/dom/bindings/codegen/CodegenRust.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 643c396febc..84d80124df9 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -700,10 +700,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, raise TypeError("Can't handle array arguments yet") if type.isSequence(): - # Use the same type that for return values - declType = getRetvalDeclarationForType(type, descriptorProvider) + innerInfo = getJSToNativeConversionInfo(type.unroll(), descriptorProvider) + declType = CGWrapper(innerInfo.declType, pre="Vec<", post=">") config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs) + if type.nullable(): + declType = CGWrapper(declType, pre="Option<", post=" >") + templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n" " Ok(value) => value,\n" " Err(()) => { %s },\n"