Skip to content

Commit aae3c87

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 7aaf780 commit aae3c87

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
@@ -14,6 +14,8 @@ Shared support for the wasm-bindgen-cli package, an internal dependency
1414
base64 = "0.9"
1515
failure = "0.1"
1616
parity-wasm = "0.27"
17+
serde = "1.0"
18+
serde_derive = "1.0"
1719
serde_json = "1.0"
1820
wasm-bindgen-shared = { path = "../shared", version = '=0.2.5' }
1921
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
@@ -5,6 +5,7 @@ use std::mem;
55
use failure::{Error, ResultExt};
66
use parity_wasm::elements::*;
77
use parity_wasm;
8+
use serde_json;
89
use shared;
910
use wasm_gc;
1011

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

3436
#[derive(Default)]
@@ -342,6 +344,7 @@ impl<'a> Context<'a> {
342344

343345
self.export_table();
344346
self.gc()?;
347+
self.add_wasm_pack_section();
345348

346349
while js.contains("\n\n\n") {
347350
js = js.replace("\n\n\n", "\n\n");
@@ -1314,6 +1317,28 @@ impl<'a> Context<'a> {
13141317
self.globals.push_str(s);
13151318
self.globals.push_str("\n");
13161319
}
1320+
1321+
fn add_wasm_pack_section(&mut self) {
1322+
if self.module_versions.len() == 0 {
1323+
return
1324+
}
1325+
1326+
#[derive(Serialize)]
1327+
struct WasmPackSchema<'a> {
1328+
version: &'a str,
1329+
modules: &'a [(String, String)],
1330+
}
1331+
1332+
let contents = serde_json::to_string(&WasmPackSchema {
1333+
version: "0.0.1",
1334+
modules: &self.module_versions,
1335+
}).unwrap();
1336+
1337+
let mut section = CustomSection::default();
1338+
*section.name_mut() = "__wasm_pack_unstable".to_string();
1339+
*section.payload_mut() = contents.into_bytes();
1340+
self.module.sections_mut().push(Section::Custom(section));
1341+
}
13171342
}
13181343

13191344
impl<'a, 'b> SubContext<'a, 'b> {
@@ -1404,6 +1429,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
14041429
}
14051430

14061431
fn generate_import(&mut self, import: &shared::Import) -> Result<(), Error> {
1432+
match (&import.module, &import.version) {
1433+
(&Some(ref m), &Some(ref v)) => {
1434+
self.cx.module_versions.push((m.clone(), v.clone()));
1435+
}
1436+
_ => {}
1437+
}
14071438
match import.kind {
14081439
shared::ImportKind::Function(ref f) => {
14091440
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;
@@ -131,6 +133,7 @@ impl Bindgen {
131133
config: &self,
132134
module: &mut module,
133135
function_table_needed: false,
136+
module_versions: Default::default(),
134137
run_descriptor: &|name| {
135138
let mut v = MyExternals(Vec::new());
136139
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)