From f6cb73442a6efed8611f0651533eb82245d74c0f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 10 Oct 2018 10:21:19 -0700 Subject: [PATCH] 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 --- crates/cli-support/src/js/mod.rs | 115 +++++++++++++++---------------- tests/wasm/classes.js | 4 ++ tests/wasm/classes.rs | 28 ++++++++ 3 files changed, 89 insertions(+), 58 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 9f762cef..34756279 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -56,13 +56,6 @@ pub struct ExportedClass { typescript: String, has_constructor: bool, wrap_needed: bool, - fields: Vec, -} - -struct ClassField { - comments: Vec, - 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, diff --git a/tests/wasm/classes.js b/tests/wasm/classes.js index ad09011d..4e0408bf 100644 --- a/tests/wasm/classes.js +++ b/tests/wasm/classes.js @@ -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); +}; diff --git a/tests/wasm/classes.rs b/tests/wasm/classes.rs index c0b562f7..4ef3e18a 100644 --- a/tests/wasm/classes.rs +++ b/tests/wasm/classes.rs @@ -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(); +}