mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Allow undefined
variants in IDL unions (#37042)
Testing: Includes a test which ensures that unions with a `undefined` variant compile Fixes https://github.com/servo/servo/issues/28679 Part of https://github.com/servo/servo/issues/30287 --------- Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
parent
9c0c1cf30f
commit
fcc391d852
2 changed files with 37 additions and 10 deletions
|
@ -39,6 +39,7 @@ from WebIDL import (
|
||||||
from Configuration import (
|
from Configuration import (
|
||||||
Configuration,
|
Configuration,
|
||||||
Descriptor,
|
Descriptor,
|
||||||
|
DescriptorProvider,
|
||||||
MakeNativeName,
|
MakeNativeName,
|
||||||
MemberIsLegacyUnforgeable,
|
MemberIsLegacyUnforgeable,
|
||||||
getModuleFromObject,
|
getModuleFromObject,
|
||||||
|
@ -617,16 +618,16 @@ class JSToNativeConversionInfo():
|
||||||
|
|
||||||
|
|
||||||
def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||||
isDefinitelyObject=False,
|
isDefinitelyObject: bool = False,
|
||||||
isMember=False,
|
isMember: bool | str = False,
|
||||||
isArgument=False,
|
isArgument=False,
|
||||||
isAutoRooted=False,
|
isAutoRooted=False,
|
||||||
invalidEnumValueFatal=True,
|
invalidEnumValueFatal=True,
|
||||||
defaultValue=None,
|
defaultValue=None,
|
||||||
exceptionCode=None,
|
exceptionCode=None,
|
||||||
allowTreatNonObjectAsNull=False,
|
allowTreatNonObjectAsNull: bool = False,
|
||||||
isCallbackReturnValue=False,
|
isCallbackReturnValue=False,
|
||||||
sourceDescription="value"):
|
sourceDescription="value") -> JSToNativeConversionInfo:
|
||||||
"""
|
"""
|
||||||
Get a template for converting a JS value to a native object based on the
|
Get a template for converting a JS value to a native object based on the
|
||||||
given type and descriptor. If failureCode is given, then we're actually
|
given type and descriptor. If failureCode is given, then we're actually
|
||||||
|
@ -5071,7 +5072,7 @@ class CGConstant(CGThing):
|
||||||
return f"pub const {name}: {const_type} = {value};\n"
|
return f"pub const {name}: {const_type} = {value};\n"
|
||||||
|
|
||||||
|
|
||||||
def getUnionTypeTemplateVars(type, descriptorProvider):
|
def getUnionTypeTemplateVars(type, descriptorProvider: DescriptorProvider):
|
||||||
if type.isGeckoInterface():
|
if type.isGeckoInterface():
|
||||||
name = type.inner.identifier.name
|
name = type.inner.identifier.name
|
||||||
typeName = descriptorProvider.getDescriptor(name).returnType
|
typeName = descriptorProvider.getDescriptor(name).returnType
|
||||||
|
@ -5108,6 +5109,12 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
|
||||||
elif type.isCallback():
|
elif type.isCallback():
|
||||||
name = type.name
|
name = type.name
|
||||||
typeName = f"{name}<D>"
|
typeName = f"{name}<D>"
|
||||||
|
elif type.isUndefined():
|
||||||
|
return {
|
||||||
|
"name": type.name,
|
||||||
|
"typeName": "()",
|
||||||
|
"jsConversion": CGGeneric("if value.is_undefined() { Ok(Some(())) } else { Ok(None) }")
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
raise TypeError(f"Can't handle {type} in unions yet")
|
raise TypeError(f"Can't handle {type} in unions yet")
|
||||||
|
|
||||||
|
@ -5173,7 +5180,7 @@ impl{self.generic} Clone for {self.type}{self.genericSuffix} {{
|
||||||
def manualImpl(self, t, templateVars):
|
def manualImpl(self, t, templateVars):
|
||||||
if t == "Clone":
|
if t == "Clone":
|
||||||
return self.manualImplClone(templateVars)
|
return self.manualImplClone(templateVars)
|
||||||
raise f"Don't know how to impl {t} for union"
|
raise ValueError(f"Don't know how to impl {t} for union")
|
||||||
|
|
||||||
def define(self):
|
def define(self):
|
||||||
def getTypeWrapper(t):
|
def getTypeWrapper(t):
|
||||||
|
@ -5330,31 +5337,44 @@ class CGUnionConversionStruct(CGThing):
|
||||||
stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
|
stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
|
||||||
numericTypes = [t for t in memberTypes if t.isNumeric()]
|
numericTypes = [t for t in memberTypes if t.isNumeric()]
|
||||||
booleanTypes = [t for t in memberTypes if t.isBoolean()]
|
booleanTypes = [t for t in memberTypes if t.isBoolean()]
|
||||||
if stringTypes or numericTypes or booleanTypes:
|
numUndefinedVariants = [t.isUndefined() for t in memberTypes].count(True)
|
||||||
|
if stringTypes or numericTypes or booleanTypes or numUndefinedVariants != 0:
|
||||||
assert len(stringTypes) <= 1
|
assert len(stringTypes) <= 1
|
||||||
assert len(numericTypes) <= 1
|
assert len(numericTypes) <= 1
|
||||||
assert len(booleanTypes) <= 1
|
assert len(booleanTypes) <= 1
|
||||||
|
assert numUndefinedVariants <= 1
|
||||||
|
|
||||||
def getStringOrPrimitiveConversion(memberType):
|
def getStringOrPrimitiveConversion(memberType):
|
||||||
typename = get_name(memberType)
|
typename = get_name(memberType)
|
||||||
return CGGeneric(get_match(typename))
|
return CGGeneric(get_match(typename))
|
||||||
|
|
||||||
other = []
|
other = []
|
||||||
stringConversion = list(map(getStringOrPrimitiveConversion, stringTypes))
|
stringConversion = list(map(getStringOrPrimitiveConversion, stringTypes))
|
||||||
numericConversion = list(map(getStringOrPrimitiveConversion, numericTypes))
|
numericConversion = list(map(getStringOrPrimitiveConversion, numericTypes))
|
||||||
booleanConversion = list(map(getStringOrPrimitiveConversion, booleanTypes))
|
booleanConversion = list(map(getStringOrPrimitiveConversion, booleanTypes))
|
||||||
|
undefinedConversion = CGGeneric("return Ok(ConversionResult::Success(Self::Undefined(())));")
|
||||||
|
|
||||||
if stringConversion:
|
if stringConversion:
|
||||||
if booleanConversion:
|
if booleanConversion:
|
||||||
other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0]))
|
other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0]))
|
||||||
if numericConversion:
|
if numericConversion:
|
||||||
other.append(CGIfWrapper("value.get().is_number()", numericConversion[0]))
|
other.append(CGIfWrapper("value.get().is_number()", numericConversion[0]))
|
||||||
|
if numUndefinedVariants != 0:
|
||||||
|
other.append(CGIfWrapper("value.get().is_undefined()", undefinedConversion))
|
||||||
other.append(stringConversion[0])
|
other.append(stringConversion[0])
|
||||||
elif numericConversion:
|
elif numericConversion:
|
||||||
if booleanConversion:
|
if booleanConversion:
|
||||||
other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0]))
|
other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0]))
|
||||||
|
if numUndefinedVariants != 0:
|
||||||
|
other.append(CGIfWrapper("value.get().is_undefined()", undefinedConversion))
|
||||||
other.append(numericConversion[0])
|
other.append(numericConversion[0])
|
||||||
else:
|
elif booleanConversion:
|
||||||
assert booleanConversion
|
if numUndefinedVariants != 0:
|
||||||
|
other.append(CGIfWrapper("value.get().is_undefined()", undefinedConversion))
|
||||||
other.append(booleanConversion[0])
|
other.append(booleanConversion[0])
|
||||||
|
else:
|
||||||
|
assert numUndefinedVariants != 0
|
||||||
|
other.append(undefinedConversion)
|
||||||
conversions.append(CGList(other, "\n\n"))
|
conversions.append(CGList(other, "\n\n"))
|
||||||
conversions.append(CGGeneric(
|
conversions.append(CGGeneric(
|
||||||
f'Ok(ConversionResult::Failure("argument could not be converted to any of: {", ".join(names)}".into()))'
|
f'Ok(ConversionResult::Failure("argument could not be converted to any of: {", ".join(names)}".into()))'
|
||||||
|
@ -5376,6 +5396,10 @@ class CGUnionConversionStruct(CGThing):
|
||||||
post="\n}")
|
post="\n}")
|
||||||
|
|
||||||
def try_method(self, t):
|
def try_method(self, t):
|
||||||
|
if t.isUndefined():
|
||||||
|
# undefined does not require a conversion method, so we don't generate one
|
||||||
|
return CGGeneric("")
|
||||||
|
|
||||||
templateVars = getUnionTypeTemplateVars(t, self.descriptorProvider)
|
templateVars = getUnionTypeTemplateVars(t, self.descriptorProvider)
|
||||||
actualType = templateVars["typeName"]
|
actualType = templateVars["typeName"]
|
||||||
if type_needs_tracing(t):
|
if type_needs_tracing(t):
|
||||||
|
@ -7151,7 +7175,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS
|
||||||
def manualImpl(self, t):
|
def manualImpl(self, t):
|
||||||
if t == "Clone":
|
if t == "Clone":
|
||||||
return self.manualImplClone()
|
return self.manualImplClone()
|
||||||
raise f"Don't know how to impl {t} for dicts."
|
raise ValueError(f"Don't know how to impl {t} for dicts.")
|
||||||
|
|
||||||
def struct(self):
|
def struct(self):
|
||||||
d = self.dictionary
|
d = self.dictionary
|
||||||
|
|
|
@ -627,3 +627,6 @@ dictionary NotUsedAnyWhereElse {};
|
||||||
dictionary RecordFieldWithUnionInside {
|
dictionary RecordFieldWithUnionInside {
|
||||||
record<USVString, (USVString or NotUsedAnyWhereElse)> recordWithUnionField;
|
record<USVString, (USVString or NotUsedAnyWhereElse)> recordWithUnionField;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://github.com/servo/servo/issues/28679
|
||||||
|
typedef (USVString or undefined) UnionWithUndefinedVariant;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue