Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assert that DOM structs have the correct first field #21105

Merged
merged 1 commit into from Jul 3, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Some generated files are not rendered by default. Learn more.

@@ -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 @@ def __init__(self, descriptor):
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 @@ def define(self):
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) {

This comment has been minimized.

@nox

nox Jul 3, 2018

Member

I would rather encode the relation with a trait specifying from which interface it is extended.

This comment has been minimized.

@Manishearth

Manishearth Jul 3, 2018

Author Member

You still need to assert the existence of this impl somehow, though. Thoughts?

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 @@ def reexportedName(name):
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(),
}
}

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.