mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-21 16:51:33 +00:00
Merge remote-tracking branch 'upstream/master' into variadic_js_functions
This commit is contained in:
@ -142,3 +142,7 @@ global.MixinFoo = class MixinFoo {
|
|||||||
this._bar += other;
|
this._bar += other;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
global.Overloads = class {
|
||||||
|
foo() {}
|
||||||
|
};
|
||||||
|
@ -82,12 +82,12 @@ fn optional_and_union_arguments() {
|
|||||||
let f = OptionalAndUnionArguments::new().unwrap();
|
let f = OptionalAndUnionArguments::new().unwrap();
|
||||||
assert_eq!(f.m("abc"), "string, abc, boolean, true, number, 123, number, 456");
|
assert_eq!(f.m("abc"), "string, abc, boolean, true, number, 123, number, 456");
|
||||||
assert_eq!(f.m_with_b("abc", false), "string, abc, boolean, false, number, 123, number, 456");
|
assert_eq!(f.m_with_b("abc", false), "string, abc, boolean, false, number, 123, number, 456");
|
||||||
assert_eq!(f.m_with_bool_and_i16("abc", false, 5), "string, abc, boolean, false, number, 5, number, 456");
|
assert_eq!(f.m_with_b_and_i16("abc", false, 5), "string, abc, boolean, false, number, 5, number, 456");
|
||||||
assert_eq!(f.m_with_bool_and_str("abc", false, "5"), "string, abc, boolean, false, string, 5, number, 456");
|
assert_eq!(f.m_with_b_and_str("abc", false, "5"), "string, abc, boolean, false, string, 5, number, 456");
|
||||||
assert_eq!(f.m_with_bool_and_i16_and_opt_i64("abc", false, 5, Some(10)), "string, abc, boolean, false, number, 5, bigint, 10");
|
assert_eq!(f.m_with_b_and_i16_and_opt_i64("abc", false, 5, Some(10)), "string, abc, boolean, false, number, 5, bigint, 10");
|
||||||
assert_eq!(f.m_with_bool_and_i16_and_opt_bool("abc", false, 5, Some(true)), "string, abc, boolean, false, number, 5, boolean, true");
|
assert_eq!(f.m_with_b_and_i16_and_opt_bool("abc", false, 5, Some(true)), "string, abc, boolean, false, number, 5, boolean, true");
|
||||||
assert_eq!(f.m_with_bool_and_str_and_opt_i64("abc", false, "5", Some(10)), "string, abc, boolean, false, string, 5, bigint, 10");
|
assert_eq!(f.m_with_b_and_str_and_opt_i64("abc", false, "5", Some(10)), "string, abc, boolean, false, string, 5, bigint, 10");
|
||||||
assert_eq!(f.m_with_bool_and_str_and_opt_bool("abc", false, "5", Some(true)), "string, abc, boolean, false, string, 5, boolean, true");
|
assert_eq!(f.m_with_b_and_str_and_opt_bool("abc", false, "5", Some(true)), "string, abc, boolean, false, string, 5, boolean, true");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
@ -114,3 +114,13 @@ fn mixin() {
|
|||||||
f.add_to_bar(MixinFoo::default_bar());
|
f.add_to_bar(MixinFoo::default_bar());
|
||||||
assert_eq!(f.bar(), 8);
|
assert_eq!(f.bar(), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn overload_naming() {
|
||||||
|
let o = Overloads::new().unwrap();
|
||||||
|
o.foo();
|
||||||
|
o.foo_with_arg("x");
|
||||||
|
o.foo_with_arg_and_i32("x", 3);
|
||||||
|
o.foo_with_arg_and_f32("x", 2.0);
|
||||||
|
o.foo_with_arg_and_i16("x", 5);
|
||||||
|
}
|
||||||
|
7
crates/webidl-tests/simple.webidl
vendored
7
crates/webidl-tests/simple.webidl
vendored
@ -88,3 +88,10 @@ partial interface mixin MixinBar {
|
|||||||
};
|
};
|
||||||
|
|
||||||
MixinFoo includes MixinBar;
|
MixinFoo includes MixinBar;
|
||||||
|
|
||||||
|
[Constructor()]
|
||||||
|
interface Overloads {
|
||||||
|
void foo();
|
||||||
|
void foo(DOMString arg, optional long a);
|
||||||
|
void foo(DOMString arg, (float or short) b);
|
||||||
|
};
|
||||||
|
@ -7,25 +7,25 @@
|
|||||||
//! Only `interface`s, `dictionary`s, `enum`s and `mixin`s can
|
//! Only `interface`s, `dictionary`s, `enum`s and `mixin`s can
|
||||||
//! be partial.
|
//! be partial.
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
use weedle::{DictionaryDefinition, PartialDictionaryDefinition};
|
use weedle::{DictionaryDefinition, PartialDictionaryDefinition};
|
||||||
use weedle::argument::Argument;
|
use weedle::argument::Argument;
|
||||||
use weedle::attribute::ExtendedAttribute;
|
use weedle::attribute::*;
|
||||||
use weedle::interface::{StringifierOrStatic, Special};
|
use weedle::interface::*;
|
||||||
use weedle::mixin::MixinMember;
|
use weedle::mixin::*;
|
||||||
use weedle::namespace::NamespaceMember;
|
|
||||||
use weedle;
|
use weedle;
|
||||||
|
|
||||||
use super::Result;
|
use super::Result;
|
||||||
use util;
|
|
||||||
use util::camel_case_ident;
|
use util::camel_case_ident;
|
||||||
|
use util;
|
||||||
|
|
||||||
/// Collection of constructs that may use partial.
|
/// Collection of constructs that may use partial.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct FirstPassRecord<'src> {
|
pub(crate) struct FirstPassRecord<'src> {
|
||||||
pub(crate) interfaces: BTreeMap<&'src str, InterfaceData<'src>>,
|
pub(crate) interfaces: BTreeMap<&'src str, InterfaceData<'src>>,
|
||||||
pub(crate) enums: BTreeSet<&'src str>,
|
pub(crate) enums: BTreeMap<&'src str, &'src weedle::EnumDefinition<'src>>,
|
||||||
/// The mixins, mapping their name to the webidl ast node for the mixin.
|
/// The mixins, mapping their name to the webidl ast node for the mixin.
|
||||||
pub(crate) mixins: BTreeMap<&'src str, MixinData<'src>>,
|
pub(crate) mixins: BTreeMap<&'src str, MixinData<'src>>,
|
||||||
pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>,
|
pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>,
|
||||||
@ -40,8 +40,11 @@ pub(crate) struct InterfaceData<'src> {
|
|||||||
/// Whether only partial interfaces were encountered
|
/// Whether only partial interfaces were encountered
|
||||||
pub(crate) partial: bool,
|
pub(crate) partial: bool,
|
||||||
pub(crate) global: bool,
|
pub(crate) global: bool,
|
||||||
|
pub(crate) attributes: Vec<&'src AttributeInterfaceMember<'src>>,
|
||||||
|
pub(crate) consts: Vec<&'src ConstMember<'src>>,
|
||||||
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
||||||
pub(crate) superclass: Option<&'src str>,
|
pub(crate) superclass: Option<&'src str>,
|
||||||
|
pub(crate) definition_attributes: Option<&'src [ExtendedAttribute<'src>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We need to collect mixin data during the first pass, to be used later.
|
/// We need to collect mixin data during the first pass, to be used later.
|
||||||
@ -49,7 +52,8 @@ pub(crate) struct InterfaceData<'src> {
|
|||||||
pub(crate) struct MixinData<'src> {
|
pub(crate) struct MixinData<'src> {
|
||||||
/// Whether only partial mixins were encountered
|
/// Whether only partial mixins were encountered
|
||||||
pub(crate) partial: bool,
|
pub(crate) partial: bool,
|
||||||
pub(crate) members: Vec<&'src MixinMember<'src>>,
|
pub(crate) attributes: Vec<&'src AttributeMixinMember<'src>>,
|
||||||
|
pub(crate) consts: Vec<&'src ConstMember<'src>>,
|
||||||
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,8 +61,6 @@ pub(crate) struct MixinData<'src> {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct NamespaceData<'src> {
|
pub(crate) struct NamespaceData<'src> {
|
||||||
/// Whether only partial namespaces were encountered
|
/// Whether only partial namespaces were encountered
|
||||||
pub(crate) partial: bool,
|
|
||||||
pub(crate) members: Vec<&'src NamespaceMember<'src>>,
|
|
||||||
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
pub(crate) operations: BTreeMap<OperationId<'src>, OperationData<'src>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +73,7 @@ pub(crate) struct DictionaryData<'src> {
|
|||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||||
pub(crate) enum OperationId<'src> {
|
pub(crate) enum OperationId<'src> {
|
||||||
Constructor,
|
Constructor(IgnoreTraits<&'src str>),
|
||||||
Operation(Option<&'src str>),
|
Operation(Option<&'src str>),
|
||||||
IndexingGetter,
|
IndexingGetter,
|
||||||
IndexingSetter,
|
IndexingSetter,
|
||||||
@ -80,9 +82,22 @@ pub(crate) enum OperationId<'src> {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct OperationData<'src> {
|
pub(crate) struct OperationData<'src> {
|
||||||
pub(crate) overloaded: bool,
|
pub(crate) signatures: Vec<Signature<'src>>,
|
||||||
/// Map from argument names to whether they are the same for multiple overloads
|
pub(crate) is_static: bool,
|
||||||
pub(crate) argument_names_same: BTreeMap<Vec<&'src str>, bool>,
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct Signature<'src> {
|
||||||
|
pub(crate) args: Vec<Arg<'src>>,
|
||||||
|
pub(crate) ret: weedle::types::ReturnType<'src>,
|
||||||
|
pub(crate) attrs: &'src Option<ExtendedAttributeList<'src>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct Arg<'src> {
|
||||||
|
pub(crate) name: &'src str,
|
||||||
|
pub(crate) ty: &'src weedle::types::Type<'src>,
|
||||||
|
pub(crate) optional: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implemented on an AST node to populate the `FirstPassRecord` struct.
|
/// Implemented on an AST node to populate the `FirstPassRecord` struct.
|
||||||
@ -117,8 +132,14 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> {
|
|||||||
Namespace(namespace) => namespace.first_pass(record, ()),
|
Namespace(namespace) => namespace.first_pass(record, ()),
|
||||||
PartialNamespace(namespace) => namespace.first_pass(record, ()),
|
PartialNamespace(namespace) => namespace.first_pass(record, ()),
|
||||||
Typedef(typedef) => typedef.first_pass(record, ()),
|
Typedef(typedef) => typedef.first_pass(record, ()),
|
||||||
_ => {
|
|
||||||
// Other definitions aren't currently used in the first pass
|
Implements(_) => Ok(()),
|
||||||
|
Callback(..) => {
|
||||||
|
warn!("Unsupported WebIDL Callback definition: {:?}", self);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
CallbackInterface(..) => {
|
||||||
|
warn!("Unsupported WebIDL CallbackInterface definition: {:?}", self);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,6 +148,10 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> {
|
|||||||
|
|
||||||
impl<'src> FirstPass<'src, ()> for weedle::DictionaryDefinition<'src> {
|
impl<'src> FirstPass<'src, ()> for weedle::DictionaryDefinition<'src> {
|
||||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||||
|
if util::is_chrome_only(&self.attributes) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
record.dictionaries.entry(self.identifier.0)
|
record.dictionaries.entry(self.identifier.0)
|
||||||
.or_default()
|
.or_default()
|
||||||
.definition = Some(self);
|
.definition = Some(self);
|
||||||
@ -136,6 +161,10 @@ impl<'src> FirstPass<'src, ()> for weedle::DictionaryDefinition<'src> {
|
|||||||
|
|
||||||
impl<'src> FirstPass<'src, ()> for weedle::PartialDictionaryDefinition<'src> {
|
impl<'src> FirstPass<'src, ()> for weedle::PartialDictionaryDefinition<'src> {
|
||||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||||
|
if util::is_chrome_only(&self.attributes) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
record.dictionaries.entry(self.identifier.0)
|
record.dictionaries.entry(self.identifier.0)
|
||||||
.or_default()
|
.or_default()
|
||||||
.partials
|
.partials
|
||||||
@ -150,7 +179,7 @@ impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !record.enums.insert(self.identifier.0) {
|
if record.enums.insert(self.identifier.0, self).is_some() {
|
||||||
info!("Encountered multiple enum declarations: {}", self.identifier.0);
|
info!("Encountered multiple enum declarations: {}", self.identifier.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,8 +215,15 @@ fn first_pass_operation<'src>(
|
|||||||
first_pass_operation_type: FirstPassOperationType,
|
first_pass_operation_type: FirstPassOperationType,
|
||||||
self_name: &'src str,
|
self_name: &'src str,
|
||||||
ids: &[OperationId<'src>],
|
ids: &[OperationId<'src>],
|
||||||
arguments: &[Argument<'src>],
|
arguments: &'src [Argument<'src>],
|
||||||
) -> Result<()> {
|
ret: &weedle::types::ReturnType<'src>,
|
||||||
|
attrs: &'src Option<ExtendedAttributeList<'src>>,
|
||||||
|
is_static: bool,
|
||||||
|
) {
|
||||||
|
if util::is_chrome_only(attrs) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let mut names = Vec::with_capacity(arguments.len());
|
let mut names = Vec::with_capacity(arguments.len());
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
match argument {
|
match argument {
|
||||||
@ -195,41 +231,55 @@ fn first_pass_operation<'src>(
|
|||||||
Argument::Variadic(variadic) => names.push(variadic.identifier.0),
|
Argument::Variadic(variadic) => names.push(variadic.identifier.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let operations = match first_pass_operation_type{
|
let operations = match first_pass_operation_type {
|
||||||
FirstPassOperationType::Interface => {
|
FirstPassOperationType::Interface => {
|
||||||
&mut record
|
let x = record
|
||||||
.interfaces
|
.interfaces
|
||||||
.get_mut(self_name)
|
.get_mut(self_name)
|
||||||
.expect(&format!("not found {} interface", self_name))
|
.expect(&format!("not found {} interface", self_name));
|
||||||
.operations
|
&mut x.operations
|
||||||
},
|
},
|
||||||
FirstPassOperationType::Mixin => {
|
FirstPassOperationType::Mixin => {
|
||||||
&mut record
|
let x = record
|
||||||
.mixins
|
.mixins
|
||||||
.get_mut(self_name)
|
.get_mut(self_name)
|
||||||
.expect(&format!("not found {} mixin", self_name))
|
.expect(&format!("not found {} mixin", self_name));
|
||||||
.operations
|
&mut x.operations
|
||||||
},
|
},
|
||||||
FirstPassOperationType::Namespace => {
|
FirstPassOperationType::Namespace => {
|
||||||
&mut record
|
let x = record
|
||||||
.namespaces
|
.namespaces
|
||||||
.get_mut(self_name)
|
.get_mut(self_name)
|
||||||
.expect(&format!("not found {} namesace", self_name))
|
.expect(&format!("not found {} namespace", self_name));
|
||||||
.operations
|
&mut x.operations
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
for id in ids {
|
let mut args = Vec::with_capacity(arguments.len());
|
||||||
operations
|
for argument in arguments {
|
||||||
.entry(*id)
|
let arg = match argument {
|
||||||
.and_modify(|operation_data| operation_data.overloaded = true)
|
Argument::Single(single) => single,
|
||||||
.or_default()
|
Argument::Variadic(v) => {
|
||||||
.argument_names_same
|
warn!("Unsupported variadic argument {} in {}",
|
||||||
.entry(names.clone())
|
v.identifier.0,
|
||||||
.and_modify(|same_argument_names| *same_argument_names = true)
|
self_name);
|
||||||
.or_insert(false);
|
return
|
||||||
|
}
|
||||||
|
};
|
||||||
|
args.push(Arg {
|
||||||
|
name: arg.identifier.0,
|
||||||
|
ty: &arg.type_.type_,
|
||||||
|
optional: arg.optional.is_some(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for id in ids {
|
||||||
|
let op = operations.entry(*id).or_default();
|
||||||
|
op.is_static = is_static;
|
||||||
|
op.signatures.push(Signature {
|
||||||
|
args: args.clone(),
|
||||||
|
ret: ret.clone(),
|
||||||
|
attrs,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
|
impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
|
||||||
@ -238,6 +288,11 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if util::is_no_interface_object(&self.attributes) {
|
||||||
|
info!("Skipping because of `NoInterfaceObject` attribute: {:?}", self.identifier.0);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let interface_data = record
|
let interface_data = record
|
||||||
.interfaces
|
.interfaces
|
||||||
@ -245,39 +300,18 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> {
|
|||||||
.or_default();
|
.or_default();
|
||||||
interface_data.partial = false;
|
interface_data.partial = false;
|
||||||
interface_data.superclass = self.inheritance.map(|s| s.identifier.0);
|
interface_data.superclass = self.inheritance.map(|s| s.identifier.0);
|
||||||
|
interface_data.definition_attributes = self.attributes.as_ref()
|
||||||
|
.map(|l| &l.body.list[..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(attrs) = &self.attributes {
|
if let Some(attrs) = &self.attributes {
|
||||||
for attr in &attrs.body.list {
|
for attr in attrs.body.list.iter() {
|
||||||
attr.first_pass(record, self.identifier.0)?;
|
process_interface_attribute(
|
||||||
}
|
record,
|
||||||
}
|
self.identifier.0,
|
||||||
|
attr,
|
||||||
for member in &self.members.body {
|
|
||||||
member.first_pass(record, self.identifier.0)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> {
|
|
||||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
|
||||||
if util::is_chrome_only(&self.attributes) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
record
|
|
||||||
.interfaces
|
|
||||||
.entry(self.identifier.0)
|
|
||||||
.or_insert_with(||
|
|
||||||
InterfaceData {
|
|
||||||
partial: true,
|
|
||||||
global: false,
|
|
||||||
operations: Default::default(),
|
|
||||||
superclass: None,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for member in &self.members.body {
|
for member in &self.members.body {
|
||||||
member.first_pass(record, self.identifier.0)?;
|
member.first_pass(record, self.identifier.0)?;
|
||||||
@ -287,26 +321,43 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> {
|
fn process_interface_attribute<'src>(
|
||||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
record: &mut FirstPassRecord<'src>,
|
||||||
match self {
|
self_name: &'src str,
|
||||||
ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => {
|
attr: &'src ExtendedAttribute<'src>
|
||||||
|
) {
|
||||||
|
let ident = weedle::common::Identifier(self_name);
|
||||||
|
let non_null = weedle::types::MayBeNull { type_: ident, q_mark: None };
|
||||||
|
let non_any = weedle::types::NonAnyType::Identifier(non_null);
|
||||||
|
let single = weedle::types::SingleType::NonAny(non_any);
|
||||||
|
let ty = weedle::types::Type::Single(single);
|
||||||
|
let return_ty = weedle::types::ReturnType::Type(ty);
|
||||||
|
match attr {
|
||||||
|
ExtendedAttribute::ArgList(list)
|
||||||
|
if list.identifier.0 == "Constructor" =>
|
||||||
|
{
|
||||||
first_pass_operation(
|
first_pass_operation(
|
||||||
record,
|
record,
|
||||||
FirstPassOperationType::Interface,
|
FirstPassOperationType::Interface,
|
||||||
self_name,
|
self_name,
|
||||||
&[OperationId::Constructor],
|
&[OperationId::Constructor(IgnoreTraits(self_name))],
|
||||||
&list.args.body.list,
|
&list.args.body.list,
|
||||||
)
|
&return_ty,
|
||||||
|
&None,
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ExtendedAttribute::NoArgs(name) if (name.0).0 == "Constructor" => {
|
ExtendedAttribute::NoArgs(other) if (other.0).0 == "Constructor" => {
|
||||||
first_pass_operation(
|
first_pass_operation(
|
||||||
record,
|
record,
|
||||||
FirstPassOperationType::Interface,
|
FirstPassOperationType::Interface,
|
||||||
self_name,
|
self_name,
|
||||||
&[OperationId::Constructor],
|
&[OperationId::Constructor(IgnoreTraits(self_name))],
|
||||||
&[],
|
&[],
|
||||||
)
|
&return_ty,
|
||||||
|
&None,
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ExtendedAttribute::NamedArgList(list)
|
ExtendedAttribute::NamedArgList(list)
|
||||||
if list.lhs_identifier.0 == "NamedConstructor" =>
|
if list.lhs_identifier.0 == "NamedConstructor" =>
|
||||||
@ -315,48 +366,100 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> {
|
|||||||
record,
|
record,
|
||||||
FirstPassOperationType::Interface,
|
FirstPassOperationType::Interface,
|
||||||
self_name,
|
self_name,
|
||||||
&[OperationId::Constructor],
|
&[OperationId::Constructor(IgnoreTraits(list.rhs_identifier.0))],
|
||||||
&list.args.body.list,
|
&list.args.body.list,
|
||||||
)
|
&return_ty,
|
||||||
|
&None,
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Global" => {
|
ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Global" => {
|
||||||
record.interfaces.get_mut(self_name).unwrap().global = true;
|
record.interfaces.get_mut(self_name).unwrap().global = true;
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
ExtendedAttribute::IdentList(id) if id.identifier.0 == "Global" => {
|
ExtendedAttribute::IdentList(id) if id.identifier.0 == "Global" => {
|
||||||
record.interfaces.get_mut(self_name).unwrap().global = true;
|
record.interfaces.get_mut(self_name).unwrap().global = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> {
|
||||||
|
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> {
|
||||||
|
if util::is_chrome_only(&self.attributes) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
record
|
||||||
|
.interfaces
|
||||||
|
.entry(self.identifier.0)
|
||||||
|
.or_insert_with(||
|
||||||
|
InterfaceData {
|
||||||
|
partial: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
for member in &self.members.body {
|
||||||
|
member.first_pass(record, self.identifier.0)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> FirstPass<'src, &'src str> for weedle::interface::InterfaceMember<'src> {
|
impl<'src> FirstPass<'src, &'src str> for weedle::interface::InterfaceMember<'src> {
|
||||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
weedle::interface::InterfaceMember::Operation(op) => {
|
InterfaceMember::Attribute(attr) => {
|
||||||
|
attr.first_pass(record, self_name)
|
||||||
|
}
|
||||||
|
InterfaceMember::Operation(op) => {
|
||||||
op.first_pass(record, self_name)
|
op.first_pass(record, self_name)
|
||||||
}
|
}
|
||||||
_ => Ok(()),
|
InterfaceMember::Const(const_) => {
|
||||||
|
if util::is_chrome_only(&const_.attributes) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
record.interfaces
|
||||||
|
.get_mut(self_name)
|
||||||
|
.unwrap()
|
||||||
|
.consts
|
||||||
|
.push(const_);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
InterfaceMember::Iterable(_iterable) => {
|
||||||
|
warn!("Unsupported WebIDL iterable interface member: {:?}", self);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
InterfaceMember::Maplike(_) => {
|
||||||
|
warn!("Unsupported WebIDL Maplike interface member: {:?}", self);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
InterfaceMember::Stringifier(_) => {
|
||||||
|
warn!("Unsupported WebIDL Stringifier interface member: {:?}", self);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
InterfaceMember::Setlike(_) => {
|
||||||
|
warn!("Unsupported WebIDL Setlike interface member: {:?}", self);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceMember<'src> {
|
impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceMember<'src> {
|
||||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||||
if util::is_chrome_only(&self.attributes) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.specials.len() > 1 {
|
if self.specials.len() > 1 {
|
||||||
warn!("Unsupported webidl operation: {:?}", self);
|
warn!("Unsupported webidl operation: {:?}", self);
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
if let Some(StringifierOrStatic::Stringifier(_)) = self.modifier {
|
let is_static = match self.modifier {
|
||||||
|
Some(StringifierOrStatic::Stringifier(_)) => {
|
||||||
warn!("Unsupported webidl stringifier: {:?}", self);
|
warn!("Unsupported webidl stringifier: {:?}", self);
|
||||||
return Ok(())
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
Some(StringifierOrStatic::Static(_)) => true,
|
||||||
|
None => false,
|
||||||
|
};
|
||||||
|
|
||||||
let mut ids = vec![OperationId::Operation(self.identifier.map(|s| s.0))];
|
let mut ids = vec![OperationId::Operation(self.identifier.map(|s| s.0))];
|
||||||
for special in self.specials.iter() {
|
for special in self.specials.iter() {
|
||||||
ids.push(match special {
|
ids.push(match special {
|
||||||
@ -372,7 +475,26 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM
|
|||||||
self_name,
|
self_name,
|
||||||
&ids,
|
&ids,
|
||||||
&self.args.body.list,
|
&self.args.body.list,
|
||||||
)
|
&self.return_type,
|
||||||
|
&self.attributes,
|
||||||
|
is_static,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'src> FirstPass<'src, &'src str> for weedle::interface::AttributeInterfaceMember<'src> {
|
||||||
|
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||||
|
if util::is_chrome_only(&self.attributes) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
record.interfaces
|
||||||
|
.get_mut(self_name)
|
||||||
|
.unwrap()
|
||||||
|
.attributes
|
||||||
|
.push(self);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +510,6 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{
|
|||||||
.entry(self.identifier.0)
|
.entry(self.identifier.0)
|
||||||
.or_default();
|
.or_default();
|
||||||
mixin_data.partial = false;
|
mixin_data.partial = false;
|
||||||
mixin_data.members.extend(&self.members.body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for member in &self.members.body {
|
for member in &self.members.body {
|
||||||
@ -411,12 +532,9 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src>
|
|||||||
.or_insert_with(||
|
.or_insert_with(||
|
||||||
MixinData {
|
MixinData {
|
||||||
partial: true,
|
partial: true,
|
||||||
members: Default::default(),
|
..Default::default()
|
||||||
operations: Default::default(),
|
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
.members
|
|
||||||
.extend(&self.members.body);
|
|
||||||
|
|
||||||
for member in &self.members.body {
|
for member in &self.members.body {
|
||||||
member.first_pass(record, self.identifier.0)?;
|
member.first_pass(record, self.identifier.0)?;
|
||||||
@ -429,20 +547,29 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src>
|
|||||||
impl<'src> FirstPass<'src, &'src str> for weedle::mixin::MixinMember<'src> {
|
impl<'src> FirstPass<'src, &'src str> for weedle::mixin::MixinMember<'src> {
|
||||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
weedle::mixin::MixinMember::Operation(op) => {
|
MixinMember::Operation(op) => op.first_pass(record, self_name),
|
||||||
op.first_pass(record, self_name)
|
MixinMember::Attribute(a) => a.first_pass(record, self_name),
|
||||||
|
MixinMember::Const(a) => {
|
||||||
|
if util::is_chrome_only(&a.attributes) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
record.mixins
|
||||||
|
.get_mut(self_name)
|
||||||
|
.unwrap()
|
||||||
|
.consts
|
||||||
|
.push(a);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
MixinMember::Stringifier(_) => {
|
||||||
|
warn!("Unsupported WebIDL stringifier mixin member: {:?}", self);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Ok(()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'src> {
|
impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'src> {
|
||||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||||
if util::is_chrome_only(&self.attributes) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.stringifier.is_some() {
|
if self.stringifier.is_some() {
|
||||||
warn!("Unsupported webidl stringifier: {:?}", self);
|
warn!("Unsupported webidl stringifier: {:?}", self);
|
||||||
return Ok(())
|
return Ok(())
|
||||||
@ -454,7 +581,25 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'s
|
|||||||
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,
|
||||||
)
|
&self.return_type,
|
||||||
|
&self.attributes,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'src> FirstPass<'src, &'src str> for weedle::mixin::AttributeMixinMember<'src> {
|
||||||
|
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||||
|
if util::is_chrome_only(&self.attributes) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
record.mixins
|
||||||
|
.get_mut(self_name)
|
||||||
|
.unwrap()
|
||||||
|
.attributes
|
||||||
|
.push(self);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,16 +626,7 @@ impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> {
|
|||||||
record
|
record
|
||||||
.namespaces
|
.namespaces
|
||||||
.entry(self.identifier.0)
|
.entry(self.identifier.0)
|
||||||
.and_modify(|namespace_data| namespace_data.partial = false)
|
.or_default();
|
||||||
.or_insert_with(||
|
|
||||||
NamespaceData {
|
|
||||||
partial: true,
|
|
||||||
members: Default::default(),
|
|
||||||
operations: Default::default(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.members
|
|
||||||
.extend(&self.members.body);
|
|
||||||
|
|
||||||
for member in &self.members.body {
|
for member in &self.members.body {
|
||||||
member.first_pass(record, self.identifier.0)?;
|
member.first_pass(record, self.identifier.0)?;
|
||||||
@ -509,9 +645,7 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> {
|
|||||||
record
|
record
|
||||||
.namespaces
|
.namespaces
|
||||||
.entry(self.identifier.0)
|
.entry(self.identifier.0)
|
||||||
.or_default()
|
.or_default();
|
||||||
.members
|
|
||||||
.extend(&self.members.body);
|
|
||||||
|
|
||||||
for member in &self.members.body {
|
for member in &self.members.body {
|
||||||
member.first_pass(record, self.identifier.0)?;
|
member.first_pass(record, self.identifier.0)?;
|
||||||
@ -534,17 +668,17 @@ impl<'src> FirstPass<'src, &'src str> for weedle::namespace::NamespaceMember<'sr
|
|||||||
|
|
||||||
impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> {
|
impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> {
|
||||||
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> {
|
||||||
if util::is_chrome_only(&self.attributes) {
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
first_pass_operation(
|
first_pass_operation(
|
||||||
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,
|
||||||
)
|
&self.return_type,
|
||||||
|
&self.attributes,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,4 +704,49 @@ impl<'a> FirstPassRecord<'a> {
|
|||||||
self.fill_superclasses(superclass, set);
|
self.fill_superclasses(superclass, set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn all_mixins<'me>(&'me self, interface: &str)
|
||||||
|
-> impl Iterator<Item = &'me MixinData<'a>> + 'me
|
||||||
|
{
|
||||||
|
let mut set = Vec::new();
|
||||||
|
self.fill_mixins(interface, interface, &mut set);
|
||||||
|
set.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_mixins<'me>(
|
||||||
|
&'me self,
|
||||||
|
self_name: &str,
|
||||||
|
mixin_name: &str,
|
||||||
|
list: &mut Vec<&'me MixinData<'a>>,
|
||||||
|
) {
|
||||||
|
if let Some(mixin_data) = self.mixins.get(mixin_name) {
|
||||||
|
list.push(mixin_data);
|
||||||
|
}
|
||||||
|
if let Some(mixin_names) = self.includes.get(mixin_name) {
|
||||||
|
for mixin_name in mixin_names {
|
||||||
|
self.fill_mixins(self_name, mixin_name, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct IgnoreTraits<T>(pub T);
|
||||||
|
|
||||||
|
impl<T> PartialEq for IgnoreTraits<T> {
|
||||||
|
fn eq(&self, _other: &IgnoreTraits<T>) -> bool { true }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Eq for IgnoreTraits<T> {}
|
||||||
|
|
||||||
|
impl<T> PartialOrd for IgnoreTraits<T> {
|
||||||
|
fn partial_cmp(&self, _other: &IgnoreTraits<T>) -> Option<Ordering> {
|
||||||
|
Some(Ordering::Equal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Ord for IgnoreTraits<T> {
|
||||||
|
fn cmp(&self, _other: &IgnoreTraits<T>) -> Ordering {
|
||||||
|
Ordering::Equal
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -287,7 +287,7 @@ impl<'a> ToIdlType<'a> for Identifier<'a> {
|
|||||||
Some(IdlType::Interface(self.0))
|
Some(IdlType::Interface(self.0))
|
||||||
} else if record.dictionaries.contains_key(self.0) {
|
} else if record.dictionaries.contains_key(self.0) {
|
||||||
Some(IdlType::Dictionary(self.0))
|
Some(IdlType::Dictionary(self.0))
|
||||||
} else if record.enums.contains(self.0) {
|
} else if record.enums.contains_key(self.0) {
|
||||||
Some(IdlType::Enum(self.0))
|
Some(IdlType::Enum(self.0))
|
||||||
} else {
|
} else {
|
||||||
warn!("Unrecognized type: {}", self.0);
|
warn!("Unrecognized type: {}", self.0);
|
||||||
@ -596,88 +596,3 @@ fn idl_type_flatten_test() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts arguments into possibilities.
|
|
||||||
///
|
|
||||||
/// Each argument represented with a tuple of its idl type and whether it is optional.
|
|
||||||
/// Each possibility is a vector of idl types.
|
|
||||||
///
|
|
||||||
/// The goal is to find equivalent possibilities of argument types each of which is not optional and
|
|
||||||
/// does not contains union types.
|
|
||||||
pub(crate) fn flatten<'a>(arguments: &'a [(IdlType, bool)]) -> Vec<Vec<IdlType<'a>>> {
|
|
||||||
if arguments.is_empty() {
|
|
||||||
return vec![Vec::new()];
|
|
||||||
}
|
|
||||||
let mut optional_possibilities = if arguments[0].1 { vec![Vec::new()] } else { Vec::new() };
|
|
||||||
let mut possibilities = Vec::new();
|
|
||||||
for idl_type in arguments[0].0.flatten() {
|
|
||||||
possibilities.push(vec![idl_type])
|
|
||||||
}
|
|
||||||
for argument in arguments[1..].iter() {
|
|
||||||
let mut new_possibilities = Vec::new();
|
|
||||||
for old_idl_types in possibilities {
|
|
||||||
if argument.1 {
|
|
||||||
optional_possibilities.push(old_idl_types.clone());
|
|
||||||
}
|
|
||||||
for idl_type in argument.0.flatten() {
|
|
||||||
let mut new_idl_types = old_idl_types.clone();
|
|
||||||
new_idl_types.push(idl_type);
|
|
||||||
new_possibilities.push(new_idl_types)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
possibilities = new_possibilities;
|
|
||||||
}
|
|
||||||
optional_possibilities.extend(possibilities.into_iter());
|
|
||||||
optional_possibilities
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn arguments_flatten_test() {
|
|
||||||
use self::IdlType::*;
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
flatten(
|
|
||||||
&vec![
|
|
||||||
(
|
|
||||||
Union(vec![
|
|
||||||
Short,
|
|
||||||
Long,
|
|
||||||
]),
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Union(vec![
|
|
||||||
Sequence(Box::new(
|
|
||||||
Union(vec![
|
|
||||||
Byte,
|
|
||||||
Octet,
|
|
||||||
]),
|
|
||||||
)),
|
|
||||||
LongLong,
|
|
||||||
]),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
(
|
|
||||||
DomString,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
]
|
|
||||||
),
|
|
||||||
vec![
|
|
||||||
vec![Short],
|
|
||||||
vec![Long],
|
|
||||||
vec![Short, Sequence(Box::new(Byte))],
|
|
||||||
vec![Short, Sequence(Box::new(Octet))],
|
|
||||||
vec![Short, LongLong],
|
|
||||||
vec![Long, Sequence(Box::new(Byte))],
|
|
||||||
vec![Long, Sequence(Box::new(Octet))],
|
|
||||||
vec![Long, LongLong],
|
|
||||||
vec![Short, Sequence(Box::new(Byte)), DomString],
|
|
||||||
vec![Short, Sequence(Box::new(Octet)), DomString],
|
|
||||||
vec![Short, LongLong, DomString],
|
|
||||||
vec![Long, Sequence(Box::new(Byte)), DomString],
|
|
||||||
vec![Long, Sequence(Box::new(Octet)), DomString],
|
|
||||||
vec![Long, LongLong, DomString]
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
|||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::collections::BTreeMap;
|
use std::ptr;
|
||||||
|
|
||||||
use backend;
|
use backend;
|
||||||
use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident};
|
use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident};
|
||||||
@ -8,11 +8,10 @@ use proc_macro2::Ident;
|
|||||||
use syn;
|
use syn;
|
||||||
use weedle;
|
use weedle;
|
||||||
use weedle::attribute::{ExtendedAttributeList, ExtendedAttribute};
|
use weedle::attribute::{ExtendedAttributeList, ExtendedAttribute};
|
||||||
use weedle::argument::Argument;
|
|
||||||
use weedle::literal::{ConstValue, FloatLit, IntegerLit};
|
use weedle::literal::{ConstValue, FloatLit, IntegerLit};
|
||||||
|
|
||||||
use first_pass::{self, FirstPassRecord};
|
use first_pass::{FirstPassRecord, OperationId, OperationData, Signature};
|
||||||
use idl_type::{IdlType, ToIdlType, flatten};
|
use idl_type::{IdlType, ToIdlType};
|
||||||
|
|
||||||
/// Take a type and create an immutable shared reference to that type.
|
/// Take a type and create an immutable shared reference to that type.
|
||||||
pub(crate) fn shared_ref(ty: syn::Type) -> syn::Type {
|
pub(crate) fn shared_ref(ty: syn::Type) -> syn::Type {
|
||||||
@ -196,116 +195,23 @@ pub enum TypePosition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> FirstPassRecord<'src> {
|
impl<'src> FirstPassRecord<'src> {
|
||||||
/// Create a wasm-bindgen function, if possible.
|
pub fn create_one_function<'a>(
|
||||||
pub fn create_function(
|
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
js_name: &str,
|
||||||
overloaded: bool,
|
rust_name: &str,
|
||||||
same_argument_names: bool,
|
idl_arguments: impl Iterator<Item = (&'a str, &'a IdlType<'src>)>,
|
||||||
arguments: &[(&str, IdlType<'src>, bool)],
|
ret: &IdlType<'src>,
|
||||||
ret: IdlType<'src>,
|
|
||||||
kind: backend::ast::ImportFunctionKind,
|
kind: backend::ast::ImportFunctionKind,
|
||||||
structural: bool,
|
structural: bool,
|
||||||
catch: bool,
|
catch: bool,
|
||||||
doc_comment: Option<String>,
|
doc_comment: Option<String>,
|
||||||
) -> Vec<backend::ast::ImportFunction> {
|
) -> Option<backend::ast::ImportFunction> where 'src: 'a {
|
||||||
let rust_name = if overloaded && !arguments.is_empty() {
|
// Convert all of the arguments from their IDL type to a `syn` type,
|
||||||
let mut argument_type_names = String::new();
|
// ready to pass to the backend.
|
||||||
for argument in arguments {
|
//
|
||||||
if argument_type_names.len() > 0 {
|
// Note that for non-static methods we add a `&self` type placeholder,
|
||||||
argument_type_names.push_str("_and_");
|
// but this type isn't actually used so it's just here for show mostly.
|
||||||
}
|
let mut arguments = if let &backend::ast::ImportFunctionKind::Method {
|
||||||
if same_argument_names {
|
|
||||||
argument.1.push_type_name(&mut argument_type_names);
|
|
||||||
} else {
|
|
||||||
argument_type_names.push_str(&argument.0.to_snake_case());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if name == "new" {
|
|
||||||
"with_".to_owned() + &argument_type_names
|
|
||||||
} else {
|
|
||||||
name.to_snake_case() + "_with_" + &argument_type_names
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name.to_snake_case()
|
|
||||||
};
|
|
||||||
|
|
||||||
let ret = match ret {
|
|
||||||
IdlType::Void => None,
|
|
||||||
ret @ _ => {
|
|
||||||
match ret.to_syn_type(TypePosition::Return) {
|
|
||||||
None => {
|
|
||||||
warn!(
|
|
||||||
"Unsupported return type: {:?} on {:?}",
|
|
||||||
ret,
|
|
||||||
rust_name
|
|
||||||
);
|
|
||||||
return Vec::new();
|
|
||||||
},
|
|
||||||
Some(ret) => Some(ret),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let js_ret = ret.clone();
|
|
||||||
|
|
||||||
let ret = if catch {
|
|
||||||
Some(ret.map_or_else(|| result_ty(unit_ty()), result_ty))
|
|
||||||
} else {
|
|
||||||
ret
|
|
||||||
};
|
|
||||||
|
|
||||||
let converted_arguments = arguments
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|(_name, idl_type, optional)| (idl_type, optional))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let possibilities = flatten(&converted_arguments);
|
|
||||||
let mut arguments_count_multiple = BTreeMap::new();
|
|
||||||
for idl_types in &possibilities {
|
|
||||||
arguments_count_multiple
|
|
||||||
.entry(idl_types.len())
|
|
||||||
.and_modify(|variants_count| { *variants_count = true; })
|
|
||||||
.or_insert(false);
|
|
||||||
}
|
|
||||||
let mut import_functions = Vec::new();
|
|
||||||
'outer: for idl_types in &possibilities {
|
|
||||||
let rust_name = if possibilities.len() > 1 {
|
|
||||||
let mut rust_name = rust_name.clone();
|
|
||||||
let mut first = true;
|
|
||||||
let iter = arguments.iter().zip(idl_types).enumerate();
|
|
||||||
for (i, ((argument_name, _, _), idl_type)) in iter {
|
|
||||||
if possibilities.iter().all(|p| p.get(i) == Some(idl_type)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if first {
|
|
||||||
rust_name.push_str("_with_");
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
rust_name.push_str("_and_");
|
|
||||||
}
|
|
||||||
if arguments_count_multiple[&idl_types.len()] {
|
|
||||||
idl_type.push_type_name(&mut rust_name);
|
|
||||||
} else {
|
|
||||||
rust_name.push_str(&argument_name.to_snake_case());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rust_name
|
|
||||||
} else {
|
|
||||||
rust_name.clone()
|
|
||||||
};
|
|
||||||
let rust_name = rust_ident(&rust_name);
|
|
||||||
let shim = {
|
|
||||||
let ns = match kind {
|
|
||||||
backend::ast::ImportFunctionKind::ScopedMethod { .. } |
|
|
||||||
backend::ast::ImportFunctionKind::Normal => "",
|
|
||||||
backend::ast::ImportFunctionKind::Method { ref class, .. } => class,
|
|
||||||
};
|
|
||||||
|
|
||||||
raw_ident(&format!("__widl_f_{}_{}", rust_name, ns))
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut args_captured = if let &backend::ast::ImportFunctionKind::Method {
|
|
||||||
ref ty,
|
ref ty,
|
||||||
kind: backend::ast::MethodKind::Operation(
|
kind: backend::ast::MethodKind::Operation(
|
||||||
backend::ast::Operation {
|
backend::ast::Operation {
|
||||||
@ -314,339 +220,144 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
),
|
),
|
||||||
..
|
..
|
||||||
} = &kind {
|
} = &kind {
|
||||||
let mut res = Vec::with_capacity(idl_types.len() + 1);
|
let mut res = Vec::with_capacity(idl_arguments.size_hint().0 + 1);
|
||||||
res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone())));
|
res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone())));
|
||||||
res
|
res
|
||||||
} else {
|
} else {
|
||||||
Vec::with_capacity(idl_types.len())
|
Vec::with_capacity(idl_arguments.size_hint().0)
|
||||||
};
|
};
|
||||||
for ((argument_name, _, _), idl_type) in arguments.iter().zip(idl_types) {
|
for (argument_name, idl_type) in idl_arguments {
|
||||||
let syn_type = if let Some(syn_type) = idl_type.to_syn_type(TypePosition::Argument) {
|
let syn_type = match idl_type.to_syn_type(TypePosition::Argument) {
|
||||||
syn_type
|
Some(t) => t,
|
||||||
} else {
|
None => {
|
||||||
warn!(
|
warn!(
|
||||||
"Unsupported argument type: {:?} on {:?}",
|
"Unsupported argument type: {:?} on {:?}",
|
||||||
idl_type,
|
idl_type,
|
||||||
rust_name
|
rust_name
|
||||||
);
|
);
|
||||||
continue 'outer;
|
return None
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let argument_name = rust_ident(&argument_name.to_snake_case());
|
let argument_name = rust_ident(&argument_name.to_snake_case());
|
||||||
args_captured.push(simple_fn_arg(argument_name, syn_type));
|
arguments.push(simple_fn_arg(argument_name, syn_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
import_functions.push(backend::ast::ImportFunction {
|
// Convert the return type to a `syn` type, handling the `catch`
|
||||||
|
// attribute here to use a `Result` in Rust.
|
||||||
|
let ret = match ret {
|
||||||
|
IdlType::Void => None,
|
||||||
|
ret @ _ => {
|
||||||
|
match ret.to_syn_type(TypePosition::Return) {
|
||||||
|
Some(ret) => Some(ret),
|
||||||
|
None => {
|
||||||
|
warn!(
|
||||||
|
"Unsupported return type: {:?} on {:?}",
|
||||||
|
ret,
|
||||||
|
rust_name
|
||||||
|
);
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let js_ret = ret.clone();
|
||||||
|
let ret = if catch {
|
||||||
|
Some(ret.map_or_else(|| result_ty(unit_ty()), result_ty))
|
||||||
|
} else {
|
||||||
|
ret
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(backend::ast::ImportFunction {
|
||||||
function: backend::ast::Function {
|
function: backend::ast::Function {
|
||||||
name: name.to_string(),
|
name: js_name.to_string(),
|
||||||
arguments: args_captured,
|
arguments,
|
||||||
ret: ret.clone(),
|
ret: ret.clone(),
|
||||||
rust_attrs: vec![],
|
rust_attrs: vec![],
|
||||||
rust_vis: public(),
|
rust_vis: public(),
|
||||||
},
|
},
|
||||||
rust_name,
|
rust_name: rust_ident(rust_name),
|
||||||
js_ret: js_ret.clone(),
|
js_ret: js_ret.clone(),
|
||||||
catch,
|
|
||||||
variadic: false,
|
variadic: false,
|
||||||
|
catch,
|
||||||
structural,
|
structural,
|
||||||
kind: kind.clone(),
|
shim:{
|
||||||
shim,
|
let ns = match kind {
|
||||||
doc_comment: doc_comment.clone(),
|
backend::ast::ImportFunctionKind::ScopedMethod { .. } |
|
||||||
})
|
backend::ast::ImportFunctionKind::Normal => "",
|
||||||
}
|
backend::ast::ImportFunctionKind::Method { ref class, .. } => class,
|
||||||
import_functions
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert arguments to ones suitable crating function
|
|
||||||
pub(crate) fn convert_arguments(
|
|
||||||
&self,
|
|
||||||
arguments: &[weedle::argument::Argument<'src>],
|
|
||||||
) -> Option<Vec<(&str, IdlType<'src>, bool)>> {
|
|
||||||
let mut converted_arguments = Vec::with_capacity(arguments.len());
|
|
||||||
for argument in arguments {
|
|
||||||
let name = match argument {
|
|
||||||
Argument::Single(single) => single.identifier.0,
|
|
||||||
Argument::Variadic(variadic) => variadic.identifier.0,
|
|
||||||
};
|
};
|
||||||
let ty = match argument {
|
raw_ident(&format!("__widl_f_{}_{}", rust_name, ns))
|
||||||
Argument::Single(single) => &single.type_.type_,
|
|
||||||
Argument::Variadic(variadic) => &variadic.type_,
|
|
||||||
};
|
|
||||||
let idl_type = ty.to_idl_type(self)?;
|
|
||||||
let optional = match argument {
|
|
||||||
Argument::Single(single) => single.optional.is_some(),
|
|
||||||
Argument::Variadic(_variadic) => false,
|
|
||||||
};
|
|
||||||
converted_arguments.push((name, idl_type, optional));
|
|
||||||
}
|
|
||||||
Some(converted_arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a wasm-bindgen method, if possible.
|
|
||||||
pub fn create_basic_method(
|
|
||||||
&self,
|
|
||||||
arguments: &[Argument],
|
|
||||||
operation_id: first_pass::OperationId,
|
|
||||||
return_type: &weedle::types::ReturnType,
|
|
||||||
self_name: &str,
|
|
||||||
is_static: bool,
|
|
||||||
structural: bool,
|
|
||||||
catch: bool,
|
|
||||||
global: bool,
|
|
||||||
) -> Vec<backend::ast::ImportFunction> {
|
|
||||||
let (overloaded, same_argument_names) = self.get_operation_overloading(
|
|
||||||
arguments,
|
|
||||||
&operation_id,
|
|
||||||
self_name,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
let name = match &operation_id {
|
|
||||||
first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
|
|
||||||
first_pass::OperationId::Operation(name) => match name {
|
|
||||||
None => {
|
|
||||||
warn!("Unsupported unnamed operation: {:?}", operation_id);
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
Some(name) => name,
|
|
||||||
},
|
},
|
||||||
first_pass::OperationId::IndexingGetter => "get",
|
|
||||||
first_pass::OperationId::IndexingSetter => "set",
|
|
||||||
first_pass::OperationId::IndexingDeleter => "delete",
|
|
||||||
};
|
|
||||||
let operation_kind = match &operation_id {
|
|
||||||
first_pass::OperationId::Constructor => panic!("constructors are unsupported"),
|
|
||||||
first_pass::OperationId::Operation(_) => backend::ast::OperationKind::Regular,
|
|
||||||
first_pass::OperationId::IndexingGetter => backend::ast::OperationKind::IndexingGetter,
|
|
||||||
first_pass::OperationId::IndexingSetter => backend::ast::OperationKind::IndexingSetter,
|
|
||||||
first_pass::OperationId::IndexingDeleter => backend::ast::OperationKind::IndexingDeleter,
|
|
||||||
};
|
|
||||||
let operation = backend::ast::Operation { is_static, kind: operation_kind };
|
|
||||||
let ty = ident_ty(rust_ident(camel_case_ident(&self_name).as_str()));
|
|
||||||
let kind = if global {
|
|
||||||
backend::ast::ImportFunctionKind::ScopedMethod {
|
|
||||||
ty,
|
|
||||||
operation,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
backend::ast::ImportFunctionKind::Method {
|
|
||||||
class: self_name.to_string(),
|
|
||||||
ty,
|
|
||||||
kind: backend::ast::MethodKind::Operation(operation),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let ret = match return_type.to_idl_type(self) {
|
|
||||||
None => return Vec::new(),
|
|
||||||
Some(idl_type) => idl_type,
|
|
||||||
};
|
|
||||||
|
|
||||||
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::IndexingGetter => Some("The indexing getter\n\n".to_string()),
|
|
||||||
first_pass::OperationId::IndexingSetter => Some("The indexing setter\n\n".to_string()),
|
|
||||||
first_pass::OperationId::IndexingDeleter => Some("The indexing deleter\n\n".to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let arguments = match self.convert_arguments(arguments) {
|
|
||||||
None => return Vec::new(),
|
|
||||||
Some(arguments) => arguments
|
|
||||||
};
|
|
||||||
|
|
||||||
self.create_function(
|
|
||||||
&name,
|
|
||||||
overloaded,
|
|
||||||
same_argument_names,
|
|
||||||
&arguments,
|
|
||||||
ret,
|
|
||||||
kind,
|
kind,
|
||||||
structural,
|
|
||||||
catch,
|
|
||||||
doc_comment,
|
doc_comment,
|
||||||
)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether operation is overloaded and
|
|
||||||
/// whether there overloads with same argument names for given argument types
|
|
||||||
pub fn get_operation_overloading(
|
|
||||||
&self,
|
|
||||||
arguments: &[Argument],
|
|
||||||
operation_id: &first_pass::OperationId,
|
|
||||||
self_name: &str,
|
|
||||||
namespace: bool,
|
|
||||||
) -> (bool, bool) {
|
|
||||||
fn get_operation_data<'src>(
|
|
||||||
record: &'src FirstPassRecord,
|
|
||||||
operation_id: &'src ::first_pass::OperationId,
|
|
||||||
self_name: &str,
|
|
||||||
mixin_name: &str,
|
|
||||||
) -> Option<&'src ::first_pass::OperationData<'src>> {
|
|
||||||
if let Some(mixin_data) = record.mixins.get(mixin_name) {
|
|
||||||
if let Some(operation_data) = mixin_data.operations.get(operation_id) {
|
|
||||||
return Some(operation_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(mixin_names) = record.includes.get(mixin_name) {
|
|
||||||
for mixin_name in mixin_names {
|
|
||||||
if let Some(operation_data) = get_operation_data(record, operation_id, self_name, mixin_name) {
|
|
||||||
return Some(operation_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
let operation_data = if !namespace {
|
|
||||||
self
|
|
||||||
.interfaces
|
|
||||||
.get(self_name)
|
|
||||||
.and_then(|interface_data| interface_data.operations.get(operation_id))
|
|
||||||
.unwrap_or_else(||
|
|
||||||
get_operation_data(self, operation_id, self_name, self_name)
|
|
||||||
.expect(&format!("not found operation {:?} in interface {}", operation_id, self_name))
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
.namespaces
|
|
||||||
.get(self_name)
|
|
||||||
.and_then(|interface_data| interface_data.operations.get(operation_id))
|
|
||||||
.expect(&format!("not found operation {:?} in namespace {}", operation_id, self_name))
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut names = Vec::with_capacity(arguments.len());
|
|
||||||
for argument in arguments {
|
|
||||||
match argument {
|
|
||||||
Argument::Single(single) => names.push(single.identifier.0),
|
|
||||||
Argument::Variadic(variadic) => names.push(variadic.identifier.0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(
|
|
||||||
operation_data.overloaded,
|
|
||||||
*operation_data
|
|
||||||
.argument_names_same
|
|
||||||
.get(&names)
|
|
||||||
.unwrap_or(&false)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a wasm-bindgen operation (free function with no `self` type), if possible.
|
|
||||||
pub fn create_namespace_operation(
|
|
||||||
&self,
|
|
||||||
arguments: &[weedle::argument::Argument],
|
|
||||||
operation_name: Option<&str>,
|
|
||||||
return_type: &weedle::types::ReturnType,
|
|
||||||
self_name: &str,
|
|
||||||
catch: bool,
|
|
||||||
) -> Vec<backend::ast::ImportFunction> {
|
|
||||||
let (overloaded, same_argument_names) = self.get_operation_overloading(
|
|
||||||
arguments,
|
|
||||||
&first_pass::OperationId::Operation(operation_name),
|
|
||||||
self_name,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let name = match operation_name {
|
|
||||||
Some(name) => name.to_string(),
|
|
||||||
None => {
|
|
||||||
warn!("Unsupported unnamed operation: on {:?}", self_name);
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let ret = match return_type.to_idl_type(self) {
|
|
||||||
None => return Vec::new(),
|
|
||||||
Some(idl_type) => idl_type,
|
|
||||||
};
|
|
||||||
|
|
||||||
let doc_comment = Some(
|
|
||||||
format!(
|
|
||||||
"The `{}.{}()` function\n\n{}",
|
|
||||||
self_name,
|
|
||||||
name,
|
|
||||||
mdn_doc(self_name, Some(&name))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
let arguments = match self.convert_arguments(arguments) {
|
|
||||||
None => return Vec::new(),
|
|
||||||
Some(arguments) => arguments
|
|
||||||
};
|
|
||||||
|
|
||||||
self.create_function(
|
|
||||||
&name,
|
|
||||||
overloaded,
|
|
||||||
same_argument_names,
|
|
||||||
&arguments,
|
|
||||||
ret,
|
|
||||||
backend::ast::ImportFunctionKind::Normal,
|
|
||||||
false,
|
|
||||||
catch,
|
|
||||||
doc_comment,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a wasm-bindgen getter method, if possible.
|
/// Create a wasm-bindgen getter method, if possible.
|
||||||
pub fn create_getter(
|
pub fn create_getter(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
ty: &weedle::types::Type,
|
ty: &weedle::types::Type<'src>,
|
||||||
self_name: &str,
|
self_name: &str,
|
||||||
is_static: bool,
|
is_static: bool,
|
||||||
is_structural: bool,
|
is_structural: bool,
|
||||||
catch: bool,
|
catch: bool,
|
||||||
global: bool,
|
global: bool,
|
||||||
) -> Vec<backend::ast::ImportFunction> {
|
) -> Option<backend::ast::ImportFunction> {
|
||||||
let ret = match ty.to_idl_type(self) {
|
let kind = backend::ast::OperationKind::Getter(Some(raw_ident(name)));
|
||||||
None => return Vec::new(),
|
let kind = self.import_function_kind(self_name, global, is_static, kind);
|
||||||
Some(idl_type) => idl_type,
|
let ret = ty.to_idl_type(self)?;
|
||||||
};
|
self.create_one_function(
|
||||||
let operation = backend::ast::Operation {
|
&name,
|
||||||
is_static,
|
&name.to_snake_case(),
|
||||||
kind: backend::ast::OperationKind::Getter(Some(raw_ident(name))),
|
None.into_iter(),
|
||||||
};
|
&ret,
|
||||||
let ty = ident_ty(rust_ident(camel_case_ident(&self_name).as_str()));
|
kind,
|
||||||
|
is_structural,
|
||||||
let kind = if global {
|
catch,
|
||||||
backend::ast::ImportFunctionKind::ScopedMethod {
|
Some(format!("The `{}` getter\n\n{}", name, mdn_doc(self_name, Some(name))))
|
||||||
ty,
|
)
|
||||||
operation,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
backend::ast::ImportFunctionKind::Method {
|
|
||||||
class: self_name.to_string(),
|
|
||||||
ty,
|
|
||||||
kind: backend::ast::MethodKind::Operation(operation),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let doc_comment = Some(format!("The `{}` getter\n\n{}", name, mdn_doc(self_name, Some(name))));
|
|
||||||
|
|
||||||
self.create_function(name, false, false, &[], ret, kind, is_structural, catch, doc_comment)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a wasm-bindgen setter method, if possible.
|
/// Create a wasm-bindgen setter method, if possible.
|
||||||
pub fn create_setter(
|
pub fn create_setter(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
field_ty: weedle::types::Type,
|
field_ty: weedle::types::Type<'src>,
|
||||||
self_name: &str,
|
self_name: &str,
|
||||||
is_static: bool,
|
is_static: bool,
|
||||||
is_structural: bool,
|
is_structural: bool,
|
||||||
catch: bool,
|
catch: bool,
|
||||||
global: bool,
|
global: bool,
|
||||||
) -> Vec<backend::ast::ImportFunction> {
|
) -> Option<backend::ast::ImportFunction> {
|
||||||
|
let kind = backend::ast::OperationKind::Setter(Some(raw_ident(name)));
|
||||||
|
let kind = self.import_function_kind(self_name, global, is_static, kind);
|
||||||
|
let field_ty = field_ty.to_idl_type(self)?;
|
||||||
|
self.create_one_function(
|
||||||
|
&name,
|
||||||
|
&format!("set_{}", name).to_snake_case(),
|
||||||
|
Some((name, &field_ty)).into_iter(),
|
||||||
|
&IdlType::Void,
|
||||||
|
kind,
|
||||||
|
is_structural,
|
||||||
|
catch,
|
||||||
|
Some(format!("The `{}` setter\n\n{}", name, mdn_doc(self_name, Some(name))))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn import_function_kind(
|
||||||
|
&self,
|
||||||
|
self_name: &str,
|
||||||
|
global: bool,
|
||||||
|
is_static: bool,
|
||||||
|
operation_kind: backend::ast::OperationKind,
|
||||||
|
) -> backend::ast::ImportFunctionKind {
|
||||||
let operation = backend::ast::Operation {
|
let operation = backend::ast::Operation {
|
||||||
is_static,
|
is_static,
|
||||||
kind: backend::ast::OperationKind::Setter(Some(raw_ident(name))),
|
kind: operation_kind,
|
||||||
};
|
};
|
||||||
let ty = ident_ty(rust_ident(camel_case_ident(&self_name).as_str()));
|
let ty = ident_ty(rust_ident(camel_case_ident(&self_name).as_str()));
|
||||||
|
if global {
|
||||||
let kind = if global {
|
|
||||||
backend::ast::ImportFunctionKind::ScopedMethod {
|
backend::ast::ImportFunctionKind::ScopedMethod {
|
||||||
ty,
|
ty,
|
||||||
operation,
|
operation,
|
||||||
@ -657,27 +368,197 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
ty,
|
ty,
|
||||||
kind: backend::ast::MethodKind::Operation(operation),
|
kind: backend::ast::MethodKind::Operation(operation),
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
let doc_comment = Some(format!("The `{}` setter\n\n{}", name, mdn_doc(self_name, Some(name))));
|
}
|
||||||
|
|
||||||
self.create_function(
|
pub fn create_imports(
|
||||||
&format!("set_{}", name),
|
&self,
|
||||||
false,
|
kind: backend::ast::ImportFunctionKind,
|
||||||
false,
|
id: &OperationId<'src>,
|
||||||
&[(
|
data: &OperationData<'src>,
|
||||||
name,
|
|
||||||
match field_ty.to_idl_type(self) {
|
|
||||||
None => return Vec::new(),
|
|
||||||
Some(idl_type) => idl_type,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)],
|
|
||||||
IdlType::Void,
|
|
||||||
kind,
|
|
||||||
is_structural,
|
|
||||||
catch,
|
|
||||||
doc_comment,
|
|
||||||
)
|
)
|
||||||
|
-> Vec<backend::ast::ImportFunction>
|
||||||
|
{
|
||||||
|
// First up, prune all signatures that reference unsupported arguments.
|
||||||
|
// We won't consider these until said arguments are implemented.
|
||||||
|
let mut signatures = Vec::new();
|
||||||
|
'outer:
|
||||||
|
for signature in data.signatures.iter() {
|
||||||
|
let mut idl_args = Vec::with_capacity(signature.args.len());
|
||||||
|
for arg in signature.args.iter() {
|
||||||
|
match arg.ty.to_idl_type(self) {
|
||||||
|
Some(t) => idl_args.push((t, arg)),
|
||||||
|
None => continue 'outer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signatures.push((signature, idl_args));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next expand all the signatures in `data` into all signatures that
|
||||||
|
// we're going to generate. These signatures will be used to determine
|
||||||
|
// the names for all the various functions.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct ExpandedSig<'a> {
|
||||||
|
orig: &'a Signature<'a>,
|
||||||
|
args: Vec<IdlType<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut actual_signatures = Vec::new();
|
||||||
|
for (signature, idl_args) in signatures.iter() {
|
||||||
|
let mut start = actual_signatures.len();
|
||||||
|
|
||||||
|
// Start off with an empty signature, this'll handle zero-argument
|
||||||
|
// cases and otherwise the loop below will continue to add on to this.
|
||||||
|
actual_signatures.push(ExpandedSig {
|
||||||
|
orig: signature,
|
||||||
|
args: Vec::with_capacity(signature.args.len()),
|
||||||
|
});
|
||||||
|
|
||||||
|
for (i, (idl_type, arg)) in idl_args.iter().enumerate() {
|
||||||
|
// If this is an optional argument, then all remaining arguments
|
||||||
|
// should also be optional (if any). This means that all the
|
||||||
|
// signatures we've built up so far are valid signatures because
|
||||||
|
// we're just going to omit all the future arguments. As a
|
||||||
|
// result we duplicate all the previous signatures we've made in
|
||||||
|
// the list. The duplicates will be modified in-place below.
|
||||||
|
if arg.optional {
|
||||||
|
assert!(signature.args[i..].iter().all(|a| a.optional));
|
||||||
|
let end = actual_signatures.len();
|
||||||
|
for j in start..end {
|
||||||
|
let sig = actual_signatures[j].clone();
|
||||||
|
actual_signatures.push(sig);
|
||||||
|
}
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// small sanity check
|
||||||
|
assert!(start < actual_signatures.len());
|
||||||
|
for sig in actual_signatures[start..].iter() {
|
||||||
|
assert_eq!(sig.args.len(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first element of the flattened type gets pushed directly
|
||||||
|
// in-place, but all other flattened types will cause new
|
||||||
|
// signatures to be created.
|
||||||
|
let cur = actual_signatures.len();
|
||||||
|
for (j, idl_type) in idl_type.flatten().into_iter().enumerate() {
|
||||||
|
for k in start..cur {
|
||||||
|
if j == 0 {
|
||||||
|
actual_signatures[k].args.push(idl_type.clone());
|
||||||
|
} else {
|
||||||
|
let mut sig = actual_signatures[k].clone();
|
||||||
|
assert_eq!(sig.args.len(), i + 1);
|
||||||
|
sig.args.truncate(i);
|
||||||
|
sig.args.push(idl_type.clone());
|
||||||
|
actual_signatures.push(sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (name, force_structural, force_throws) = match id {
|
||||||
|
// Constructors aren't annotated with `[Throws]` extended attributes
|
||||||
|
// (how could they be, since they themselves are extended
|
||||||
|
// attributes?) so we must conservatively assume that they can
|
||||||
|
// always throw.
|
||||||
|
//
|
||||||
|
// From https://heycam.github.io/webidl/#Constructor (emphasis
|
||||||
|
// mine):
|
||||||
|
//
|
||||||
|
// > The prose definition of a constructor must either return an IDL
|
||||||
|
// > value of a type corresponding to the interface the
|
||||||
|
// > `[Constructor]` extended attribute appears on, **or throw an
|
||||||
|
// > exception**.
|
||||||
|
OperationId::Constructor(_) => ("new", false, true),
|
||||||
|
OperationId::Operation(Some(s)) => (*s, false, false),
|
||||||
|
OperationId::Operation(None) => {
|
||||||
|
warn!("unsupported unnamed operation");
|
||||||
|
return Vec::new()
|
||||||
|
}
|
||||||
|
OperationId::IndexingGetter => ("get", true, false),
|
||||||
|
OperationId::IndexingSetter => ("set", true, false),
|
||||||
|
OperationId::IndexingDeleter => ("delete", true, false),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
for signature in actual_signatures.iter() {
|
||||||
|
// Ignore signatures with invalid return types
|
||||||
|
//
|
||||||
|
// TODO: overloads probably never change return types, so we should
|
||||||
|
// do this much earlier to avoid all the above work if
|
||||||
|
// possible.
|
||||||
|
let ret_ty = match signature.orig.ret.to_idl_type(self) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rust_name = name.to_snake_case();
|
||||||
|
let mut first = true;
|
||||||
|
for (i, arg) in signature.args.iter().enumerate() {
|
||||||
|
// Find out if any other known signature either has the same
|
||||||
|
// name for this argument or a different type for this argument.
|
||||||
|
let mut any_same_name = false;
|
||||||
|
let mut any_different_type = false;
|
||||||
|
let mut any_different = false;
|
||||||
|
let arg_name = signature.orig.args[i].name;
|
||||||
|
for other in actual_signatures.iter() {
|
||||||
|
if other.orig.args.get(i).map(|s| s.name) == Some(arg_name) {
|
||||||
|
if !ptr::eq(signature, other) {
|
||||||
|
any_same_name = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(other) = other.args.get(i) {
|
||||||
|
if other != arg {
|
||||||
|
any_different_type = true;
|
||||||
|
any_different = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
any_different = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all signatures have the exact same type for this argument,
|
||||||
|
// then there's nothing to disambiguate so we don't modify the
|
||||||
|
// name.
|
||||||
|
if !any_different {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if first {
|
||||||
|
rust_name.push_str("_with_");
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
rust_name.push_str("_and_");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this name of the argument for this signature is unique
|
||||||
|
// then that's a bit more human readable so we include it in the
|
||||||
|
// method name. Otherwise the type name should disambiguate
|
||||||
|
// correctly.
|
||||||
|
//
|
||||||
|
// If any signature's argument has the same name as our argument
|
||||||
|
// then we can't use that if the types are also the same because
|
||||||
|
// otherwise it could be ambiguous.
|
||||||
|
if any_same_name && any_different_type {
|
||||||
|
arg.push_type_name(&mut rust_name);
|
||||||
|
} else {
|
||||||
|
rust_name.push_str(&arg_name.to_snake_case());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret.extend(self.create_one_function(
|
||||||
|
name,
|
||||||
|
&rust_name,
|
||||||
|
signature.args.iter()
|
||||||
|
.zip(&signature.orig.args)
|
||||||
|
.map(|(ty, orig_arg)| (orig_arg.name, ty)),
|
||||||
|
&ret_ty,
|
||||||
|
kind.clone(),
|
||||||
|
force_structural || is_structural(&signature.orig.attrs),
|
||||||
|
force_throws || throws(&signature.orig.attrs),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,4 +8,3 @@ crate-type = ["cdylib"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasm-bindgen = { path = "../.." }
|
wasm-bindgen = { path = "../.." }
|
||||||
web-sys = { path = "../../crates/web-sys" }
|
|
@ -1,11 +1,7 @@
|
|||||||
extern crate wasm_bindgen;
|
extern crate wasm_bindgen;
|
||||||
extern crate web_sys;
|
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
// You can use the console bindings from web-sys...
|
|
||||||
use web_sys::console;
|
|
||||||
|
|
||||||
// ... or you can manually write the bindings yourself
|
// ... or you can manually write the bindings yourself
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -22,7 +18,4 @@ pub fn run() {
|
|||||||
log("Hello from Rust!");
|
log("Hello from Rust!");
|
||||||
log_u32(42);
|
log_u32(42);
|
||||||
log_many("Logging", "many values!");
|
log_many("Logging", "many values!");
|
||||||
|
|
||||||
console::log(JsValue::from("Another message from rust!"));
|
|
||||||
console::log(JsValue::from(56u32));
|
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ pub fn run() -> Promise {
|
|||||||
request_options.method("GET");
|
request_options.method("GET");
|
||||||
request_options.mode(RequestMode::Cors);
|
request_options.mode(RequestMode::Cors);
|
||||||
|
|
||||||
let req = Request::new_with_str_and_request_init("https://api.github.com/repos/rustwasm/wasm-bindgen/branches/master", &request_options).unwrap();
|
let req = Request::new_with_str_and_init("https://api.github.com/repos/rustwasm/wasm-bindgen/branches/master", &request_options).unwrap();
|
||||||
|
|
||||||
// the RequestInit struct will eventually support setting headers, but that's missing right now
|
// the RequestInit struct will eventually support setting headers, but that's missing right now
|
||||||
req.headers().set("Accept", "application/vnd.github.v3+json").unwrap();
|
req.headers().set("Accept", "application/vnd.github.v3+json").unwrap();
|
||||||
|
@ -8,3 +8,6 @@ cargo +nightly build --target wasm32-unknown-unknown
|
|||||||
cargo +nightly run --manifest-path ../../crates/cli/Cargo.toml \
|
cargo +nightly run --manifest-path ../../crates/cli/Cargo.toml \
|
||||||
--bin wasm-bindgen -- \
|
--bin wasm-bindgen -- \
|
||||||
../../target/wasm32-unknown-unknown/debug/webaudio.wasm --out-dir .
|
../../target/wasm32-unknown-unknown/debug/webaudio.wasm --out-dir .
|
||||||
|
|
||||||
|
npm install
|
||||||
|
npm run serve
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "webpack-serve ./webpack.config.js"
|
"serve": "webpack-dev-server"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"webpack": "^4.16.5",
|
"webpack": "^4.16.5",
|
||||||
|
@ -81,16 +81,16 @@ impl FmOsc {
|
|||||||
// connect them up:
|
// connect them up:
|
||||||
|
|
||||||
// The primary oscillator is routed through the gain node, so that it can control the overall output volume
|
// The primary oscillator is routed through the gain node, so that it can control the overall output volume
|
||||||
primary_node.connect_with_destination_and_output_and_input(gain.as_ref()).unwrap();
|
primary_node.connect_with_audio_node(gain.as_ref()).unwrap();
|
||||||
// Then connect the gain node to the AudioContext destination (aka your speakers)
|
// Then connect the gain node to the AudioContext destination (aka your speakers)
|
||||||
gain_node.connect_with_destination_and_output_and_input(destination_node).unwrap();
|
gain_node.connect_with_audio_node(destination_node).unwrap();
|
||||||
|
|
||||||
// the FM oscillator is connected to its own gain node, so it can control the amount of modulation
|
// the FM oscillator is connected to its own gain node, so it can control the amount of modulation
|
||||||
fm_osc_node.connect_with_destination_and_output_and_input(fm_gain.as_ref()).unwrap();
|
fm_osc_node.connect_with_audio_node(fm_gain.as_ref()).unwrap();
|
||||||
|
|
||||||
// Connect the FM oscillator to the frequency parameter of the main oscillator, so that the
|
// Connect the FM oscillator to the frequency parameter of the main oscillator, so that the
|
||||||
// FM node can modulate its frequency
|
// FM node can modulate its frequency
|
||||||
fm_gain_node.connect_with_destination_and_output(&primary.frequency()).unwrap();
|
fm_gain_node.connect_with_audio_param(&primary.frequency()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user