diff --git a/crates/webidl-tests/simple.rs b/crates/webidl-tests/simple.rs index 23484af1..e5e4d230 100644 --- a/crates/webidl-tests/simple.rs +++ b/crates/webidl-tests/simple.rs @@ -82,12 +82,12 @@ fn optional_and_union_arguments() { let f = OptionalAndUnionArguments::new().unwrap(); 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_bool_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_bool_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_bool_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_i16("abc", false, 5), "string, abc, boolean, false, number, 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_b_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_bool("abc", false, 5, Some(true)), "string, abc, boolean, false, number, 5, boolean, true"); + 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_b_and_str_and_opt_bool("abc", false, "5", Some(true)), "string, abc, boolean, false, string, 5, boolean, true"); } #[wasm_bindgen_test] @@ -118,7 +118,9 @@ fn mixin() { #[wasm_bindgen_test] fn overload_naming() { let o = Overloads::new().unwrap(); - // o.foo(); - // o.foo_with_arg("x"); - // o.foo_with_arg_and_a("x", 3); + 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); } diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 0fb2ae81..c4ebc7fa 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -97,6 +97,7 @@ pub(crate) struct OperationData<'src> { #[derive(Default)] pub(crate) struct OperationData2<'src> { pub(crate) signatures: Vec>, + pub(crate) is_static: bool, } #[derive(Clone)] @@ -231,6 +232,7 @@ fn first_pass_operation<'src>( arguments: &'src [Argument<'src>], ret: &weedle::types::ReturnType<'src>, attrs: &'src Option>, + is_static: bool, ) -> bool { if util::is_chrome_only(attrs) { return false @@ -292,14 +294,13 @@ fn first_pass_operation<'src>( .entry(names.clone()) .and_modify(|same_argument_names| *same_argument_names = true) .or_insert(false); - operations2.entry(*id) - .or_default() - .signatures - .push(Signature { - args: args.clone(), - ret: ret.clone(), - attrs, - }); + let op2 = operations2.entry(*id).or_default(); + op2.is_static = is_static; + op2.signatures.push(Signature { + args: args.clone(), + ret: ret.clone(), + attrs, + }); } true } @@ -366,6 +367,7 @@ fn process_interface_attribute<'src>( &list.args.body.list, &return_ty, &None, + false, ) { record.interfaces .get_mut(self_name) @@ -383,6 +385,7 @@ fn process_interface_attribute<'src>( &[], &return_ty, &None, + false, ) { record.interfaces .get_mut(self_name) @@ -402,6 +405,7 @@ fn process_interface_attribute<'src>( &list.args.body.list, &return_ty, &None, + false, ) { record.interfaces .get_mut(self_name) @@ -488,10 +492,14 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM warn!("Unsupported webidl operation: {:?}", self); return Ok(()) } - if let Some(StringifierOrStatic::Stringifier(_)) = self.modifier { - warn!("Unsupported webidl stringifier: {:?}", self); - return Ok(()) - } + let is_static = match self.modifier { + Some(StringifierOrStatic::Stringifier(_)) => { + 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))]; 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.return_type, &self.attributes, + is_static, ) { record.interfaces .get_mut(self_name) @@ -621,6 +630,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'s &self.args.body.list, &self.return_type, &self.attributes, + false, ) { record.mixins .get_mut(self_name) @@ -719,6 +729,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceM &self.args.body.list, &self.return_type, &self.attributes, + true, ) { record.namespaces.get_mut(self_name).unwrap().members.push(self); } diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 1b828210..438ffb74 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -47,7 +47,8 @@ use weedle::argument::Argument; use weedle::attribute::{ExtendedAttributeList}; 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 idl_type::{IdlType, ToIdlType}; @@ -309,7 +310,7 @@ impl<'src> FirstPassRecord<'src> { module: &mut backend::ast::Module, self_name: &'src str, id: &OperationId<'src>, - data: &first_pass::OperationData2<'src>, + data: &OperationData2<'src>, ) { let name = match id { OperationId::Operation(Some(name)) => name, @@ -379,7 +380,7 @@ impl<'src> FirstPassRecord<'src> { &self, program: &mut backend::ast::Program, name: &'src str, - data: &first_pass::InterfaceData<'src>, + data: &InterfaceData<'src>, ) { let doc_comment = Some(format!( "The `{}` object\n\n{}", @@ -406,17 +407,11 @@ impl<'src> FirstPassRecord<'src> { for (ctor_name, args) in data.constructors.iter() { self.append_constructor(program, name, ctor_name, args); } - for member in data.methods.iter() { - self.member_operation( - program, - name, - &member.attributes, - member.modifier, - &member.specials, - &member.return_type, - &member.args.body.list, - &member.identifier, - ) + for (id, op_data) in data.operations2.iter() { + if let OperationId::Constructor = id { + continue // TODO + } + self.member_operation2(program, name, data, id, op_data); } for member in data.consts.iter() { self.append_const(program, name, member); @@ -433,23 +428,14 @@ impl<'src> FirstPassRecord<'src> { ); } - for data in self.all_mixins(name) { - for member in &data.methods { - self.member_operation( - program, - name, - &member.attributes, - None, - &[], - &member.return_type, - &member.args.body.list, - &member.identifier, - ); + for mixin_data in self.all_mixins(name) { + for (id, op_data) in mixin_data.operations2.iter() { + self.member_operation2(program, name, data, id, op_data); } - for member in &data.consts { + for member in &mixin_data.consts { self.append_const(program, name, member); } - for member in &data.attributes { + for member in &mixin_data.attributes { self.member_attribute( program, name, @@ -577,69 +563,40 @@ impl<'src> FirstPassRecord<'src> { } } - fn member_operation( + fn member_operation2( &self, program: &mut backend::ast::Program, - self_name: &'src str, - attrs: &'src Option, - modifier: Option, - specials: &[weedle::interface::Special], - return_type: &'src weedle::types::ReturnType<'src>, - args: &'src [Argument], - identifier: &Option>, + self_name: &str, + data: &InterfaceData<'src>, + id: &OperationId<'src>, + op_data: &OperationData2<'src>, ) { - use weedle::interface::StringifierOrStatic::*; - use weedle::interface::Special; - - let is_static = match modifier { - Some(Stringifier(_)) => unimplemented!(), // filtered out earlier - Some(Static(_)) => true, - None => false, + let operation_kind = match id { + OperationId::Constructor => panic!("constructors are unsupported"), + OperationId::Operation(_) => backend::ast::OperationKind::Regular, + OperationId::IndexingGetter => backend::ast::OperationKind::IndexingGetter, + OperationId::IndexingSetter => backend::ast::OperationKind::IndexingSetter, + OperationId::IndexingDeleter => backend::ast::OperationKind::IndexingDeleter, }; - - let mut operation_ids = vec![ - OperationId::Operation(identifier.map(|s| s.0)), - ]; - if specials.len() == 1 { - let id = match specials[0] { - Special::Getter(weedle::term::Getter) => OperationId::IndexingGetter, - Special::Setter(weedle::term::Setter) => OperationId::IndexingSetter, - Special::Deleter(weedle::term::Deleter) => OperationId::IndexingDeleter, - 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)); + let operation = backend::ast::Operation { + is_static: op_data.is_static, + kind: operation_kind, + }; + let ty = ident_ty(rust_ident(camel_case_ident(&self_name).as_str())); + let kind = if data.global { + backend::ast::ImportFunctionKind::ScopedMethod { + ty, + operation, } + } 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)); } } } diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 64b28563..9ff2582b 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -407,97 +407,6 @@ impl<'src> FirstPassRecord<'src> { 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 { - 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 there overloads with same argument names for given argument types pub fn get_operation_overloading( @@ -659,7 +568,8 @@ impl<'src> FirstPassRecord<'src> { let mut actual_signatures = Vec::new(); 'outer: 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 // 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() { - let cur = actual_signatures.len(); // If any argument in this signature can't be converted we have // to throw out the entire signature, so revert back to the // beginning and then keep going. let idl_type = match arg.ty.to_idl_type(self) { Some(t) => t, None => { - actual_signatures.truncate(start); + actual_signatures.truncate(real_start); 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 // future expanded arguments will cause new signatures to be // created. If we have an optional argument then we consider the // already existing signature as the "none" case and the flatten // below will produce the "some" case, so we've already // 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 j in start..cur { if first { actual_signatures[j].args.push(idl_type.clone()); - first = false; } else { let mut sig = actual_signatures[j].clone(); + assert_eq!(sig.args.len(), i + 1); sig.args.truncate(i); sig.args.push(idl_type.clone()); actual_signatures.push(sig); } } + first = false; } } } - let name = match id { - OperationId::Constructor => "new", - OperationId::Operation(Some(s)) => s, + let (name, force_structural) = match id { + OperationId::Constructor => ("new", false), + OperationId::Operation(Some(s)) => (*s, false), OperationId::Operation(None) => { warn!("unsupported unnamed operation"); return Vec::new() } - OperationId::IndexingGetter => "get", - OperationId::IndexingSetter => "set", - OperationId::IndexingDeleter => "delete", + OperationId::IndexingGetter => ("get", true), + OperationId::IndexingSetter => ("set", true), + OperationId::IndexingDeleter => ("delete", true), }; let mut ret = Vec::new(); @@ -731,6 +657,7 @@ impl<'src> FirstPassRecord<'src> { // 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) { @@ -738,15 +665,20 @@ impl<'src> FirstPassRecord<'src> { any_same_name = true; } } - if other.args.get(i) != Some(arg) { - any_different_type = 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_type { + if !any_different { continue } if first { @@ -760,7 +692,7 @@ impl<'src> FirstPassRecord<'src> { // 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_same_name { + if !any_same_name || !any_different_type { rust_name.push_str(&arg_name.to_snake_case()); } else { arg.push_type_name(&mut rust_name); @@ -774,7 +706,7 @@ impl<'src> FirstPassRecord<'src> { .map(|(ty, orig_arg)| (orig_arg.name, ty)), &ret_ty, kind.clone(), - is_structural(&signature.orig.attrs), + force_structural || is_structural(&signature.orig.attrs), throws(&signature.orig.attrs), None, ));