Skip to content

Commit ed52120

Browse files
committed
Add support for version specifications
This commit adds a `#[wasm_bindgen(version = "...")]` attribute support. This information is eventually written into a `__wasm_pack_unstable` section. Currently this is a strawman for the proposal in ashleygwilliams/wasm-pack#101
1 parent 2b9c48d commit ed52120

File tree

7 files changed

+131
-0
lines changed

7 files changed

+131
-0
lines changed

DESIGN.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,34 @@ controlling precisely how imports are imported and what they map to in JS. This
886886
section is intended to hopefully be an exhaustive reference of the
887887
possibilities!
888888

889+
* `module` and `version` - we've seen `module` so far indicating where we can
890+
import items from but `version` is also allowed:
891+
892+
```rust
893+
#[wasm_bindgen(module = "moment", version = "2.0.0")]
894+
extern {
895+
type Moment;
896+
fn moment() -> Moment;
897+
#[wasm_bindgen(method)]
898+
fn format(this: &Moment) -> String;
899+
}
900+
```
901+
902+
The `module` key is used to configure the module that each item is imported
903+
from. The `version` key does not affect the generated wasm itself but rather
904+
it's an informative directive for tools like [wasm-pack]. Tools like wasm-pack
905+
will generate a `package.json` for you and the `version` listed here, when
906+
`module` is also an NPM package, will correspond to what to write down in
907+
`package.json`.
908+
909+
In other words the usage of `module` as the name of an NPM package and
910+
`version` as the version requirement allows you to, inline in Rust, depend on
911+
the NPM ecosystem and import functionality from those packages. When bundled
912+
with a tool like [wasm-pack] everything will automatically get wired up with
913+
bundlers and you should be good to go!
914+
915+
[wasm-pack]: https://github.com/ashleygwilliams/wasm-pack
916+
889917
* `catch` - as we saw before the `catch` attribute allows catching a JS
890918
exception. This can be attached to any imported function and the function must
891919
return a `Result` where the `Err` payload is a `JsValue`, like so:

crates/backend/src/ast.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub struct Export {
2020

2121
pub struct Import {
2222
pub module: Option<String>,
23+
pub version: Option<String>,
2324
pub js_namespace: Option<syn::Ident>,
2425
pub kind: ImportKind,
2526
}
@@ -294,6 +295,7 @@ impl Program {
294295
BindgenAttrs::find(attrs)
295296
};
296297
let module = item_opts.module().or(opts.module()).map(|s| s.to_string());
298+
let version = item_opts.version().or(opts.version()).map(|s| s.to_string());
297299
let js_namespace = item_opts.js_namespace().or(opts.js_namespace());
298300
let mut kind = match item {
299301
syn::ForeignItem::Fn(f) => self.push_foreign_fn(f, item_opts),
@@ -304,6 +306,7 @@ impl Program {
304306

305307
self.imports.push(Import {
306308
module,
309+
version,
307310
js_namespace,
308311
kind,
309312
});
@@ -586,6 +589,7 @@ impl Import {
586589
fn shared(&self) -> shared::Import {
587590
shared::Import {
588591
module: self.module.clone(),
592+
version: self.version.clone(),
589593
js_namespace: self.js_namespace.map(|s| s.as_ref().to_string()),
590594
kind: self.kind.shared(),
591595
}
@@ -746,6 +750,16 @@ impl BindgenAttrs {
746750
.next()
747751
}
748752

753+
fn version(&self) -> Option<&str> {
754+
self.attrs
755+
.iter()
756+
.filter_map(|a| match *a {
757+
BindgenAttr::Version(ref s) => Some(&s[..]),
758+
_ => None,
759+
})
760+
.next()
761+
}
762+
749763
pub fn catch(&self) -> bool {
750764
self.attrs.iter().any(|a| match *a {
751765
BindgenAttr::Catch => true,
@@ -844,6 +858,7 @@ enum BindgenAttr {
844858
Method,
845859
JsNamespace(syn::Ident),
846860
Module(String),
861+
Version(String),
847862
Getter(Option<syn::Ident>),
848863
Setter(Option<syn::Ident>),
849864
Structural,
@@ -897,6 +912,13 @@ impl syn::synom::Synom for BindgenAttr {
897912
(s.value())
898913
)=> { BindgenAttr::Module }
899914
|
915+
do_parse!(
916+
call!(term, "version") >>
917+
punct!(=) >>
918+
s: syn!(syn::LitStr) >>
919+
(s.value())
920+
)=> { BindgenAttr::Version }
921+
|
900922
do_parse!(
901923
call!(term, "js_name") >>
902924
punct!(=) >>

crates/cli-support/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Shared support for the wasm-bindgen-cli package, an internal dependency
1313
[dependencies]
1414
base64 = "0.9"
1515
parity-wasm = "0.27"
16+
serde = "1.0"
17+
serde_derive = "1.0"
1618
serde_json = "1.0"
1719
wasm-bindgen-shared = { path = "../shared", version = '=0.2.5' }
1820
wasm-gc-api = "0.1"

crates/cli-support/src/js/mod.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::mem;
44

55
use parity_wasm::elements::*;
66
use parity_wasm;
7+
use serde_json;
78
use shared;
89
use wasm_gc;
910

@@ -28,6 +29,7 @@ pub struct Context<'a> {
2829
pub exported_classes: HashMap<String, ExportedClass>,
2930
pub function_table_needed: bool,
3031
pub run_descriptor: &'a Fn(&str) -> Vec<u32>,
32+
pub module_versions: Vec<(String, String)>,
3133
}
3234

3335
#[derive(Default)]
@@ -336,6 +338,7 @@ impl<'a> Context<'a> {
336338

337339
self.export_table();
338340
self.gc();
341+
self.add_wasm_pack_section();
339342

340343
while js.contains("\n\n\n") {
341344
js = js.replace("\n\n\n", "\n\n");
@@ -1295,6 +1298,28 @@ impl<'a> Context<'a> {
12951298
self.globals.push_str(s);
12961299
self.globals.push_str("\n");
12971300
}
1301+
1302+
fn add_wasm_pack_section(&mut self) {
1303+
if self.module_versions.len() == 0 {
1304+
return
1305+
}
1306+
1307+
#[derive(Serialize)]
1308+
struct WasmPackSchema<'a> {
1309+
version: &'a str,
1310+
modules: &'a [(String, String)],
1311+
}
1312+
1313+
let contents = serde_json::to_string(&WasmPackSchema {
1314+
version: "0.0.1",
1315+
modules: &self.module_versions,
1316+
}).unwrap();
1317+
1318+
let mut section = CustomSection::default();
1319+
*section.name_mut() = "__wasm_pack_unstable".to_string();
1320+
*section.payload_mut() = contents.into_bytes();
1321+
self.module.sections_mut().push(Section::Custom(section));
1322+
}
12981323
}
12991324

13001325
impl<'a, 'b> SubContext<'a, 'b> {
@@ -1371,6 +1396,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
13711396
}
13721397

13731398
pub fn generate_import(&mut self, import: &shared::Import) {
1399+
match (&import.module, &import.version) {
1400+
(&Some(ref m), &Some(ref v)) => {
1401+
self.cx.module_versions.push((m.clone(), v.clone()));
1402+
}
1403+
_ => {}
1404+
}
13741405
match import.kind {
13751406
shared::ImportKind::Function(ref f) => {
13761407
self.generate_import_function(import, f)

crates/cli-support/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
extern crate parity_wasm;
22
extern crate wasm_bindgen_shared as shared;
3+
#[macro_use]
4+
extern crate serde_derive;
35
extern crate serde_json;
46
extern crate wasm_gc;
57
extern crate wasmi;
@@ -133,6 +135,7 @@ impl Bindgen {
133135
config: &self,
134136
module: &mut module,
135137
function_table_needed: false,
138+
module_versions: Default::default(),
136139
run_descriptor: &|name| {
137140
let mut v = MyExternals(Vec::new());
138141
let ret = instance

crates/shared/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub struct Program {
2222
#[derive(Deserialize, Serialize)]
2323
pub struct Import {
2424
pub module: Option<String>,
25+
pub version: Option<String>,
2526
pub js_namespace: Option<String>,
2627
pub kind: ImportKind,
2728
}

tests/all/imports.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,47 @@ fn rename() {
350350
"#)
351351
.test();
352352
}
353+
354+
#[test]
355+
fn versions() {
356+
project()
357+
.file("src/lib.rs", r#"
358+
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
359+
360+
extern crate wasm_bindgen;
361+
362+
use wasm_bindgen::prelude::*;
363+
364+
#[wasm_bindgen(module = "a_module", version = "0.2.0")]
365+
extern {
366+
fn foo();
367+
}
368+
369+
#[wasm_bindgen]
370+
pub fn run() {
371+
foo();
372+
}
373+
"#)
374+
.file("test.js", r#"
375+
const fs = require("fs");
376+
const assert = require("assert");
377+
378+
exports.test = function() {
379+
const bytes = fs.readFileSync('out_bg.wasm');
380+
const m = new WebAssembly.Module(bytes);
381+
const name = '__wasm_pack_unstable';
382+
const sections = WebAssembly.Module.customSections(m, name);
383+
assert.strictEqual(sections.length, 1);
384+
const b = new Uint8Array(sections[0]);
385+
const buf = new Buffer(b);
386+
const map = JSON.parse(buf.toString());
387+
assert.deepStrictEqual(map, {
388+
version: '0.0.1',
389+
modules: [
390+
['a_module', '0.2.0']
391+
]
392+
});
393+
};
394+
"#)
395+
.test();
396+
}

0 commit comments

Comments
 (0)