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))]
pub enum OperationKind {
Regular,
Setter(Option<Ident>),
Getter(Option<Ident>),
Setter(Option<Ident>),
SpecialGetter,
SpecialSetter,
SpecialDeleter,
}
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
@ -395,6 +398,9 @@ impl ImportFunction {
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 })
}

View File

@ -1869,18 +1869,6 @@ impl<'a, 'b> SubContext<'a, 'b> {
let location = if *is_static { &class } else { "this" };
match kind {
shared::OperationKind::Getter(g) => format!(
"function() {{
return {}.{};
}}",
location, g
),
shared::OperationKind::Setter(s) => format!(
"function(y) {{
{}.{} = y;
}}",
location, s
),
shared::OperationKind::Regular => {
let nargs = descriptor.unwrap_function().arguments.len();
let mut s = format!("function(");
@ -1902,11 +1890,44 @@ impl<'a, 'b> SubContext<'a, 'b> {
s.push_str(");\n}");
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 {
let location = if *is_static { "" } else { ".prototype" };
match kind {
shared::OperationKind::Regular => {
format!("{}{}.{}", class, location, import.function.name)
}
shared::OperationKind::Getter(g) => {
self.cx.expose_get_inherited_descriptor();
format!(
@ -1921,9 +1942,9 @@ impl<'a, 'b> SubContext<'a, 'b> {
class, location, s,
)
}
shared::OperationKind::Regular => {
format!("{}{}.{}", class, location, import.function.name)
}
shared::OperationKind::SpecialGetter => panic!("getter should be structural"),
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()
}
/// 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
fn structural(&self) -> bool {
self.attrs.iter().any(|a| match *a {
@ -198,6 +222,9 @@ pub enum BindgenAttr {
Version(String),
Getter(Option<Ident>),
Setter(Option<Ident>),
SpecialGetter,
SpecialSetter,
SpecialDeleter,
Structural,
Readonly,
JsName(Ident),
@ -239,6 +266,12 @@ impl syn::synom::Synom for BindgenAttr {
(val)
)=> { 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, "readonly") => { |_| BindgenAttr::Readonly }
@ -395,6 +428,15 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
if let Some(s) = opts.setter() {
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 class = wasm

View File

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

View File

@ -38,7 +38,10 @@ pub(crate) struct InterfaceData {
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum OperationId {
Constructor,
Operation(Option<String>)
Operation(Option<String>),
SpecialGetter,
SpecialSetter,
SpecialDeleter,
}
#[derive(Default)]
@ -302,8 +305,9 @@ impl<'b> FirstPass<&'b str> for webidl::ast::Operation {
match self {
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::Special(op) => op.first_pass(record, self_name),
// TODO
webidl::ast::Operation::Special(_) | webidl::ast::Operation::Stringifier(_) => {
webidl::ast::Operation::Stringifier(_) => {
warn!("Unsupported WebIDL operation: {:?}", self);
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 {
fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> {
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(
arguments,
::first_pass::OperationId::Constructor,
&::first_pass::OperationId::Constructor,
&interface.name,
);
@ -475,8 +475,9 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Operation {
match self {
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::Special(op) => op.webidl_parse(program, first_pass, self_name),
// TODO
webidl::ast::Operation::Special(_) | webidl::ast::Operation::Stringifier(_) => {
webidl::ast::Operation::Stringifier(_) => {
warn!("Unsupported WebIDL operation: {:?}", self);
Ok(())
}
@ -627,8 +628,6 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation {
return Ok(());
}
let throws = util::throws(&self.extended_attributes);
first_pass
.create_basic_method(
&self
@ -636,11 +635,16 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation {
.iter()
.map(|argument| argument.apply_typedefs(first_pass))
.collect::<Vec<_>>(),
self.name.as_ref(),
::first_pass::OperationId::Operation(self.name.clone()),
&self.return_type.apply_typedefs(first_pass),
self_name,
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(|import| program.imports.push(import));
@ -660,7 +664,41 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation {
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
.create_basic_method(
@ -669,11 +707,23 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation {
.iter()
.map(|argument| argument.apply_typedefs(first_pass))
.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_name,
false,
true,
throws,
util::throws(&self.extended_attributes),
)
.map(wrap_import_function)
.map(|import| program.imports.push(import));

View File

@ -604,24 +604,31 @@ impl<'a> FirstPassRecord<'a> {
pub fn create_basic_method(
&self,
arguments: &[webidl::ast::Argument],
name: Option<&String>,
operation_id: ::first_pass::OperationId,
return_type: &webidl::ast::ReturnType,
self_name: &str,
is_static: bool,
structural: bool,
catch: bool,
) -> Option<backend::ast::ImportFunction> {
let (overloaded, same_argument_names) = self.get_operation_overloading(
arguments,
::first_pass::OperationId::Operation(name.cloned()),
&operation_id,
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 => {
warn!("Operations without a name are unsupported");
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 {
@ -629,7 +636,13 @@ impl<'a> FirstPassRecord<'a> {
ty: ident_ty(rust_ident(camel_case_ident(&self_name).as_str())),
kind: backend::ast::MethodKind::Operation(backend::ast::Operation {
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(
&name,
@ -656,11 +681,7 @@ impl<'a> FirstPassRecord<'a> {
.map(|arg| (&*arg.name, &*arg.type_, arg.variadic)),
ret,
kind,
self
.interfaces
.get(self_name)
.map(|interface_data| interface_data.global)
.unwrap_or(false),
structural,
catch,
doc_comment,
)
@ -671,7 +692,7 @@ impl<'a> FirstPassRecord<'a> {
pub fn get_operation_overloading(
&self,
arguments: &[webidl::ast::Argument],
id: ::first_pass::OperationId,
id: &::first_pass::OperationId,
self_name: &str,
) -> (bool, bool) {
self
@ -680,7 +701,7 @@ impl<'a> FirstPassRecord<'a> {
.map(|interface_data| {
interface_data
.operations
.get(&id)
.get(id)
.map(|operation_data|
(
operation_data.overloaded,