Merge pull request #726 from derekdreery/variadic_js_functions

Support variadic javascript function parameters
This commit is contained in:
Alex Crichton
2018-09-03 10:28:58 -07:00
committed by GitHub
12 changed files with 314 additions and 9 deletions

View File

@@ -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(),

View File

@@ -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(())
}

View File

@@ -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 {

View File

@@ -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() {

View File

@@ -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,

View File

@@ -277,6 +277,7 @@ impl<'src> FirstPassRecord<'src> {
},
rust_name: rust_ident(rust_name),
js_ret: js_ret.clone(),
variadic: false,
catch,
structural,
shim:{