Add support for getters, setters and deleters

This commit is contained in:
Anton Danilkin
2018-08-05 23:32:31 +03:00
parent b4601295d0
commit fd2b2140a9
7 changed files with 213 additions and 44 deletions

View File

@ -106,8 +106,11 @@ pub struct Operation {
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
pub enum OperationKind { pub enum OperationKind {
Regular, Regular,
Setter(Option<Ident>),
Getter(Option<Ident>), Getter(Option<Ident>),
Setter(Option<Ident>),
SpecialGetter,
SpecialSetter,
SpecialDeleter,
} }
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
@ -395,6 +398,9 @@ impl ImportFunction {
s.unwrap_or_else(|| self.infer_setter_property()), s.unwrap_or_else(|| self.infer_setter_property()),
) )
} }
OperationKind::SpecialGetter => shared::OperationKind::SpecialGetter,
OperationKind::SpecialSetter => shared::OperationKind::SpecialSetter,
OperationKind::SpecialDeleter => shared::OperationKind::SpecialDeleter,
}; };
shared::MethodKind::Operation(shared::Operation { is_static, kind }) shared::MethodKind::Operation(shared::Operation { is_static, kind })
} }

View File

@ -1869,18 +1869,6 @@ impl<'a, 'b> SubContext<'a, 'b> {
let location = if *is_static { &class } else { "this" }; let location = if *is_static { &class } else { "this" };
match kind { match kind {
shared::OperationKind::Getter(g) => format!(
"function() {{
return {}.{};
}}",
location, g
),
shared::OperationKind::Setter(s) => format!(
"function(y) {{
{}.{} = y;
}}",
location, s
),
shared::OperationKind::Regular => { shared::OperationKind::Regular => {
let nargs = descriptor.unwrap_function().arguments.len(); let nargs = descriptor.unwrap_function().arguments.len();
let mut s = format!("function("); let mut s = format!("function(");
@ -1902,11 +1890,44 @@ impl<'a, 'b> SubContext<'a, 'b> {
s.push_str(");\n}"); s.push_str(");\n}");
s s
} }
shared::OperationKind::Getter(g) => format!(
"function() {{
return {}.{};
}}",
location, g
),
shared::OperationKind::Setter(s) => format!(
"function(y) {{
{}.{} = y;
}}",
location, s
),
shared::OperationKind::SpecialGetter => format!(
"function(y) {{
return {}[y];
}}",
location
),
shared::OperationKind::SpecialSetter => format!(
"function(y, z) {{
{}[y] = z;
}}",
location
),
shared::OperationKind::SpecialDeleter => format!(
"function(y) {{
delete {}[y];
}}",
location
),
} }
} else { } else {
let location = if *is_static { "" } else { ".prototype" }; let location = if *is_static { "" } else { ".prototype" };
match kind { match kind {
shared::OperationKind::Regular => {
format!("{}{}.{}", class, location, import.function.name)
}
shared::OperationKind::Getter(g) => { shared::OperationKind::Getter(g) => {
self.cx.expose_get_inherited_descriptor(); self.cx.expose_get_inherited_descriptor();
format!( format!(
@ -1921,9 +1942,9 @@ impl<'a, 'b> SubContext<'a, 'b> {
class, location, s, class, location, s,
) )
} }
shared::OperationKind::Regular => { shared::OperationKind::SpecialGetter => panic!("getter should be structural"),
format!("{}{}.{}", class, location, import.function.name) shared::OperationKind::SpecialSetter => panic!("setter should be structural"),
} shared::OperationKind::SpecialDeleter => panic!("deleter should be structural"),
} }
}; };

View File

@ -132,6 +132,30 @@ impl BindgenAttrs {
.next() .next()
} }
/// Whether the special getter attributes is present
fn special_getter(&self) -> bool {
self.attrs.iter().any(|a| match *a {
BindgenAttr::SpecialGetter => true,
_ => false,
})
}
/// Whether the special setter attributes is present
fn special_setter(&self) -> bool {
self.attrs.iter().any(|a| match *a {
BindgenAttr::SpecialSetter => true,
_ => false,
})
}
/// Whether the special deleter attributes is present
fn special_deleter(&self) -> bool {
self.attrs.iter().any(|a| match *a {
BindgenAttr::SpecialDeleter => true,
_ => false,
})
}
/// Whether the structural attributes is present /// Whether the structural attributes is present
fn structural(&self) -> bool { fn structural(&self) -> bool {
self.attrs.iter().any(|a| match *a { self.attrs.iter().any(|a| match *a {
@ -198,6 +222,9 @@ pub enum BindgenAttr {
Version(String), Version(String),
Getter(Option<Ident>), Getter(Option<Ident>),
Setter(Option<Ident>), Setter(Option<Ident>),
SpecialGetter,
SpecialSetter,
SpecialDeleter,
Structural, Structural,
Readonly, Readonly,
JsName(Ident), JsName(Ident),
@ -239,6 +266,12 @@ impl syn::synom::Synom for BindgenAttr {
(val) (val)
)=> { BindgenAttr::Setter } )=> { BindgenAttr::Setter }
| |
call!(term, "special_getter") => { |_| BindgenAttr::SpecialGetter }
|
call!(term, "special_setter") => { |_| BindgenAttr::SpecialSetter }
|
call!(term, "special_deleter") => { |_| BindgenAttr::SpecialDeleter }
|
call!(term, "structural") => { |_| BindgenAttr::Structural } call!(term, "structural") => { |_| BindgenAttr::Structural }
| |
call!(term, "readonly") => { |_| BindgenAttr::Readonly } call!(term, "readonly") => { |_| BindgenAttr::Readonly }
@ -395,6 +428,15 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
if let Some(s) = opts.setter() { if let Some(s) = opts.setter() {
operation_kind = ast::OperationKind::Setter(s); operation_kind = ast::OperationKind::Setter(s);
} }
if opts.special_getter() {
operation_kind = ast::OperationKind::SpecialGetter;
}
if opts.special_setter() {
operation_kind = ast::OperationKind::SpecialSetter;
}
if opts.special_deleter() {
operation_kind = ast::OperationKind::SpecialDeleter;
}
let kind = if opts.method() { let kind = if opts.method() {
let class = wasm let class = wasm

View File

@ -70,6 +70,9 @@ pub enum OperationKind {
Regular, Regular,
Getter(String), Getter(String),
Setter(String), Setter(String),
SpecialGetter,
SpecialSetter,
SpecialDeleter,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]

View File

@ -38,7 +38,10 @@ pub(crate) struct InterfaceData {
#[derive(PartialEq, Eq, PartialOrd, Ord)] #[derive(PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum OperationId { pub(crate) enum OperationId {
Constructor, Constructor,
Operation(Option<String>) Operation(Option<String>),
SpecialGetter,
SpecialSetter,
SpecialDeleter,
} }
#[derive(Default)] #[derive(Default)]
@ -302,8 +305,9 @@ impl<'b> FirstPass<&'b str> for webidl::ast::Operation {
match self { match self {
webidl::ast::Operation::Regular(op) => op.first_pass(record, self_name), webidl::ast::Operation::Regular(op) => op.first_pass(record, self_name),
webidl::ast::Operation::Static(op) => op.first_pass(record, self_name), webidl::ast::Operation::Static(op) => op.first_pass(record, self_name),
webidl::ast::Operation::Special(op) => op.first_pass(record, self_name),
// TODO // TODO
webidl::ast::Operation::Special(_) | webidl::ast::Operation::Stringifier(_) => { webidl::ast::Operation::Stringifier(_) => {
warn!("Unsupported WebIDL operation: {:?}", self); warn!("Unsupported WebIDL operation: {:?}", self);
Ok(()) Ok(())
} }
@ -333,6 +337,28 @@ impl<'b> FirstPass<&'b str> for webidl::ast::StaticOperation {
} }
} }
impl<'b> FirstPass<&'b str> for webidl::ast::SpecialOperation {
fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> {
first_pass_operation(
record,
self_name,
match self.name {
None => match self.special_keywords.iter().next() {
Some(webidl::ast::Special::Getter) => OperationId::SpecialGetter,
Some(webidl::ast::Special::Setter) => OperationId::SpecialSetter,
Some(webidl::ast::Special::Deleter) => OperationId::SpecialDeleter,
Some(webidl::ast::Special::LegacyCaller) => return Ok(()),
None => {
panic!("unsupported special operation: {:?} of {}", self, self_name);
}
},
Some(ref name) => OperationId::Operation(Some(name.clone())),
},
&self.arguments,
)
}
}
impl FirstPass<()> for webidl::ast::Mixin { impl FirstPass<()> for webidl::ast::Mixin {
fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> {
use webidl::ast::Mixin::*; use webidl::ast::Mixin::*;

View File

@ -307,7 +307,7 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte
let (overloaded, same_argument_names) = first_pass.get_operation_overloading( let (overloaded, same_argument_names) = first_pass.get_operation_overloading(
arguments, arguments,
::first_pass::OperationId::Constructor, &::first_pass::OperationId::Constructor,
&interface.name, &interface.name,
); );
@ -475,8 +475,9 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Operation {
match self { match self {
webidl::ast::Operation::Regular(op) => op.webidl_parse(program, first_pass, self_name), webidl::ast::Operation::Regular(op) => op.webidl_parse(program, first_pass, self_name),
webidl::ast::Operation::Static(op) => op.webidl_parse(program, first_pass, self_name), webidl::ast::Operation::Static(op) => op.webidl_parse(program, first_pass, self_name),
webidl::ast::Operation::Special(op) => op.webidl_parse(program, first_pass, self_name),
// TODO // TODO
webidl::ast::Operation::Special(_) | webidl::ast::Operation::Stringifier(_) => { webidl::ast::Operation::Stringifier(_) => {
warn!("Unsupported WebIDL operation: {:?}", self); warn!("Unsupported WebIDL operation: {:?}", self);
Ok(()) Ok(())
} }
@ -627,8 +628,6 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation {
return Ok(()); return Ok(());
} }
let throws = util::throws(&self.extended_attributes);
first_pass first_pass
.create_basic_method( .create_basic_method(
&self &self
@ -636,11 +635,16 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation {
.iter() .iter()
.map(|argument| argument.apply_typedefs(first_pass)) .map(|argument| argument.apply_typedefs(first_pass))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
self.name.as_ref(), ::first_pass::OperationId::Operation(self.name.clone()),
&self.return_type.apply_typedefs(first_pass), &self.return_type.apply_typedefs(first_pass),
self_name, self_name,
false, false,
throws, first_pass
.interfaces
.get(self_name)
.map(|interface_data| interface_data.global)
.unwrap_or(false),
util::throws(&self.extended_attributes),
) )
.map(wrap_import_function) .map(wrap_import_function)
.map(|import| program.imports.push(import)); .map(|import| program.imports.push(import));
@ -660,7 +664,41 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation {
return Ok(()); return Ok(());
} }
let throws = util::throws(&self.extended_attributes); first_pass
.create_basic_method(
&self
.arguments
.iter()
.map(|argument| argument.apply_typedefs(first_pass))
.collect::<Vec<_>>(),
::first_pass::OperationId::Operation(self.name.clone()),
&self.return_type.apply_typedefs(first_pass),
self_name,
true,
first_pass
.interfaces
.get(self_name)
.map(|interface_data| interface_data.global)
.unwrap_or(false),
util::throws(&self.extended_attributes),
)
.map(wrap_import_function)
.map(|import| program.imports.push(import));
Ok(())
}
}
impl<'a> WebidlParse<&'a str> for webidl::ast::SpecialOperation {
fn webidl_parse(
&self,
program: &mut backend::ast::Program,
first_pass: &FirstPassRecord<'_>,
self_name: &'a str,
) -> Result<()> {
if util::is_chrome_only(&self.extended_attributes) {
return Ok(());
}
first_pass first_pass
.create_basic_method( .create_basic_method(
@ -669,11 +707,23 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation {
.iter() .iter()
.map(|argument| argument.apply_typedefs(first_pass)) .map(|argument| argument.apply_typedefs(first_pass))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
self.name.as_ref(), match self.name {
None => match self.special_keywords.iter().next() {
Some(webidl::ast::Special::Getter) => ::first_pass::OperationId::SpecialGetter,
Some(webidl::ast::Special::Setter) => ::first_pass::OperationId::SpecialSetter,
Some(webidl::ast::Special::Deleter) => ::first_pass::OperationId::SpecialDeleter,
Some(webidl::ast::Special::LegacyCaller) => return Ok(()),
None => {
panic!("unsupported special operation: {:?} of {}", self, self_name);
}
},
Some(ref name) => ::first_pass::OperationId::Operation(Some(name.clone())),
},
&self.return_type.apply_typedefs(first_pass), &self.return_type.apply_typedefs(first_pass),
self_name, self_name,
false,
true, true,
throws, util::throws(&self.extended_attributes),
) )
.map(wrap_import_function) .map(wrap_import_function)
.map(|import| program.imports.push(import)); .map(|import| program.imports.push(import));

View File

@ -604,24 +604,31 @@ impl<'a> FirstPassRecord<'a> {
pub fn create_basic_method( pub fn create_basic_method(
&self, &self,
arguments: &[webidl::ast::Argument], arguments: &[webidl::ast::Argument],
name: Option<&String>, operation_id: ::first_pass::OperationId,
return_type: &webidl::ast::ReturnType, return_type: &webidl::ast::ReturnType,
self_name: &str, self_name: &str,
is_static: bool, is_static: bool,
structural: bool,
catch: bool, catch: bool,
) -> Option<backend::ast::ImportFunction> { ) -> Option<backend::ast::ImportFunction> {
let (overloaded, same_argument_names) = self.get_operation_overloading( let (overloaded, same_argument_names) = self.get_operation_overloading(
arguments, arguments,
::first_pass::OperationId::Operation(name.cloned()), &operation_id,
self_name, self_name,
); );
let name = match name { let name = match &operation_id {
::first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
::first_pass::OperationId::Operation(name) => match name {
None => { None => {
warn!("Operations without a name are unsupported"); warn!("Operations without a name are unsupported");
return None; return None;
} }
Some(ref name) => name, Some(ref name) => name.clone(),
},
::first_pass::OperationId::SpecialGetter => "get".to_string(),
::first_pass::OperationId::SpecialSetter => "set".to_string(),
::first_pass::OperationId::SpecialDeleter => "delete".to_string(),
}; };
let kind = backend::ast::ImportFunctionKind::Method { let kind = backend::ast::ImportFunctionKind::Method {
@ -629,7 +636,13 @@ impl<'a> FirstPassRecord<'a> {
ty: ident_ty(rust_ident(camel_case_ident(&self_name).as_str())), ty: ident_ty(rust_ident(camel_case_ident(&self_name).as_str())),
kind: backend::ast::MethodKind::Operation(backend::ast::Operation { kind: backend::ast::MethodKind::Operation(backend::ast::Operation {
is_static, is_static,
kind: backend::ast::OperationKind::Regular, kind: match &operation_id {
::first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
::first_pass::OperationId::Operation(_) => backend::ast::OperationKind::Regular,
::first_pass::OperationId::SpecialGetter => backend::ast::OperationKind::SpecialGetter,
::first_pass::OperationId::SpecialSetter => backend::ast::OperationKind::SpecialSetter,
::first_pass::OperationId::SpecialDeleter => backend::ast::OperationKind::SpecialDeleter,
},
}), }),
}; };
@ -645,7 +658,19 @@ impl<'a> FirstPassRecord<'a> {
} }
} }
}; };
let doc_comment = Some(format!("The `{}()` method\n\n{}", name, mdn_doc(self_name, Some(name)))); let doc_comment = match &operation_id {
::first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
::first_pass::OperationId::Operation(_) => Some(
format!(
"The `{}()` method\n\n{}",
name,
mdn_doc(self_name, Some(&name))
)
),
::first_pass::OperationId::SpecialGetter => Some("The getter\n\n".to_string()),
::first_pass::OperationId::SpecialSetter => Some("The setter\n\n".to_string()),
::first_pass::OperationId::SpecialDeleter => Some("The deleter\n\n".to_string()),
};
self.create_function( self.create_function(
&name, &name,
@ -656,11 +681,7 @@ impl<'a> FirstPassRecord<'a> {
.map(|arg| (&*arg.name, &*arg.type_, arg.variadic)), .map(|arg| (&*arg.name, &*arg.type_, arg.variadic)),
ret, ret,
kind, kind,
self structural,
.interfaces
.get(self_name)
.map(|interface_data| interface_data.global)
.unwrap_or(false),
catch, catch,
doc_comment, doc_comment,
) )
@ -671,7 +692,7 @@ impl<'a> FirstPassRecord<'a> {
pub fn get_operation_overloading( pub fn get_operation_overloading(
&self, &self,
arguments: &[webidl::ast::Argument], arguments: &[webidl::ast::Argument],
id: ::first_pass::OperationId, id: &::first_pass::OperationId,
self_name: &str, self_name: &str,
) -> (bool, bool) { ) -> (bool, bool) {
self self
@ -680,7 +701,7 @@ impl<'a> FirstPassRecord<'a> {
.map(|interface_data| { .map(|interface_data| {
interface_data interface_data
.operations .operations
.get(&id) .get(id)
.map(|operation_data| .map(|operation_data|
( (
operation_data.overloaded, operation_data.overloaded,