mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Assert that DOM structs have the correct first field
DOM structs embed their parent type as their first field. This introduces a `.parent()` method to the DOM struct that returns its first field, and codegens a type assert that ensures that `.parent()` returns the parent struct. This generates: On `#[dom_struct]`: ```rust impl HasParent for Type { type Parent = ParentType; fn as_parent(&self) -> ParentType { &self.first_field } } ``` In the codegen files: ```rust impl Type { fn __assert_parent_type(&self) { let _: &ParentType = self.as_parent(); } } ````
This commit is contained in:
parent
e2fca1b228
commit
ad198993b1
6 changed files with 109 additions and 9 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -722,6 +722,10 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "dom_struct"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "domobject_derive"
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
[package]
|
||||
name = "dom_struct"
|
||||
version = "0.0.1"
|
||||
authors = ["The Servo Project Developers"]
|
||||
license = "MPL-2.0"
|
||||
name = "dom_struct"
|
||||
publish = false
|
||||
version = "0.0.1"
|
||||
|
||||
[dependencies]
|
||||
quote = "0.6.3"
|
||||
syn = { version = "0.14.2", features = ["full"] }
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
|
|
@ -7,7 +7,12 @@
|
|||
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::{TokenStream, quote};
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
extern crate syn;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::*;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn dom_struct(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
|
@ -23,5 +28,38 @@ pub fn dom_struct(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||
// Work around https://github.com/rust-lang/rust/issues/46489
|
||||
let attributes: TokenStream = attributes.to_string().parse().unwrap();
|
||||
|
||||
attributes.into_iter().chain(input.into_iter()).collect()
|
||||
|
||||
let output: TokenStream = attributes.into_iter().chain(input.into_iter()).collect();
|
||||
|
||||
let item: Item = syn::parse(output).unwrap();
|
||||
|
||||
if let Item::Struct(s) = item {
|
||||
let s2 = s.clone();
|
||||
if s.generics.params.len() > 0 {
|
||||
return quote!(#s2).into();
|
||||
}
|
||||
if let Fields::Named(ref f) = s.fields {
|
||||
let f = f.named.first().expect("Must have at least one field").into_value();
|
||||
let ident = f.ident.as_ref().expect("Must have named fields");
|
||||
let name = &s.ident;
|
||||
let ty = &f.ty;
|
||||
|
||||
quote! (
|
||||
#s2
|
||||
|
||||
impl ::dom::bindings::inheritance::HasParent for #name {
|
||||
type Parent = #ty;
|
||||
/// This is used in a type assertion to ensure that
|
||||
/// the source and webidls agree as to what the parent type is
|
||||
fn as_parent(&self) -> &#ty {
|
||||
&self.#ident
|
||||
}
|
||||
}
|
||||
).into()
|
||||
} else {
|
||||
panic!("#[dom_struct] only applies to structs with named fields");
|
||||
}
|
||||
} else {
|
||||
panic!("#[dom_struct] only applies to structs");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2116,6 +2116,10 @@ class CGDOMJSClass(CGThing):
|
|||
self.descriptor = descriptor
|
||||
|
||||
def define(self):
|
||||
parentName = self.descriptor.getParentName()
|
||||
if not parentName:
|
||||
parentName = "::dom::bindings::reflector::Reflector"
|
||||
|
||||
args = {
|
||||
"domClass": DOMClass(self.descriptor),
|
||||
"enumerateHook": "None",
|
||||
|
@ -2161,7 +2165,51 @@ static Class: DOMJSClass = DOMJSClass {
|
|||
reserved: [0 as *mut _; 3],
|
||||
},
|
||||
dom_class: %(domClass)s
|
||||
};""" % args
|
||||
};
|
||||
""" % args
|
||||
|
||||
|
||||
class CGAssertInheritance(CGThing):
|
||||
"""
|
||||
Generate a type assertion for inheritance
|
||||
"""
|
||||
def __init__(self, descriptor):
|
||||
CGThing.__init__(self)
|
||||
self.descriptor = descriptor
|
||||
|
||||
def define(self):
|
||||
parent = self.descriptor.interface.parent
|
||||
parentName = ""
|
||||
if parent:
|
||||
parentName = parent.identifier.name
|
||||
else:
|
||||
parentName = "::dom::bindings::reflector::Reflector"
|
||||
|
||||
selfName = self.descriptor.interface.identifier.name
|
||||
|
||||
if selfName == "PaintRenderingContext2D":
|
||||
# PaintRenderingContext2D embeds a CanvasRenderingContext2D
|
||||
# instead of a Reflector as an optimization,
|
||||
# but this is fine since CanvasRenderingContext2D
|
||||
# also has a reflector
|
||||
#
|
||||
# FIXME *RenderingContext2D should use Inline
|
||||
parentName = "::dom::canvasrenderingcontext2d::CanvasRenderingContext2D"
|
||||
args = {
|
||||
"parentName": parentName,
|
||||
"selfName": selfName,
|
||||
}
|
||||
|
||||
return """\
|
||||
impl %(selfName)s {
|
||||
fn __assert_parent_type(&self) {
|
||||
use dom::bindings::inheritance::HasParent;
|
||||
// If this type assertion fails, make sure the first field of your
|
||||
// DOM struct is of the correct type -- it must be the parent class.
|
||||
let _: &%(parentName)s = self.as_parent();
|
||||
}
|
||||
}
|
||||
""" % args
|
||||
|
||||
|
||||
def str_to_const_array(s):
|
||||
|
@ -6011,6 +6059,8 @@ class CGDescriptor(CGThing):
|
|||
pass
|
||||
else:
|
||||
cgThings.append(CGDOMJSClass(descriptor))
|
||||
if not descriptor.interface.isIteratorInterface():
|
||||
cgThings.append(CGAssertInheritance(descriptor))
|
||||
pass
|
||||
|
||||
if descriptor.isGlobal():
|
||||
|
|
|
@ -41,3 +41,8 @@ pub trait Castable: IDLInterface + DomObject + Sized {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasParent {
|
||||
type Parent;
|
||||
fn as_parent(&self) -> &Self::Parent;
|
||||
}
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
|
||||
use dom::bindings::codegen::Bindings::FileReaderSyncBinding::{FileReaderSyncBinding, FileReaderSyncMethods};
|
||||
use dom::bindings::error::{Error, Fallible};
|
||||
use dom::bindings::reflector::reflect_dom_object;
|
||||
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||
use dom::bindings::root::DomRoot;
|
||||
use dom::bindings::str::DOMString;
|
||||
use dom::blob::Blob;
|
||||
use dom::eventtarget::EventTarget;
|
||||
use dom::filereader::FileReaderSharedFunctionality;
|
||||
use dom::globalscope::GlobalScope;
|
||||
use dom_struct::dom_struct;
|
||||
|
@ -20,13 +19,13 @@ use std::ptr::NonNull;
|
|||
|
||||
#[dom_struct]
|
||||
pub struct FileReaderSync {
|
||||
eventtarget: EventTarget,
|
||||
reflector: Reflector,
|
||||
}
|
||||
|
||||
impl FileReaderSync {
|
||||
pub fn new_inherited() -> FileReaderSync {
|
||||
FileReaderSync {
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
reflector: Reflector::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue