Migrate methods to new naming scheme

Allows deletion of `create_basic_method`!
This commit is contained in:
Alex Crichton 2018-08-29 17:33:35 -07:00
parent 15d4338abe
commit 5a4a34d4a1
4 changed files with 116 additions and 214 deletions

View File

@ -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]
@ -118,7 +118,9 @@ fn mixin() {
#[wasm_bindgen_test] #[wasm_bindgen_test]
fn overload_naming() { fn overload_naming() {
let o = Overloads::new().unwrap(); let o = Overloads::new().unwrap();
// o.foo(); o.foo();
// o.foo_with_arg("x"); o.foo_with_arg("x");
// o.foo_with_arg_and_a("x", 3); 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);
} }

View File

@ -97,6 +97,7 @@ pub(crate) struct OperationData<'src> {
#[derive(Default)] #[derive(Default)]
pub(crate) struct OperationData2<'src> { pub(crate) struct OperationData2<'src> {
pub(crate) signatures: Vec<Signature<'src>>, pub(crate) signatures: Vec<Signature<'src>>,
pub(crate) is_static: bool,
} }
#[derive(Clone)] #[derive(Clone)]
@ -231,6 +232,7 @@ fn first_pass_operation<'src>(
arguments: &'src [Argument<'src>], arguments: &'src [Argument<'src>],
ret: &weedle::types::ReturnType<'src>, ret: &weedle::types::ReturnType<'src>,
attrs: &'src Option<ExtendedAttributeList<'src>>, attrs: &'src Option<ExtendedAttributeList<'src>>,
is_static: bool,
) -> bool { ) -> bool {
if util::is_chrome_only(attrs) { if util::is_chrome_only(attrs) {
return false return false
@ -292,14 +294,13 @@ fn first_pass_operation<'src>(
.entry(names.clone()) .entry(names.clone())
.and_modify(|same_argument_names| *same_argument_names = true) .and_modify(|same_argument_names| *same_argument_names = true)
.or_insert(false); .or_insert(false);
operations2.entry(*id) let op2 = operations2.entry(*id).or_default();
.or_default() op2.is_static = is_static;
.signatures op2.signatures.push(Signature {
.push(Signature { args: args.clone(),
args: args.clone(), ret: ret.clone(),
ret: ret.clone(), attrs,
attrs, });
});
} }
true true
} }
@ -366,6 +367,7 @@ fn process_interface_attribute<'src>(
&list.args.body.list, &list.args.body.list,
&return_ty, &return_ty,
&None, &None,
false,
) { ) {
record.interfaces record.interfaces
.get_mut(self_name) .get_mut(self_name)
@ -383,6 +385,7 @@ fn process_interface_attribute<'src>(
&[], &[],
&return_ty, &return_ty,
&None, &None,
false,
) { ) {
record.interfaces record.interfaces
.get_mut(self_name) .get_mut(self_name)
@ -402,6 +405,7 @@ fn process_interface_attribute<'src>(
&list.args.body.list, &list.args.body.list,
&return_ty, &return_ty,
&None, &None,
false,
) { ) {
record.interfaces record.interfaces
.get_mut(self_name) .get_mut(self_name)
@ -488,10 +492,14 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM
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 {
warn!("Unsupported webidl stringifier: {:?}", self); Some(StringifierOrStatic::Stringifier(_)) => {
return Ok(()) warn!("Unsupported webidl stringifier: {:?}", self);
} 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() {
@ -510,6 +518,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM
&self.args.body.list, &self.args.body.list,
&self.return_type, &self.return_type,
&self.attributes, &self.attributes,
is_static,
) { ) {
record.interfaces record.interfaces
.get_mut(self_name) .get_mut(self_name)
@ -621,6 +630,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'s
&self.args.body.list, &self.args.body.list,
&self.return_type, &self.return_type,
&self.attributes, &self.attributes,
false,
) { ) {
record.mixins record.mixins
.get_mut(self_name) .get_mut(self_name)
@ -719,6 +729,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceM
&self.args.body.list, &self.args.body.list,
&self.return_type, &self.return_type,
&self.attributes, &self.attributes,
true,
) { ) {
record.namespaces.get_mut(self_name).unwrap().members.push(self); record.namespaces.get_mut(self_name).unwrap().members.push(self);
} }

View File

@ -47,7 +47,8 @@ use weedle::argument::Argument;
use weedle::attribute::{ExtendedAttributeList}; use weedle::attribute::{ExtendedAttributeList};
use weedle::dictionary::DictionaryMember; use weedle::dictionary::DictionaryMember;
use first_pass::{FirstPass, FirstPassRecord, OperationId}; use first_pass::{FirstPass, FirstPassRecord, OperationId, InterfaceData};
use first_pass::OperationData2;
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};
@ -309,7 +310,7 @@ impl<'src> FirstPassRecord<'src> {
module: &mut backend::ast::Module, module: &mut backend::ast::Module,
self_name: &'src str, self_name: &'src str,
id: &OperationId<'src>, id: &OperationId<'src>,
data: &first_pass::OperationData2<'src>, data: &OperationData2<'src>,
) { ) {
let name = match id { let name = match id {
OperationId::Operation(Some(name)) => name, OperationId::Operation(Some(name)) => name,
@ -379,7 +380,7 @@ impl<'src> FirstPassRecord<'src> {
&self, &self,
program: &mut backend::ast::Program, program: &mut backend::ast::Program,
name: &'src str, name: &'src str,
data: &first_pass::InterfaceData<'src>, data: &InterfaceData<'src>,
) { ) {
let doc_comment = Some(format!( let doc_comment = Some(format!(
"The `{}` object\n\n{}", "The `{}` object\n\n{}",
@ -406,17 +407,11 @@ impl<'src> FirstPassRecord<'src> {
for (ctor_name, args) in data.constructors.iter() { for (ctor_name, args) in data.constructors.iter() {
self.append_constructor(program, name, ctor_name, args); self.append_constructor(program, name, ctor_name, args);
} }
for member in data.methods.iter() { for (id, op_data) in data.operations2.iter() {
self.member_operation( if let OperationId::Constructor = id {
program, continue // TODO
name, }
&member.attributes, self.member_operation2(program, name, data, id, op_data);
member.modifier,
&member.specials,
&member.return_type,
&member.args.body.list,
&member.identifier,
)
} }
for member in data.consts.iter() { for member in data.consts.iter() {
self.append_const(program, name, member); self.append_const(program, name, member);
@ -433,23 +428,14 @@ impl<'src> FirstPassRecord<'src> {
); );
} }
for data in self.all_mixins(name) { for mixin_data in self.all_mixins(name) {
for member in &data.methods { for (id, op_data) in mixin_data.operations2.iter() {
self.member_operation( self.member_operation2(program, name, data, id, op_data);
program,
name,
&member.attributes,
None,
&[],
&member.return_type,
&member.args.body.list,
&member.identifier,
);
} }
for member in &data.consts { for member in &mixin_data.consts {
self.append_const(program, name, member); self.append_const(program, name, member);
} }
for member in &data.attributes { for member in &mixin_data.attributes {
self.member_attribute( self.member_attribute(
program, program,
name, name,
@ -577,69 +563,40 @@ impl<'src> FirstPassRecord<'src> {
} }
} }
fn member_operation( fn member_operation2(
&self, &self,
program: &mut backend::ast::Program, program: &mut backend::ast::Program,
self_name: &'src str, self_name: &str,
attrs: &'src Option<ExtendedAttributeList>, data: &InterfaceData<'src>,
modifier: Option<weedle::interface::StringifierOrStatic>, id: &OperationId<'src>,
specials: &[weedle::interface::Special], op_data: &OperationData2<'src>,
return_type: &'src weedle::types::ReturnType<'src>,
args: &'src [Argument],
identifier: &Option<weedle::common::Identifier<'src>>,
) { ) {
use weedle::interface::StringifierOrStatic::*; let operation_kind = match id {
use weedle::interface::Special; OperationId::Constructor => panic!("constructors are unsupported"),
OperationId::Operation(_) => backend::ast::OperationKind::Regular,
let is_static = match modifier { OperationId::IndexingGetter => backend::ast::OperationKind::IndexingGetter,
Some(Stringifier(_)) => unimplemented!(), // filtered out earlier OperationId::IndexingSetter => backend::ast::OperationKind::IndexingSetter,
Some(Static(_)) => true, OperationId::IndexingDeleter => backend::ast::OperationKind::IndexingDeleter,
None => false,
}; };
let operation = backend::ast::Operation {
let mut operation_ids = vec![ is_static: op_data.is_static,
OperationId::Operation(identifier.map(|s| s.0)), kind: operation_kind,
]; };
if specials.len() == 1 { let ty = ident_ty(rust_ident(camel_case_ident(&self_name).as_str()));
let id = match specials[0] { let kind = if data.global {
Special::Getter(weedle::term::Getter) => OperationId::IndexingGetter, backend::ast::ImportFunctionKind::ScopedMethod {
Special::Setter(weedle::term::Setter) => OperationId::IndexingSetter, ty,
Special::Deleter(weedle::term::Deleter) => OperationId::IndexingDeleter, operation,
Special::LegacyCaller(weedle::term::LegacyCaller) => {
warn!("Unsupported legacy caller: {:?}", (self_name, identifier));
return
},
};
operation_ids.push(id);
}
let global = self
.interfaces
.get(self_name)
.map(|interface_data| interface_data.global)
.unwrap_or(false);
for id in operation_ids {
let methods = self
.create_basic_method(
args,
id,
return_type,
self_name,
is_static,
match id {
OperationId::IndexingGetter |
OperationId::IndexingSetter |
OperationId::IndexingDeleter => true,
_ => false,
},
util::throws(attrs),
global,
);
for method in methods {
program.imports.push(wrap_import_function(method));
} }
} else {
backend::ast::ImportFunctionKind::Method {
class: self_name.to_string(),
ty,
kind: backend::ast::MethodKind::Operation(operation),
}
};
for method in self.create_imports(kind, id, op_data) {
program.imports.push(wrap_import_function(method));
} }
} }
} }

View File

@ -407,97 +407,6 @@ impl<'src> FirstPassRecord<'src> {
Some(converted_arguments) Some(converted_arguments)
} }
/// Create a wasm-bindgen method, if possible.
pub fn create_basic_method(
&self,
arguments: &[Argument<'src>],
operation_id: first_pass::OperationId,
return_type: &weedle::types::ReturnType<'src>,
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,
structural,
catch,
doc_comment,
)
}
/// Whether operation is overloaded and /// Whether operation is overloaded and
/// 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(
@ -659,7 +568,8 @@ impl<'src> FirstPassRecord<'src> {
let mut actual_signatures = Vec::new(); let mut actual_signatures = Vec::new();
'outer: 'outer:
for signature in data.signatures.iter() { for signature in data.signatures.iter() {
let start = actual_signatures.len(); let real_start = actual_signatures.len();
let mut start = real_start;
// Start off with an empty signature, this'll handle zero-argument // Start off with an empty signature, this'll handle zero-argument
// cases and otherwise the loop below will continue to add on to this. // cases and otherwise the loop below will continue to add on to this.
@ -669,51 +579,67 @@ impl<'src> FirstPassRecord<'src> {
}); });
for (i, arg) in signature.args.iter().enumerate() { for (i, arg) in signature.args.iter().enumerate() {
let cur = actual_signatures.len();
// If any argument in this signature can't be converted we have // If any argument in this signature can't be converted we have
// to throw out the entire signature, so revert back to the // to throw out the entire signature, so revert back to the
// beginning and then keep going. // beginning and then keep going.
let idl_type = match arg.ty.to_idl_type(self) { let idl_type = match arg.ty.to_idl_type(self) {
Some(t) => t, Some(t) => t,
None => { None => {
actual_signatures.truncate(start); actual_signatures.truncate(real_start);
continue 'outer continue 'outer
} }
}; };
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;
}
assert!(start < actual_signatures.len());
for sig in actual_signatures[start..].iter() {
assert_eq!(sig.args.len(), i);
}
// The first arugment gets pushed directly in-place, but all // The first arugment gets pushed directly in-place, but all
// future expanded arguments will cause new signatures to be // future expanded arguments will cause new signatures to be
// created. If we have an optional argument then we consider the // created. If we have an optional argument then we consider the
// already existing signature as the "none" case and the flatten // already existing signature as the "none" case and the flatten
// below will produce the "some" case, so we've already // below will produce the "some" case, so we've already
// processed the first argument effectively. // processed the first argument effectively.
let mut first = !arg.optional; let mut first = true;
let cur = actual_signatures.len();
for idl_type in idl_type.flatten() { for idl_type in idl_type.flatten() {
for j in start..cur { for j in start..cur {
if first { if first {
actual_signatures[j].args.push(idl_type.clone()); actual_signatures[j].args.push(idl_type.clone());
first = false;
} else { } else {
let mut sig = actual_signatures[j].clone(); let mut sig = actual_signatures[j].clone();
assert_eq!(sig.args.len(), i + 1);
sig.args.truncate(i); sig.args.truncate(i);
sig.args.push(idl_type.clone()); sig.args.push(idl_type.clone());
actual_signatures.push(sig); actual_signatures.push(sig);
} }
} }
first = false;
} }
} }
} }
let name = match id { let (name, force_structural) = match id {
OperationId::Constructor => "new", OperationId::Constructor => ("new", false),
OperationId::Operation(Some(s)) => s, OperationId::Operation(Some(s)) => (*s, false),
OperationId::Operation(None) => { OperationId::Operation(None) => {
warn!("unsupported unnamed operation"); warn!("unsupported unnamed operation");
return Vec::new() return Vec::new()
} }
OperationId::IndexingGetter => "get", OperationId::IndexingGetter => ("get", true),
OperationId::IndexingSetter => "set", OperationId::IndexingSetter => ("set", true),
OperationId::IndexingDeleter => "delete", OperationId::IndexingDeleter => ("delete", true),
}; };
let mut ret = Vec::new(); let mut ret = Vec::new();
@ -731,6 +657,7 @@ impl<'src> FirstPassRecord<'src> {
// name for this argument or a different type for this argument. // name for this argument or a different type for this argument.
let mut any_same_name = false; let mut any_same_name = false;
let mut any_different_type = false; let mut any_different_type = false;
let mut any_different = false;
let arg_name = signature.orig.args[i].name; let arg_name = signature.orig.args[i].name;
for other in actual_signatures.iter() { for other in actual_signatures.iter() {
if other.orig.args.get(i).map(|s| s.name) == Some(arg_name) { if other.orig.args.get(i).map(|s| s.name) == Some(arg_name) {
@ -738,15 +665,20 @@ impl<'src> FirstPassRecord<'src> {
any_same_name = true; any_same_name = true;
} }
} }
if other.args.get(i) != Some(arg) { if let Some(other) = other.args.get(i) {
any_different_type = true; 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, // If all signatures have the exact same type for this argument,
// then there's nothing to disambiguate so we don't modify the // then there's nothing to disambiguate so we don't modify the
// name. // name.
if !any_different_type { if !any_different {
continue continue
} }
if first { if first {
@ -760,7 +692,7 @@ impl<'src> FirstPassRecord<'src> {
// then that's a bit more human readable so we include it in the // then that's a bit more human readable so we include it in the
// method name. Otherwise the type name should disambiguate // method name. Otherwise the type name should disambiguate
// correctly. // correctly.
if !any_same_name { if !any_same_name || !any_different_type {
rust_name.push_str(&arg_name.to_snake_case()); rust_name.push_str(&arg_name.to_snake_case());
} else { } else {
arg.push_type_name(&mut rust_name); arg.push_type_name(&mut rust_name);
@ -774,7 +706,7 @@ impl<'src> FirstPassRecord<'src> {
.map(|(ty, orig_arg)| (orig_arg.name, ty)), .map(|(ty, orig_arg)| (orig_arg.name, ty)),
&ret_ty, &ret_ty,
kind.clone(), kind.clone(),
is_structural(&signature.orig.attrs), force_structural || is_structural(&signature.orig.attrs),
throws(&signature.orig.attrs), throws(&signature.orig.attrs),
None, None,
)); ));