Support custom derives for generated types (#34356)

* script: Derive more Default implementations for dictionaries.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* script: Support arbitrary derives on generated enums.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* script: Support arbitrary derives for generated dictionaries.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* script: Support arbitrary derives for generated unions.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* script: Derive more impls for generated dicts and unions.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* script: Implement FromStr for generated enums.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* Fix clippy.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* crown: Allow returning unrooted values from Default::default.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Josh Matthews 2024-11-24 13:15:50 -05:00 committed by GitHub
parent 3faed9b921
commit c60e4afbee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 124 additions and 129 deletions

View file

@ -527,3 +527,49 @@ DOMInterfaces = {
},
}
Dictionaries = {
'GPUCanvasConfiguration': {
'derives': ['Clone']
},
'GPUExtent3DDict': {
'derives': ["MallocSizeOf"],
},
'GPUObjectDescriptorBase': {
'derives': ['MallocSizeOf']
},
'GPUTextureDescriptor': {
'derives': ["MallocSizeOf"],
},
'HeadersInit': {
'derives': ["Clone"],
},
}
Enums = {
'GPUFeatureName': {
'derives': ['Hash', 'Eq']
}
}
Unions = {
'ByteStringSequenceSequenceOrByteStringByteStringRecord': {
'derives': ['Clone']
},
'HTMLCanvasElementOrOffscreenCanvas': {
'derives': ['Clone', 'MallocSizeOf']
},
'RangeEnforcedUnsignedLongSequenceOrGPUExtent3DDict': {
'derives': ['MallocSizeOf']
},
'StringOrUnsignedLong': {
'derives': ['Clone'],
},
}

View file

@ -2649,7 +2649,7 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config):
if name not in unionStructs:
provider = descriptor or config.getDescriptorProvider()
unionStructs[name] = CGList([
CGUnionStruct(t, provider),
CGUnionStruct(t, provider, config),
CGUnionConversionStruct(t, provider)
])
@ -4865,14 +4865,18 @@ def getEnumValueName(value):
class CGEnum(CGThing):
def __init__(self, enum):
def __init__(self, enum, config):
CGThing.__init__(self)
ident = enum.identifier.name
enums = ",\n ".join(map(getEnumValueName, list(enum.values())))
derives = ["Copy", "Clone", "Debug", "JSTraceable", "MallocSizeOf", "PartialEq"]
enum_config = config.getEnumConfig(ident)
extra_derives = enum_config.get('derives', [])
derives = ', '.join(derives + extra_derives)
decl = f"""
#[repr(usize)]
#[derive(Copy, Clone, Debug, JSTraceable, MallocSizeOf, PartialEq)]
#[derive({derives})]
pub enum {ident} {{
{enums}
}}
@ -4907,6 +4911,18 @@ impl Default for super::{ident} {{
}}
}}
impl std::str::FromStr for super::{ident} {{
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {{
pairs
.iter()
.find(|&&(key, _)| s == key)
.map(|&(_, ev)| ev)
.ok_or(())
}}
}}
impl ToJSValConvertible for super::{ident} {{
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {{
pairs[*self as usize].0.to_jsval(cx, rval);
@ -5037,12 +5053,13 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
class CGUnionStruct(CGThing):
def __init__(self, type, descriptorProvider):
def __init__(self, type, descriptorProvider, config):
assert not type.nullable()
assert not type.hasNullableType
CGThing.__init__(self)
self.type = type
self.derives = config.getUnionConfig(str(type)).get('derives', [])
self.descriptorProvider = descriptorProvider
def membersNeedTracing(self):
@ -5071,8 +5088,9 @@ class CGUnionStruct(CGThing):
]
joinedEnumValues = "\n".join(enumValues)
joinedEnumConversions = "\n".join(enumConversions)
derives = ["JSTraceable"] + self.derives
return f"""
#[derive(JSTraceable)]
#[derive({", ".join(derives)})]
pub enum {self.type} {{
{joinedEnumValues}
}}
@ -6879,9 +6897,10 @@ class CGNonNamespacedEnum(CGThing):
class CGDictionary(CGThing):
def __init__(self, dictionary, descriptorProvider):
def __init__(self, dictionary, descriptorProvider, config):
self.dictionary = dictionary
if all(CGDictionary(d, descriptorProvider).generatable for
self.derives = config.getDictConfig(dictionary.identifier.name).get('derives', [])
if all(CGDictionary(d, descriptorProvider, config).generatable for
d in CGDictionary.getDictionaryDependencies(dictionary)):
self.generatable = True
else:
@ -6915,12 +6934,40 @@ class CGDictionary(CGThing):
memberDecls = [f" pub {self.makeMemberName(m[0].identifier.name)}: {self.getMemberType(m)},"
for m in self.memberInfo]
derive = ["JSTraceable"]
derive = ["JSTraceable"] + self.derives
default = ""
mustRoot = ""
if self.membersNeedTracing():
mustRoot = "#[crown::unrooted_must_root_lint::must_root]\n"
if not self.hasRequiredFields(self.dictionary):
derive += ["Default"]
# We can't unconditionally derive Default here, because union types can have unique
# default values provided for each usage. Instead, whenever possible we re-use the empty()
# method that is generated.
if not self.hasRequiredFields(self.dictionary):
if d.parent:
inheritanceDefault = " parent: Default::default(),\n"
else:
inheritanceDefault = ""
if not self.membersNeedTracing():
impl = " Self::empty()\n"
else:
memberDefaults = [f" {self.makeMemberName(m[0].identifier.name)}: Default::default(),"
for m in self.memberInfo]
joinedDefaults = '\n'.join(memberDefaults)
impl = (
" Self {\n"
f" {inheritanceDefault}{joinedDefaults}"
" }\n"
)
default = (
f"impl Default for {self.makeClassName(d)} {{\n"
" fn default() -> Self {\n"
f"{impl}"
" }\n"
"}\n"
)
joinedMemberDecls = '\n'.join(memberDecls)
return (
f"#[derive({', '.join(derive)})]\n"
@ -6928,7 +6975,8 @@ class CGDictionary(CGThing):
f"pub struct {self.makeClassName(d)} {{\n"
f"{inheritance}"
f"{joinedMemberDecls}\n"
"}"
"}\n"
f"{default}"
)
def impl(self):
@ -7227,7 +7275,7 @@ class CGBindingRoot(CGThing):
return
# Do codegen for all the enums.
cgthings = [CGEnum(e) for e in enums]
cgthings = [CGEnum(e, config) for e in enums]
# Do codegen for all the typedefs
for t in typedefs:
@ -7244,7 +7292,7 @@ class CGBindingRoot(CGThing):
cgthings.append(CGGeneric(typeDefinition))
# Do codegen for all the dictionaries.
cgthings.extend([CGDictionary(d, config.getDescriptorProvider())
cgthings.extend([CGDictionary(d, config.getDescriptorProvider(), config)
for d in dictionaries])
# Do codegen for all the callbacks.
@ -8145,7 +8193,10 @@ class GlobalGenRoots():
descriptors = (set(toBindingModuleFile(d.name) for d in descriptors if d.maybeGetSuperModule() is None)
| set(leafModule(d) for d in config.callbacks)
| set(leafModule(d) for d in config.getDictionaries()))
curr = CGList([CGGeneric(f"pub mod {name};\n") for name in sorted(descriptors)])
curr = CGList([CGGeneric(
"#[allow(clippy::derivable_impls)]\n"
f"pub mod {name};\n"
) for name in sorted(descriptors)])
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
return curr

View file

@ -18,6 +18,9 @@ class Configuration:
glbl = {}
exec(compile(open(filename).read(), filename, 'exec'), glbl)
config = glbl['DOMInterfaces']
self.enumConfig = glbl['Enums']
self.dictConfig = glbl['Dictionaries']
self.unionConfig = glbl['Unions']
# Build descriptors for all the interfaces we have in the parse data.
# This allows callers to specify a subset of interfaces by filtering
@ -110,6 +113,9 @@ class Configuration:
def getEnums(self, webIDLFile):
return [e for e in self.enums if e.filename == webIDLFile]
def getEnumConfig(self, name):
return self.enumConfig.get(name, {})
def getTypedefs(self, webIDLFile):
return [e for e in self.typedefs if e.filename == webIDLFile]
@ -121,9 +127,15 @@ class Configuration:
return [x for x in items if x.filename == webIDLFile]
def getUnionConfig(self, name):
return self.unionConfig.get(name, {})
def getDictionaries(self, webIDLFile=""):
return self._filterForFile(self.dictionaries, webIDLFile=webIDLFile)
def getDictConfig(self, name):
return self.dictConfig.get(name, {})
def getCallbacks(self, webIDLFile=""):
return self._filterForFile(self.callbacks, webIDLFile=webIDLFile)

View file

@ -624,15 +624,6 @@ impl BluetoothUUID {
}
}
impl Clone for StringOrUnsignedLong {
fn clone(&self) -> StringOrUnsignedLong {
match self {
StringOrUnsignedLong::String(s) => StringOrUnsignedLong::String(s.clone()),
&StringOrUnsignedLong::UnsignedLong(ul) => StringOrUnsignedLong::UnsignedLong(ul),
}
}
}
fn canonical_uuid(alias: u32) -> UUID {
UUID::from(format!("{:08x}", &alias) + BASE_UUID)
}

View file

@ -753,9 +753,3 @@ fn inner_invoke(
// Step 3.
found
}
impl Default for EventBinding::EventInit {
fn default() -> Self {
Self::empty()
}
}

View file

@ -102,9 +102,3 @@ impl ExtendableEventMethods<crate::DomTypeHolder> for ExtendableEvent {
self.event.IsTrusted()
}
}
impl Default for ExtendableEventInit {
fn default() -> Self {
Self::empty()
}
}

View file

@ -39,60 +39,6 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlcanvaselement::{HTMLCanvasElement, LayoutCanvasRenderingContextHelpers};
use crate::dom::node::{document_from_node, Node, NodeDamage};
// TODO: make all this derivables available via new Bindings.conf option
impl Clone for GPUCanvasConfiguration {
fn clone(&self) -> Self {
Self {
alphaMode: self.alphaMode,
device: self.device.clone(),
format: self.format,
usage: self.usage,
viewFormats: self.viewFormats.clone(),
}
}
}
impl Clone for HTMLCanvasElementOrOffscreenCanvas {
fn clone(&self) -> Self {
match self {
Self::HTMLCanvasElement(arg0) => Self::HTMLCanvasElement(arg0.clone()),
Self::OffscreenCanvas(arg0) => Self::OffscreenCanvas(arg0.clone()),
}
}
}
impl malloc_size_of::MallocSizeOf for GPUTextureDescriptor {
fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
let Self {
parent,
dimension,
format,
mipLevelCount,
sampleCount,
size,
usage,
viewFormats,
} = self;
parent.size_of(ops) +
dimension.size_of(ops) +
format.size_of(ops) +
mipLevelCount.size_of(ops) +
sampleCount.size_of(ops) +
size.size_of(ops) +
usage.size_of(ops) +
viewFormats.size_of(ops)
}
}
impl malloc_size_of::MallocSizeOf for HTMLCanvasElementOrOffscreenCanvas {
fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
match self {
HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(canvas) => canvas.size_of(ops),
HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(canvas) => canvas.size_of(ops),
}
}
}
impl HTMLCanvasElementOrOffscreenCanvas {
fn size(&self) -> Size2D<u64> {
match self {

View file

@ -4,8 +4,6 @@
// check-tidy: no specs after this line
use std::str::FromStr;
use dom_struct::dom_struct;
use indexmap::IndexSet;
use js::rust::HandleObject;
@ -13,7 +11,6 @@ use webgpu::wgt::Features;
use super::bindings::like::Setlike;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::GPUFeatureNameValues::pairs;
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
GPUFeatureName, GPUSupportedFeaturesMethods,
};
@ -24,16 +21,6 @@ use crate::dom::bindings::str::DOMString;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
// manual hash derived
// TODO: allow derivables in bindings.conf
impl std::hash::Hash for GPUFeatureName {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
}
}
impl Eq for GPUFeatureName {}
#[dom_struct]
pub struct GPUSupportedFeatures {
reflector: Reflector,
@ -152,19 +139,6 @@ pub fn gpu_to_wgt_feature(feature: GPUFeatureName) -> Option<Features> {
}
}
// this should be autogenerate by bindings
impl FromStr for GPUFeatureName {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
pairs
.iter()
.find(|&&(key, _)| s == key)
.map(|&(_, ev)| ev)
.ok_or(())
}
}
// this error is wrong because if we inline Self::Key and Self::Value all errors are gone
#[allow(crown::unrooted_must_root)]
impl Setlike for GPUSupportedFeatures {

View file

@ -857,16 +857,3 @@ impl From<NetTraitsRequestRedirect> for RequestRedirect {
}
}
}
impl Clone for HeadersInit {
fn clone(&self) -> HeadersInit {
match self {
HeadersInit::ByteStringSequenceSequence(b) => {
HeadersInit::ByteStringSequenceSequence(b.clone())
},
HeadersInit::ByteStringByteStringRecord(m) => {
HeadersInit::ByteStringByteStringRecord(m.clone())
},
}
}
}

View file

@ -290,7 +290,7 @@ impl<'tcx> LateLintPass<'tcx> for UnrootedPass {
) {
let in_new_function = match kind {
visit::FnKind::ItemFn(n, _, _) | visit::FnKind::Method(n, _) => {
n.as_str() == "new" || n.as_str().starts_with("new_")
n.as_str() == "new" || n.as_str().starts_with("new_") || n.as_str() == "default"
},
visit::FnKind::Closure => return,
};