Refactor creation of functions in the backend

This commit refactors the lowest-level primitive for creating functions into a
new `create_one_function` function. This doesn't take into account overloading
but is suitable for things like `create_{getter,setter}`. Eventually the
overloading will be implemented in terms of this function.
This commit is contained in:
Alex Crichton 2018-08-29 10:28:04 -07:00
parent 0a38e44f1f
commit 4f76a00024
3 changed files with 147 additions and 132 deletions

View File

@ -118,7 +118,7 @@ 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_a("x", 3);
} }

View File

@ -577,7 +577,7 @@ impl<'src> FirstPassRecord<'src> {
use weedle::interface::Special; use weedle::interface::Special;
let is_static = match modifier { let is_static = match modifier {
Some(Stringifier(_)) => uniimplemented!(), // filtered out earlier Some(Stringifier(_)) => unimplemented!(), // filtered out earlier
Some(Static(_)) => true, Some(Static(_)) => true,
None => false, None => false,
}; };

View File

@ -230,31 +230,6 @@ impl<'src> FirstPassRecord<'src> {
name.to_snake_case() 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 let converted_arguments = arguments
.iter() .iter()
.cloned() .cloned()
@ -294,65 +269,116 @@ impl<'src> FirstPassRecord<'src> {
} else { } else {
rust_name.clone() rust_name.clone()
}; };
let rust_name = rust_ident(&rust_name); let f = self.create_one_function(
let shim = { name,
let ns = match kind { &rust_name,
backend::ast::ImportFunctionKind::ScopedMethod { .. } | arguments.iter().map(|s| s.0).zip(idl_types),
backend::ast::ImportFunctionKind::Normal => "", &ret,
backend::ast::ImportFunctionKind::Method { ref class, .. } => class, kind.clone(),
}; structural,
catch,
doc_comment.clone(),
);
import_functions.extend(f);
}
import_functions
}
raw_ident(&format!("__widl_f_{}_{}", rust_name, ns)) pub fn create_one_function<'a>(
}; &self,
js_name: &str,
let mut args_captured = if let &backend::ast::ImportFunctionKind::Method { rust_name: &str,
ref ty, idl_arguments: impl Iterator<Item = (&'a str, &'a IdlType<'src>)>,
kind: backend::ast::MethodKind::Operation( ret: &IdlType<'src>,
backend::ast::Operation { kind: backend::ast::ImportFunctionKind,
is_static: false, .. structural: bool,
} catch: bool,
), doc_comment: Option<String>,
.. ) -> Option<backend::ast::ImportFunction> where 'src: 'a {
} = &kind { // Convert all of the arguments from their IDL type to a `syn` type,
let mut res = Vec::with_capacity(idl_types.len() + 1); // ready to pass to the backend.
res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone()))); //
res // Note that for non-static methods we add a `&self` type placeholder,
} else { // but this type isn't actually used so it's just here for show mostly.
Vec::with_capacity(idl_types.len()) let mut arguments = if let &backend::ast::ImportFunctionKind::Method {
}; ref ty,
for ((argument_name, _, _), idl_type) in arguments.iter().zip(idl_types) { kind: backend::ast::MethodKind::Operation(
let syn_type = if let Some(syn_type) = idl_type.to_syn_type(TypePosition::Argument) { backend::ast::Operation {
syn_type is_static: false, ..
} else { }
),
..
} = &kind {
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
} else {
Vec::with_capacity(idl_arguments.size_hint().0)
};
for (argument_name, idl_type) in idl_arguments {
let syn_type = match idl_type.to_syn_type(TypePosition::Argument) {
Some(t) => t,
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()); };
args_captured.push(simple_fn_arg(argument_name, syn_type)); let argument_name = rust_ident(&argument_name.to_snake_case());
} arguments.push(simple_fn_arg(argument_name, syn_type));
import_functions.push(backend::ast::ImportFunction {
function: backend::ast::Function {
name: name.to_string(),
arguments: args_captured,
ret: ret.clone(),
rust_attrs: vec![],
rust_vis: public(),
},
rust_name,
js_ret: js_ret.clone(),
catch,
structural,
kind: kind.clone(),
shim,
doc_comment: doc_comment.clone(),
})
} }
import_functions
// 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 {
name: js_name.to_string(),
arguments,
ret: ret.clone(),
rust_attrs: vec![],
rust_vis: public(),
},
rust_name: rust_ident(rust_name),
js_ret: js_ret.clone(),
catch,
structural,
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))
},
kind,
doc_comment,
})
} }
/// Convert arguments to ones suitable crating function /// Convert arguments to ones suitable crating function
@ -600,32 +626,20 @@ impl<'src> FirstPassRecord<'src> {
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.
@ -638,14 +652,35 @@ impl<'src> FirstPassRecord<'src> {
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))))
)
}
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,
@ -656,27 +691,7 @@ 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(
&format!("set_{}", name),
false,
false,
&[(
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,
)
} }
} }