mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-17 23:11:23 +00:00
Support named "special" operations in WebIDL
This commit adds support for two different features of the "special" operations in WebIDL. First, it implements the desugaring [described by WebIDL][1] where this: interface Dictionary { getter double getProperty(DOMString propertyName); setter void setProperty(DOMString propertyName, double propertyValue); }; becomes ... interface Dictionary { double getProperty(DOMString propertyName); void setProperty(DOMString propertyName, double propertyValue); getter double (DOMString propertyName); setter void (DOMString propertyName, double propertyValue); }; where specifically a named `getter` generates both a getter and a named function. Second it implements the distinction between two different types of getters in WebIDL, described as: > Getters and setters come in two varieties: ones that take a DOMString as a > property name, known as named property getters and named property setters, and > ones that take an unsigned long as a property index, known as indexed property > getters and indexed property setters. The name `get` is given to DOMString arguments, and the name `get_idx` is given to index property getters. [1]: https://heycam.github.io/webidl/#idl-special-operations
This commit is contained in:
@ -65,6 +65,7 @@ fn try_main() -> Result<(), failure::Error> {
|
|||||||
.context("writing bindings to output file")?;
|
.context("writing bindings to output file")?;
|
||||||
|
|
||||||
// run rustfmt on the generated file - really handy for debugging
|
// run rustfmt on the generated file - really handy for debugging
|
||||||
|
println!("cargo:rerun-if-env-changed=WEBIDL_RUSTFMT_BINDINGS");
|
||||||
if env::var("WEBIDL_RUSTFMT_BINDINGS").is_ok() {
|
if env::var("WEBIDL_RUSTFMT_BINDINGS").is_ok() {
|
||||||
let status = Command::new("rustfmt")
|
let status = Command::new("rustfmt")
|
||||||
.arg(&out_file_path)
|
.arg(&out_file_path)
|
||||||
|
@ -11,7 +11,7 @@ use std::collections::{BTreeMap, BTreeSet};
|
|||||||
|
|
||||||
use weedle::argument::Argument;
|
use weedle::argument::Argument;
|
||||||
use weedle::attribute::ExtendedAttribute;
|
use weedle::attribute::ExtendedAttribute;
|
||||||
use weedle::interface::StringifierOrStatic;
|
use weedle::interface::{StringifierOrStatic, Special};
|
||||||
use weedle::mixin::MixinMember;
|
use weedle::mixin::MixinMember;
|
||||||
use weedle::namespace::NamespaceMember;
|
use weedle::namespace::NamespaceMember;
|
||||||
use weedle;
|
use weedle;
|
||||||
@ -61,7 +61,7 @@ pub(crate) struct NamespaceData<'src> {
|
|||||||
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||||
pub(crate) enum OperationId<'src> {
|
pub(crate) enum OperationId<'src> {
|
||||||
Constructor,
|
Constructor,
|
||||||
Operation(Option<&'src str>),
|
Operation(Option<&'src str>),
|
||||||
@ -171,7 +171,7 @@ fn first_pass_operation<'src>(
|
|||||||
record: &mut FirstPassRecord<'src>,
|
record: &mut FirstPassRecord<'src>,
|
||||||
first_pass_operation_type: FirstPassOperationType,
|
first_pass_operation_type: FirstPassOperationType,
|
||||||
self_name: &'src str,
|
self_name: &'src str,
|
||||||
id: OperationId<'src>,
|
ids: &[OperationId<'src>],
|
||||||
arguments: &[Argument<'src>],
|
arguments: &[Argument<'src>],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut names = Vec::with_capacity(arguments.len());
|
let mut names = Vec::with_capacity(arguments.len());
|
||||||
@ -181,7 +181,7 @@ fn first_pass_operation<'src>(
|
|||||||
Argument::Variadic(variadic) => names.push(variadic.identifier.0),
|
Argument::Variadic(variadic) => names.push(variadic.identifier.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match first_pass_operation_type{
|
let operations = match first_pass_operation_type{
|
||||||
FirstPassOperationType::Interface => {
|
FirstPassOperationType::Interface => {
|
||||||
&mut record
|
&mut record
|
||||||
.interfaces
|
.interfaces
|
||||||
@ -203,14 +203,17 @@ fn first_pass_operation<'src>(
|
|||||||
.expect(&format!("not found {} namesace", self_name))
|
.expect(&format!("not found {} namesace", self_name))
|
||||||
.operations
|
.operations
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
for id in ids {
|
||||||
|
operations
|
||||||
|
.entry(*id)
|
||||||
|
.and_modify(|operation_data| operation_data.overloaded = true)
|
||||||
|
.or_default()
|
||||||
|
.argument_names_same
|
||||||
|
.entry(names.clone())
|
||||||
|
.and_modify(|same_argument_names| *same_argument_names = true)
|
||||||
|
.or_insert(false);
|
||||||
}
|
}
|
||||||
.entry(id)
|
|
||||||
.and_modify(|operation_data| operation_data.overloaded = true)
|
|
||||||
.or_default()
|
|
||||||
.argument_names_same
|
|
||||||
.entry(names)
|
|
||||||
.and_modify(|same_argument_names| *same_argument_names = true)
|
|
||||||
.or_insert(false);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -278,7 +281,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> {
|
|||||||
record,
|
record,
|
||||||
FirstPassOperationType::Interface,
|
FirstPassOperationType::Interface,
|
||||||
self_name,
|
self_name,
|
||||||
OperationId::Constructor,
|
&[OperationId::Constructor],
|
||||||
&list.args.body.list,
|
&list.args.body.list,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -287,7 +290,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> {
|
|||||||
record,
|
record,
|
||||||
FirstPassOperationType::Interface,
|
FirstPassOperationType::Interface,
|
||||||
self_name,
|
self_name,
|
||||||
OperationId::Constructor,
|
&[OperationId::Constructor],
|
||||||
&[],
|
&[],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -298,7 +301,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> {
|
|||||||
record,
|
record,
|
||||||
FirstPassOperationType::Interface,
|
FirstPassOperationType::Interface,
|
||||||
self_name,
|
self_name,
|
||||||
OperationId::Constructor,
|
&[OperationId::Constructor],
|
||||||
&list.args.body.list,
|
&list.args.body.list,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -332,7 +335,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.specials.is_empty() && self.specials.len() != 1 {
|
if self.specials.len() > 1 {
|
||||||
warn!("Unsupported webidl operation {:?}", self);
|
warn!("Unsupported webidl operation {:?}", self);
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
@ -340,20 +343,20 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM
|
|||||||
warn!("Unsupported webidl operation {:?}", self);
|
warn!("Unsupported webidl operation {:?}", self);
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
let mut ids = vec![OperationId::Operation(self.identifier.map(|s| s.0))];
|
||||||
|
for special in self.specials.iter() {
|
||||||
|
ids.push(match special {
|
||||||
|
Special::Getter(_) => OperationId::IndexingGetter,
|
||||||
|
Special::Setter(_) => OperationId::IndexingSetter,
|
||||||
|
Special::Deleter(_) => OperationId::IndexingDeleter,
|
||||||
|
Special::LegacyCaller(_) => continue,
|
||||||
|
});
|
||||||
|
}
|
||||||
first_pass_operation(
|
first_pass_operation(
|
||||||
record,
|
record,
|
||||||
FirstPassOperationType::Interface,
|
FirstPassOperationType::Interface,
|
||||||
self_name,
|
self_name,
|
||||||
match self.identifier.map(|s| s.0) {
|
&ids,
|
||||||
None => match self.specials.get(0) {
|
|
||||||
None => OperationId::Operation(None),
|
|
||||||
Some(weedle::interface::Special::Getter(_)) => OperationId::IndexingGetter,
|
|
||||||
Some(weedle::interface::Special::Setter(_)) => OperationId::IndexingSetter,
|
|
||||||
Some(weedle::interface::Special::Deleter(_)) => OperationId::IndexingDeleter,
|
|
||||||
Some(weedle::interface::Special::LegacyCaller(_)) => return Ok(()),
|
|
||||||
},
|
|
||||||
Some(ref name) => OperationId::Operation(Some(name.clone())),
|
|
||||||
},
|
|
||||||
&self.args.body.list,
|
&self.args.body.list,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -434,7 +437,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'s
|
|||||||
record,
|
record,
|
||||||
FirstPassOperationType::Mixin,
|
FirstPassOperationType::Mixin,
|
||||||
self_name,
|
self_name,
|
||||||
OperationId::Operation(self.identifier.map(|s| s.0.clone())),
|
&[OperationId::Operation(self.identifier.map(|s| s.0.clone()))],
|
||||||
&self.args.body.list,
|
&self.args.body.list,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -524,7 +527,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceM
|
|||||||
record,
|
record,
|
||||||
FirstPassOperationType::Namespace,
|
FirstPassOperationType::Namespace,
|
||||||
self_name,
|
self_name,
|
||||||
OperationId::Operation(self.identifier.map(|s| s.0.clone())),
|
&[OperationId::Operation(self.identifier.map(|s| s.0.clone()))],
|
||||||
&self.args.body.list,
|
&self.args.body.list,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ use proc_macro2::{Ident, Span};
|
|||||||
use weedle::argument::Argument;
|
use weedle::argument::Argument;
|
||||||
use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList};
|
use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList};
|
||||||
|
|
||||||
use first_pass::{FirstPass, FirstPassRecord};
|
use first_pass::{FirstPass, FirstPassRecord, OperationId};
|
||||||
use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc};
|
use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc};
|
||||||
use idl_type::{IdlType, ToIdlType};
|
use idl_type::{IdlType, ToIdlType};
|
||||||
|
|
||||||
@ -646,6 +646,7 @@ fn member_operation<'src>(
|
|||||||
identifier: &Option<weedle::common::Identifier<'src>>,
|
identifier: &Option<weedle::common::Identifier<'src>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use weedle::interface::StringifierOrStatic::*;
|
use weedle::interface::StringifierOrStatic::*;
|
||||||
|
use weedle::interface::Special;
|
||||||
|
|
||||||
if util::is_chrome_only(attrs) {
|
if util::is_chrome_only(attrs) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -660,33 +661,52 @@ fn member_operation<'src>(
|
|||||||
None => false,
|
None => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
for import_function in first_pass.create_basic_method(
|
let mut operation_ids = vec![
|
||||||
args,
|
OperationId::Operation(identifier.map(|s| s.0)),
|
||||||
match identifier.map(|s| s.0) {
|
];
|
||||||
None if specials.is_empty() => ::first_pass::OperationId::Operation(None),
|
if specials.len() > 1 {
|
||||||
None if specials.len() == 1 => match specials[0] {
|
warn!(
|
||||||
weedle::interface::Special::Getter(_) => ::first_pass::OperationId::IndexingGetter,
|
"Unsupported specials ({:?}) on type {:?}",
|
||||||
weedle::interface::Special::Setter(_) => ::first_pass::OperationId::IndexingSetter,
|
specials,
|
||||||
weedle::interface::Special::Deleter(_) => ::first_pass::OperationId::IndexingDeleter,
|
(self_name, identifier),
|
||||||
weedle::interface::Special::LegacyCaller(_) => return Ok(()),
|
);
|
||||||
},
|
return Ok(())
|
||||||
Some(ref name) if specials.is_empty() => ::first_pass::OperationId::Operation(Some(name.clone())),
|
} else if specials.len() == 1 {
|
||||||
_ => {
|
let id = match specials[0] {
|
||||||
warn!("Unsupported specials on type {:?}", (self_name, identifier));
|
Special::Getter(weedle::term::Getter) => OperationId::IndexingGetter,
|
||||||
return Ok(())
|
Special::Setter(weedle::term::Setter) => OperationId::IndexingSetter,
|
||||||
}
|
Special::Deleter(weedle::term::Deleter) => OperationId::IndexingDeleter,
|
||||||
},
|
Special::LegacyCaller(weedle::term::LegacyCaller) => return Ok(()),
|
||||||
return_type,
|
};
|
||||||
self_name,
|
operation_ids.push(id);
|
||||||
is_static,
|
}
|
||||||
specials.len() == 1 || first_pass
|
|
||||||
.interfaces
|
for id in operation_ids {
|
||||||
.get(self_name)
|
let methods = first_pass
|
||||||
.map(|interface_data| interface_data.global)
|
.create_basic_method(
|
||||||
.unwrap_or(false),
|
args,
|
||||||
util::throws(attrs),
|
id,
|
||||||
) {
|
return_type,
|
||||||
program.imports.push(wrap_import_function(import_function));
|
self_name,
|
||||||
|
is_static,
|
||||||
|
match id {
|
||||||
|
OperationId::IndexingGetter |
|
||||||
|
OperationId::IndexingSetter |
|
||||||
|
OperationId::IndexingDeleter => true,
|
||||||
|
_ => {
|
||||||
|
first_pass
|
||||||
|
.interfaces
|
||||||
|
.get(self_name)
|
||||||
|
.map(|interface_data| interface_data.global)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
util::throws(attrs),
|
||||||
|
);
|
||||||
|
|
||||||
|
for method in methods {
|
||||||
|
program.imports.push(wrap_import_function(method));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -370,7 +370,7 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
/// Create a wasm-bindgen method, if possible.
|
/// Create a wasm-bindgen method, if possible.
|
||||||
pub fn create_basic_method(
|
pub fn create_basic_method(
|
||||||
&self,
|
&self,
|
||||||
arguments: &[weedle::argument::Argument],
|
arguments: &[Argument],
|
||||||
operation_id: first_pass::OperationId,
|
operation_id: first_pass::OperationId,
|
||||||
return_type: &weedle::types::ReturnType,
|
return_type: &weedle::types::ReturnType,
|
||||||
self_name: &str,
|
self_name: &str,
|
||||||
@ -392,11 +392,11 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
warn!("Operations without a name are unsupported");
|
warn!("Operations without a name are unsupported");
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
Some(name) => name.to_string(),
|
Some(name) => name,
|
||||||
},
|
},
|
||||||
first_pass::OperationId::IndexingGetter => "get".to_string(),
|
first_pass::OperationId::IndexingGetter => "get",
|
||||||
first_pass::OperationId::IndexingSetter => "set".to_string(),
|
first_pass::OperationId::IndexingSetter => "set",
|
||||||
first_pass::OperationId::IndexingDeleter => "delete".to_string(),
|
first_pass::OperationId::IndexingDeleter => "delete",
|
||||||
};
|
};
|
||||||
|
|
||||||
let kind = backend::ast::ImportFunctionKind::Method {
|
let kind = backend::ast::ImportFunctionKind::Method {
|
||||||
@ -455,7 +455,7 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
/// whether there overloads with same argument names for given argument types
|
/// whether there overloads with same argument names for given argument types
|
||||||
pub fn get_operation_overloading(
|
pub fn get_operation_overloading(
|
||||||
&self,
|
&self,
|
||||||
arguments: &[weedle::argument::Argument],
|
arguments: &[Argument],
|
||||||
operation_id: &first_pass::OperationId,
|
operation_id: &first_pass::OperationId,
|
||||||
self_name: &str,
|
self_name: &str,
|
||||||
namespace: bool,
|
namespace: bool,
|
||||||
|
Reference in New Issue
Block a user