mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-07-31 03:51:56 +00:00
Merge pull request #726 from derekdreery/variadic_js_functions
Support variadic javascript function parameters
This commit is contained in:
@@ -85,6 +85,7 @@ pub struct ImportFunction {
|
||||
pub rust_name: Ident,
|
||||
pub js_ret: Option<syn::Type>,
|
||||
pub catch: bool,
|
||||
pub variadic: bool,
|
||||
pub structural: bool,
|
||||
pub kind: ImportFunctionKind,
|
||||
pub shim: Ident,
|
||||
@@ -463,6 +464,7 @@ impl ImportFunction {
|
||||
shared::ImportFunction {
|
||||
shim: self.shim.to_string(),
|
||||
catch: self.catch,
|
||||
variadic: self.variadic,
|
||||
method,
|
||||
structural: self.structural,
|
||||
function: self.function.shared(),
|
||||
|
@@ -1733,7 +1733,6 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
|
||||
fn global(&mut self, s: &str) {
|
||||
let s = s;
|
||||
let s = s.trim();
|
||||
|
||||
// Ensure a blank line between adjacent items, and ensure everything is
|
||||
@@ -1963,8 +1962,9 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
|
||||
let js = Rust2Js::new(self.cx)
|
||||
.catch(import.catch)
|
||||
.variadic(import.variadic)
|
||||
.process(descriptor.unwrap_function())?
|
||||
.finish(&target);
|
||||
.finish(&target)?;
|
||||
self.cx.export(&import.shim, &js, None);
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use failure::Error;
|
||||
use failure::{self, Error};
|
||||
|
||||
use super::{Context, Js2Rust};
|
||||
use descriptor::{Descriptor, Function};
|
||||
@@ -36,6 +36,9 @@ pub struct Rust2Js<'a, 'b: 'a> {
|
||||
|
||||
/// Whether or not we're catching JS exceptions
|
||||
catch: bool,
|
||||
|
||||
/// Whether or not the last argument is a slice representing variadic arguments.
|
||||
variadic: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
@@ -50,6 +53,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
arg_idx: 0,
|
||||
ret_expr: String::new(),
|
||||
catch: false,
|
||||
variadic: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +66,15 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn variadic(&mut self, variadic: bool) -> &mut Self {
|
||||
if variadic {
|
||||
self.cx.expose_uint32_memory();
|
||||
self.cx.expose_add_heap_object();
|
||||
}
|
||||
self.variadic = variadic;
|
||||
self
|
||||
}
|
||||
|
||||
/// Generates all bindings necessary for the signature in `Function`,
|
||||
/// creating necessary argument conversions and return value processing.
|
||||
pub fn process(&mut self, function: &Function) -> Result<&mut Self, Error> {
|
||||
@@ -72,6 +85,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Get a generated name for an argument.
|
||||
fn shim_argument(&mut self) -> String {
|
||||
let s = format!("arg{}", self.arg_idx);
|
||||
self.arg_idx += 1;
|
||||
@@ -515,7 +529,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn finish(&self, invoc: &str) -> String {
|
||||
pub fn finish(&self, invoc: &str) -> Result<String, Error> {
|
||||
let mut ret = String::new();
|
||||
ret.push_str("function(");
|
||||
ret.push_str(&self.shim_arguments.join(", "));
|
||||
@@ -528,10 +542,24 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
ret.push_str(") {\n");
|
||||
ret.push_str(&self.prelude);
|
||||
|
||||
let mut invoc = self.ret_expr.replace(
|
||||
"JS",
|
||||
&format!("{}({})", invoc, self.js_arguments.join(", ")),
|
||||
);
|
||||
let mut invoc = if self.variadic {
|
||||
if self.js_arguments.is_empty() {
|
||||
return Err(failure::err_msg("a function with no arguments cannot be variadic"));
|
||||
}
|
||||
let last_arg = self.js_arguments.len() - 1; // check implies >= 0
|
||||
self.ret_expr.replace(
|
||||
"JS",
|
||||
&format!("{}({}, ...{})",
|
||||
invoc,
|
||||
self.js_arguments[..last_arg].join(", "),
|
||||
self.js_arguments[last_arg])
|
||||
)
|
||||
} else {
|
||||
self.ret_expr.replace(
|
||||
"JS",
|
||||
&format!("{}({})", invoc, self.js_arguments.join(", ")),
|
||||
)
|
||||
};
|
||||
if self.catch {
|
||||
let catch = "\
|
||||
const view = getUint32Memory();\n\
|
||||
@@ -566,7 +594,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
|
||||
ret.push_str(&invoc);
|
||||
|
||||
ret.push_str("\n}\n");
|
||||
return ret;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn global_idx(&mut self) -> usize {
|
||||
|
@@ -183,6 +183,14 @@ impl BindgenAttrs {
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether the variadic attributes is present
|
||||
fn variadic(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match *a {
|
||||
BindgenAttr::Variadic => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttrs {
|
||||
@@ -219,6 +227,7 @@ pub enum BindgenAttr {
|
||||
JsName(String),
|
||||
JsClass(String),
|
||||
Extends(Ident),
|
||||
Variadic,
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttr {
|
||||
@@ -304,6 +313,8 @@ impl syn::synom::Synom for BindgenAttr {
|
||||
ns: call!(term2ident) >>
|
||||
(ns)
|
||||
)=> { BindgenAttr::Extends }
|
||||
|
|
||||
call!(term, "variadic") => { |_| BindgenAttr::Variadic }
|
||||
));
|
||||
}
|
||||
|
||||
@@ -365,6 +376,7 @@ impl<'a> ConvertToAst<()> for &'a mut syn::ItemStruct {
|
||||
let getter = shared::struct_field_get(&ident, &name_str);
|
||||
let setter = shared::struct_field_set(&ident, &name_str);
|
||||
let opts = BindgenAttrs::find(&mut field.attrs)?;
|
||||
assert_not_variadic(&opts, &field)?;
|
||||
let comments = extract_doc_comments(&field.attrs);
|
||||
fields.push(ast::StructField {
|
||||
name: name.clone(),
|
||||
@@ -395,6 +407,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
|
||||
) -> Result<Self::Target, Diagnostic> {
|
||||
let default_name = self.ident.to_string();
|
||||
let js_name = opts.js_name().unwrap_or(&default_name);
|
||||
|
||||
let wasm = function_from_decl(
|
||||
js_name,
|
||||
self.decl.clone(),
|
||||
@@ -404,6 +417,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
|
||||
None,
|
||||
)?.0;
|
||||
let catch = opts.catch();
|
||||
let variadic = opts.variadic();
|
||||
let js_ret = if catch {
|
||||
// TODO: this assumes a whole bunch:
|
||||
//
|
||||
@@ -533,6 +547,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
|
||||
kind,
|
||||
js_ret,
|
||||
catch,
|
||||
variadic,
|
||||
structural: opts.structural(),
|
||||
rust_name: self.ident.clone(),
|
||||
shim: Ident::new(&shim, Span::call_site()),
|
||||
@@ -545,6 +560,7 @@ impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
|
||||
type Target = ast::ImportKind;
|
||||
|
||||
fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
|
||||
assert_not_variadic(&attrs, &self)?;
|
||||
let js_name = attrs
|
||||
.js_name()
|
||||
.map_or_else(|| self.ident.to_string(), |s| s.to_string());
|
||||
@@ -570,6 +586,7 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemSt
|
||||
if self.mutability.is_some() {
|
||||
bail_span!(self.mutability, "cannot import mutable globals yet")
|
||||
}
|
||||
assert_not_variadic(&opts, &self)?;
|
||||
let default_name = self.ident.to_string();
|
||||
let js_name = opts.js_name().unwrap_or(&default_name);
|
||||
let shim = format!(
|
||||
@@ -604,6 +621,7 @@ impl ConvertToAst<BindgenAttrs> for syn::ItemFn {
|
||||
if self.unsafety.is_some() {
|
||||
bail_span!(self.unsafety, "can only #[wasm_bindgen] safe functions");
|
||||
}
|
||||
assert_not_variadic(&attrs, &self)?;
|
||||
|
||||
let default_name = self.ident.to_string();
|
||||
let name = attrs.js_name().unwrap_or(&default_name);
|
||||
@@ -1074,6 +1092,15 @@ fn assert_no_lifetimes(decl: &syn::FnDecl) -> Result<(), Diagnostic> {
|
||||
Diagnostic::from_vec(walk.diagnostics)
|
||||
}
|
||||
|
||||
/// This method always fails if the BindgenAttrs contain variadic
|
||||
fn assert_not_variadic(attrs: &BindgenAttrs, span: &dyn ToTokens) -> Result<(), Diagnostic> {
|
||||
if attrs.variadic() {
|
||||
bail_span!(span, "the `variadic` attribute can only be applied to imported \
|
||||
(`extern`) functions")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If the path is a single ident, return it.
|
||||
fn extract_path_ident(path: &syn::Path) -> Result<Ident, Diagnostic> {
|
||||
if path.leading_colon.is_some() {
|
||||
|
@@ -43,6 +43,7 @@ pub enum ImportKind {
|
||||
pub struct ImportFunction {
|
||||
pub shim: String,
|
||||
pub catch: bool,
|
||||
pub variadic: bool,
|
||||
pub method: Option<MethodData>,
|
||||
pub structural: bool,
|
||||
pub function: Function,
|
||||
|
@@ -277,6 +277,7 @@ impl<'src> FirstPassRecord<'src> {
|
||||
},
|
||||
rust_name: rust_ident(rust_name),
|
||||
js_ret: js_ret.clone(),
|
||||
variadic: false,
|
||||
catch,
|
||||
structural,
|
||||
shim:{
|
||||
|
Reference in New Issue
Block a user