mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-13 21:11:22 +00:00
Add support for getters, setters and deleters
This commit is contained in:
@ -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 })
|
||||
}
|
||||
|
@ -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"),
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -70,6 +70,9 @@ pub enum OperationKind {
|
||||
Regular,
|
||||
Getter(String),
|
||||
Setter(String),
|
||||
SpecialGetter,
|
||||
SpecialSetter,
|
||||
SpecialDeleter,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
|
@ -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::*;
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user