mirror of
https://github.com/servo/servo.git
synced 2025-06-09 17:13:24 +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]]
|
[[package]]
|
||||||
name = "dom_struct"
|
name = "dom_struct"
|
||||||
version = "0.0.1"
|
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]]
|
[[package]]
|
||||||
name = "domobject_derive"
|
name = "domobject_derive"
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
[package]
|
[package]
|
||||||
name = "dom_struct"
|
|
||||||
version = "0.0.1"
|
|
||||||
authors = ["The Servo Project Developers"]
|
authors = ["The Servo Project Developers"]
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
|
name = "dom_struct"
|
||||||
publish = false
|
publish = false
|
||||||
|
version = "0.0.1"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "0.6.3"
|
||||||
|
syn = { version = "0.14.2", features = ["full"] }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
|
@ -7,7 +7,12 @@
|
||||||
|
|
||||||
extern crate proc_macro;
|
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]
|
#[proc_macro_attribute]
|
||||||
pub fn dom_struct(args: TokenStream, input: TokenStream) -> TokenStream {
|
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
|
// Work around https://github.com/rust-lang/rust/issues/46489
|
||||||
let attributes: TokenStream = attributes.to_string().parse().unwrap();
|
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
|
self.descriptor = descriptor
|
||||||
|
|
||||||
def define(self):
|
def define(self):
|
||||||
|
parentName = self.descriptor.getParentName()
|
||||||
|
if not parentName:
|
||||||
|
parentName = "::dom::bindings::reflector::Reflector"
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
"domClass": DOMClass(self.descriptor),
|
"domClass": DOMClass(self.descriptor),
|
||||||
"enumerateHook": "None",
|
"enumerateHook": "None",
|
||||||
|
@ -2161,7 +2165,51 @@ static Class: DOMJSClass = DOMJSClass {
|
||||||
reserved: [0 as *mut _; 3],
|
reserved: [0 as *mut _; 3],
|
||||||
},
|
},
|
||||||
dom_class: %(domClass)s
|
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):
|
def str_to_const_array(s):
|
||||||
|
@ -6011,6 +6059,8 @@ class CGDescriptor(CGThing):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
cgThings.append(CGDOMJSClass(descriptor))
|
cgThings.append(CGDOMJSClass(descriptor))
|
||||||
|
if not descriptor.interface.isIteratorInterface():
|
||||||
|
cgThings.append(CGAssertInheritance(descriptor))
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if descriptor.isGlobal():
|
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::BlobBinding::BlobMethods;
|
||||||
use dom::bindings::codegen::Bindings::FileReaderSyncBinding::{FileReaderSyncBinding, FileReaderSyncMethods};
|
use dom::bindings::codegen::Bindings::FileReaderSyncBinding::{FileReaderSyncBinding, FileReaderSyncMethods};
|
||||||
use dom::bindings::error::{Error, Fallible};
|
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::root::DomRoot;
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
use dom::blob::Blob;
|
use dom::blob::Blob;
|
||||||
use dom::eventtarget::EventTarget;
|
|
||||||
use dom::filereader::FileReaderSharedFunctionality;
|
use dom::filereader::FileReaderSharedFunctionality;
|
||||||
use dom::globalscope::GlobalScope;
|
use dom::globalscope::GlobalScope;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
@ -20,13 +19,13 @@ use std::ptr::NonNull;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct FileReaderSync {
|
pub struct FileReaderSync {
|
||||||
eventtarget: EventTarget,
|
reflector: Reflector,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileReaderSync {
|
impl FileReaderSync {
|
||||||
pub fn new_inherited() -> FileReaderSync {
|
pub fn new_inherited() -> FileReaderSync {
|
||||||
FileReaderSync {
|
FileReaderSync {
|
||||||
eventtarget: EventTarget::new_inherited(),
|
reflector: Reflector::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue