Fix bindings for classes only referenced through struct fields

The bindings generation for a class would accidentally omit the `__wrap`
function if it was only discovered very late in the process that
`__wrap` was needed, after we'd already passed the point where we needed
to have decided that.

This commit moves struct field generation of bindings much earlier in
the binding generation process which should ensure everything is all
hooked up by the time we generate the classes themselves.

Closes #949
This commit is contained in:
Alex Crichton 2018-10-10 10:21:19 -07:00
parent 35eeb711ad
commit f6cb73442a
3 changed files with 89 additions and 58 deletions

View File

@ -56,13 +56,6 @@ pub struct ExportedClass {
typescript: String,
has_constructor: bool,
wrap_needed: bool,
fields: Vec<ClassField>,
}
struct ClassField {
comments: Vec<String>,
name: String,
readonly: bool,
}
pub struct SubContext<'a, 'b: 'a> {
@ -590,46 +583,6 @@ impl<'a> Context<'a> {
));
}
for field in class.fields.iter() {
let wasm_getter = shared::struct_field_get(name, &field.name);
let wasm_setter = shared::struct_field_set(name, &field.name);
let descriptor = match self.describe(&wasm_getter) {
None => continue,
Some(d) => d,
};
let set = {
let mut cx = Js2Rust::new(&field.name, self);
cx.method(true, false)
.argument(&descriptor)?
.ret(&Descriptor::Unit)?;
ts_dst.push_str(&format!(
"{}{}: {}\n",
if field.readonly { "readonly " } else { "" },
field.name,
&cx.js_arguments[0].1
));
cx.finish("", &format!("wasm.{}", wasm_setter)).0
};
let (get, _ts, js_doc) = Js2Rust::new(&field.name, self)
.method(true, false)
.ret(&descriptor)?
.finish("", &format!("wasm.{}", wasm_getter));
if !dst.ends_with("\n") {
dst.push_str("\n");
}
dst.push_str(&format_doc_comments(&field.comments, Some(js_doc)));
dst.push_str("get ");
dst.push_str(&field.name);
dst.push_str(&get);
dst.push_str("\n");
if !field.readonly {
dst.push_str("set ");
dst.push_str(&field.name);
dst.push_str(&set);
}
}
self.global(&format!(
"
function free{}(ptr) {{
@ -1748,17 +1701,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.generate_enum(e);
}
for s in self.program.structs.iter() {
let mut class = self
.cx
.exported_classes
.entry(s.name.clone())
.or_insert_with(Default::default);
class.comments = format_doc_comments(&s.comments, None);
class.fields.extend(s.fields.iter().map(|f| ClassField {
name: f.name.clone(),
readonly: f.readonly,
comments: f.comments.clone(),
}));
self.generate_struct(s);
}
Ok(())
@ -2116,6 +2059,62 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.cx.typescript.push_str("}\n");
}
fn generate_struct(&mut self, struct_: &shared::Struct) -> Result<(), Error> {
let mut dst = String::new();
let mut ts_dst = String::new();
for field in struct_.fields.iter() {
let wasm_getter = shared::struct_field_get(&struct_.name, &field.name);
let wasm_setter = shared::struct_field_set(&struct_.name, &field.name);
let descriptor = match self.cx.describe(&wasm_getter) {
None => continue,
Some(d) => d,
};
let set = {
let mut cx = Js2Rust::new(&field.name, self.cx);
cx.method(true, false)
.argument(&descriptor)?
.ret(&Descriptor::Unit)?;
ts_dst.push_str(&format!(
"{}{}: {}\n",
if field.readonly { "readonly " } else { "" },
field.name,
&cx.js_arguments[0].1
));
cx.finish("", &format!("wasm.{}", wasm_setter)).0
};
let (get, _ts, js_doc) = Js2Rust::new(&field.name, self.cx)
.method(true, false)
.ret(&descriptor)?
.finish("", &format!("wasm.{}", wasm_getter));
if !dst.ends_with("\n") {
dst.push_str("\n");
}
dst.push_str(&format_doc_comments(&field.comments, Some(js_doc)));
dst.push_str("get ");
dst.push_str(&field.name);
dst.push_str(&get);
dst.push_str("\n");
if !field.readonly {
dst.push_str("set ");
dst.push_str(&field.name);
dst.push_str(&set);
}
}
let class = self
.cx
.exported_classes
.entry(struct_.name.clone())
.or_insert_with(Default::default);
class.comments = format_doc_comments(&struct_.comments, None);
class.contents.push_str(&dst);
class.contents.push_str("\n");
class.typescript.push_str(&ts_dst);
class.typescript.push_str("\n");
Ok(())
}
fn register_vendor_prefix(
&mut self,
info: &shared::ImportType,

View File

@ -132,3 +132,7 @@ exports.js_js_rename = () => {
(new wasm.JsRename()).bar();
wasm.classes_foo();
};
exports.js_access_fields = () => {
assert.ok((new wasm.AccessFieldFoo()).bar instanceof wasm.AccessFieldBar);
};

View File

@ -20,6 +20,7 @@ extern "C" {
fn js_readonly_fields();
fn js_double_consume();
fn js_js_rename();
fn js_access_fields();
}
#[wasm_bindgen_test]
@ -351,3 +352,30 @@ impl JsRename {
#[wasm_bindgen(js_name = classes_foo)]
pub fn foo() {}
#[wasm_bindgen]
pub struct AccessFieldFoo {
pub bar: AccessFieldBar,
}
#[wasm_bindgen]
#[derive(Copy, Clone)]
pub struct AccessFieldBar {
value: u32,
}
#[wasm_bindgen]
impl AccessFieldFoo {
#[wasm_bindgen(constructor)]
pub fn new() -> AccessFieldFoo {
AccessFieldFoo {
bar: AccessFieldBar { value: 2 },
}
}
}
#[wasm_bindgen_test]
fn access_fields() {
js_access_fields();
}