mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-16 02:00:51 +00:00
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
This commit is contained in:
parent
d9a71b43db
commit
412bebca72
31
DESIGN.md
31
DESIGN.md
@ -886,6 +886,37 @@ controlling precisely how imports are imported and what they map to in JS. This
|
||||
section is intended to hopefully be an exhaustive reference of the
|
||||
possibilities!
|
||||
|
||||
* `module` and `version` - we've seen `module` so far indicating where we can
|
||||
import items from but `version` is also allowed:
|
||||
|
||||
```rust
|
||||
#[wasm_bindgen(module = "moment", version = "^2.0.0")]
|
||||
extern {
|
||||
type Moment;
|
||||
fn moment() -> Moment;
|
||||
#[wasm_bindgen(method)]
|
||||
fn format(this: &Moment) -> String;
|
||||
}
|
||||
```
|
||||
|
||||
The `module` key is used to configure the module that each item is imported
|
||||
from. The `version` key does not affect the generated wasm itself but rather
|
||||
it's an informative directive for tools like [wasm-pack]. Tools like wasm-pack
|
||||
will generate a `package.json` for you and the `version` listed here, when
|
||||
`module` is also an NPM package, will correspond to what to write down in
|
||||
`package.json`.
|
||||
|
||||
In other words the usage of `module` as the name of an NPM package and
|
||||
`version` as the version requirement allows you to, inline in Rust, depend on
|
||||
the NPM ecosystem and import functionality from those packages. When bundled
|
||||
with a tool like [wasm-pack] everything will automatically get wired up with
|
||||
bundlers and you should be good to go!
|
||||
|
||||
Note that the `version` is *required* if `module` doesn't start with `./`. If
|
||||
`module` starts with `./` then it is an error to provide a version.
|
||||
|
||||
[wasm-pack]: https://github.com/ashleygwilliams/wasm-pack
|
||||
|
||||
* `catch` - as we saw before the `catch` attribute allows catching a JS
|
||||
exception. This can be attached to any imported function and the function must
|
||||
return a `Result` where the `Err` payload is a `JsValue`, like so:
|
||||
|
@ -20,6 +20,7 @@ pub struct Export {
|
||||
|
||||
pub struct Import {
|
||||
pub module: Option<String>,
|
||||
pub version: Option<String>,
|
||||
pub js_namespace: Option<syn::Ident>,
|
||||
pub kind: ImportKind,
|
||||
}
|
||||
@ -294,6 +295,7 @@ impl Program {
|
||||
BindgenAttrs::find(attrs)
|
||||
};
|
||||
let module = item_opts.module().or(opts.module()).map(|s| s.to_string());
|
||||
let version = item_opts.version().or(opts.version()).map(|s| s.to_string());
|
||||
let js_namespace = item_opts.js_namespace().or(opts.js_namespace());
|
||||
let mut kind = match item {
|
||||
syn::ForeignItem::Fn(f) => self.push_foreign_fn(f, item_opts),
|
||||
@ -304,6 +306,7 @@ impl Program {
|
||||
|
||||
self.imports.push(Import {
|
||||
module,
|
||||
version,
|
||||
js_namespace,
|
||||
kind,
|
||||
});
|
||||
@ -584,8 +587,29 @@ impl Variant {
|
||||
|
||||
impl Import {
|
||||
fn shared(&self) -> shared::Import {
|
||||
match (&self.module, &self.version) {
|
||||
(&Some(ref m), None) if m.starts_with("./") => {}
|
||||
(&Some(ref m), &Some(_)) if m.starts_with("./") => {
|
||||
panic!("when a module path starts with `./` that indicates \
|
||||
that a local file is being imported so the `version` \
|
||||
key cannot also be specified");
|
||||
}
|
||||
(&Some(_), &Some(_)) => {}
|
||||
(&Some(_), &None) => {
|
||||
panic!("when the `module` directive doesn't start with `./` \
|
||||
then it's interpreted as an NPM package which requires \
|
||||
a `version` to be specified as well, try using \
|
||||
#[wasm_bindgen(module = \"...\", version = \"...\")]")
|
||||
}
|
||||
(&None, &Some(_)) => {
|
||||
panic!("the #[wasm_bindgen(version = \"...\")] attribute can only \
|
||||
be used when `module = \"...\"` is also specified");
|
||||
}
|
||||
(&None, &None) => {}
|
||||
}
|
||||
shared::Import {
|
||||
module: self.module.clone(),
|
||||
version: self.version.clone(),
|
||||
js_namespace: self.js_namespace.map(|s| s.as_ref().to_string()),
|
||||
kind: self.kind.shared(),
|
||||
}
|
||||
@ -746,6 +770,16 @@ impl BindgenAttrs {
|
||||
.next()
|
||||
}
|
||||
|
||||
fn version(&self) -> Option<&str> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match *a {
|
||||
BindgenAttr::Version(ref s) => Some(&s[..]),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn catch(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match *a {
|
||||
BindgenAttr::Catch => true,
|
||||
@ -844,6 +878,7 @@ enum BindgenAttr {
|
||||
Method,
|
||||
JsNamespace(syn::Ident),
|
||||
Module(String),
|
||||
Version(String),
|
||||
Getter(Option<syn::Ident>),
|
||||
Setter(Option<syn::Ident>),
|
||||
Structural,
|
||||
@ -897,6 +932,13 @@ impl syn::synom::Synom for BindgenAttr {
|
||||
(s.value())
|
||||
)=> { BindgenAttr::Module }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "version") >>
|
||||
punct!(=) >>
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
)=> { BindgenAttr::Version }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "js_name") >>
|
||||
punct!(=) >>
|
||||
|
@ -14,6 +14,8 @@ Shared support for the wasm-bindgen-cli package, an internal dependency
|
||||
base64 = "0.9"
|
||||
failure = "0.1"
|
||||
parity-wasm = "0.27"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
wasm-bindgen-shared = { path = "../shared", version = '=0.2.5' }
|
||||
wasm-gc-api = "0.1"
|
||||
|
@ -5,6 +5,7 @@ use std::mem;
|
||||
use failure::{Error, ResultExt};
|
||||
use parity_wasm::elements::*;
|
||||
use parity_wasm;
|
||||
use serde_json;
|
||||
use shared;
|
||||
use wasm_gc;
|
||||
|
||||
@ -29,6 +30,7 @@ pub struct Context<'a> {
|
||||
pub exported_classes: HashMap<String, ExportedClass>,
|
||||
pub function_table_needed: bool,
|
||||
pub run_descriptor: &'a Fn(&str) -> Vec<u32>,
|
||||
pub module_versions: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -341,6 +343,7 @@ impl<'a> Context<'a> {
|
||||
|
||||
self.export_table();
|
||||
self.gc()?;
|
||||
self.add_wasm_pack_section();
|
||||
|
||||
while js.contains("\n\n\n") {
|
||||
js = js.replace("\n\n\n", "\n\n");
|
||||
@ -1333,6 +1336,28 @@ impl<'a> Context<'a> {
|
||||
self.globals.push_str(s);
|
||||
self.globals.push_str("\n");
|
||||
}
|
||||
|
||||
fn add_wasm_pack_section(&mut self) {
|
||||
if self.module_versions.len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct WasmPackSchema<'a> {
|
||||
version: &'a str,
|
||||
modules: &'a [(String, String)],
|
||||
}
|
||||
|
||||
let contents = serde_json::to_string(&WasmPackSchema {
|
||||
version: "0.0.1",
|
||||
modules: &self.module_versions,
|
||||
}).unwrap();
|
||||
|
||||
let mut section = CustomSection::default();
|
||||
*section.name_mut() = "__wasm_pack_unstable".to_string();
|
||||
*section.payload_mut() = contents.into_bytes();
|
||||
self.module.sections_mut().push(Section::Custom(section));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> SubContext<'a, 'b> {
|
||||
@ -1423,6 +1448,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
}
|
||||
|
||||
fn generate_import(&mut self, import: &shared::Import) -> Result<(), Error> {
|
||||
self.validate_import_module(import)?;
|
||||
match import.kind {
|
||||
shared::ImportKind::Function(ref f) => {
|
||||
self.generate_import_function(import, f)
|
||||
@ -1443,6 +1469,40 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_import_module(&mut self, import: &shared::Import)
|
||||
-> Result<(), Error>
|
||||
{
|
||||
let version = match import.version {
|
||||
Some(ref s) => s,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let module = match import.module {
|
||||
Some(ref s) => s,
|
||||
None => return Ok(()),
|
||||
};
|
||||
if module.starts_with("./") {
|
||||
return Ok(())
|
||||
}
|
||||
let pkg = if module.starts_with("@") {
|
||||
// Translate `@foo/bar/baz` to `@foo/bar` and `@foo/bar` to itself
|
||||
let first_slash = match module.find('/') {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
bail!("packages starting with `@` must be of the form \
|
||||
`@foo/bar`, but found: `{}`", module)
|
||||
}
|
||||
};
|
||||
match module[first_slash + 1..].find('/') {
|
||||
Some(i) => &module[..i],
|
||||
None => module,
|
||||
}
|
||||
} else {
|
||||
module.split('/').next().unwrap()
|
||||
};
|
||||
self.cx.module_versions.push((pkg.to_string(), version.clone()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_import_static(
|
||||
&mut self,
|
||||
info: &shared::Import,
|
||||
|
@ -1,5 +1,7 @@
|
||||
extern crate parity_wasm;
|
||||
extern crate wasm_bindgen_shared as shared;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
extern crate wasm_gc;
|
||||
extern crate wasmi;
|
||||
@ -131,6 +133,7 @@ impl Bindgen {
|
||||
config: &self,
|
||||
module: &mut module,
|
||||
function_table_needed: false,
|
||||
module_versions: Default::default(),
|
||||
run_descriptor: &|name| {
|
||||
let mut v = MyExternals(Vec::new());
|
||||
let ret = instance
|
||||
|
@ -22,6 +22,7 @@ pub struct Program {
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Import {
|
||||
pub module: Option<String>,
|
||||
pub version: Option<String>,
|
||||
pub js_namespace: Option<String>,
|
||||
pub kind: ImportKind,
|
||||
}
|
||||
|
@ -351,3 +351,47 @@ fn rename() {
|
||||
"#)
|
||||
.test();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn versions() {
|
||||
project()
|
||||
.file("src/lib.rs", r#"
|
||||
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
|
||||
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(module = "webpack", version = "^0.2.0")]
|
||||
extern {
|
||||
fn foo();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn run() {
|
||||
foo();
|
||||
}
|
||||
"#)
|
||||
.file("test.js", r#"
|
||||
const fs = require("fs");
|
||||
const assert = require("assert");
|
||||
|
||||
exports.test = function() {
|
||||
const bytes = fs.readFileSync('out_bg.wasm');
|
||||
const m = new WebAssembly.Module(bytes);
|
||||
const name = '__wasm_pack_unstable';
|
||||
const sections = WebAssembly.Module.customSections(m, name);
|
||||
assert.strictEqual(sections.length, 1);
|
||||
const b = new Uint8Array(sections[0]);
|
||||
const buf = new Buffer(b);
|
||||
const map = JSON.parse(buf.toString());
|
||||
assert.deepStrictEqual(map, {
|
||||
version: '0.0.1',
|
||||
modules: [
|
||||
['webpack', '^0.2.0']
|
||||
]
|
||||
});
|
||||
};
|
||||
"#)
|
||||
.test();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user