mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-22 09:11:35 +00:00
Migrate from a macro to an attribute
This commit migrates from `wasm_bindgen!`-the-macro to `#[wasm_bindgen]`-the-attribute. The actual mechanics of the macro are relatively simple in just generating some shims here and there, but wrapping everything in one huge macro invocation can often seem intimidating as it gives off this feeling of "oh dear anything can happen here!" Using an attribute should curb expectations much more greatly of "oh there's just some extra stuff happening behind the scenes". The usage is otherwise relatively straightforward and close to what it was before, but check out the DESIGN.md/README.md changes for more info!
This commit is contained in:
173
DESIGN.md
173
DESIGN.md
@ -28,6 +28,41 @@ desired interface expressed in JS (classes, types, strings, etc) and the
|
|||||||
`foo_wasm.wasm` module is simply used as an implementation detail (it was
|
`foo_wasm.wasm` module is simply used as an implementation detail (it was
|
||||||
lightly modified from the original `foo.wasm` file).
|
lightly modified from the original `foo.wasm` file).
|
||||||
|
|
||||||
|
## Foundation #2: Unintrusive in Rust
|
||||||
|
|
||||||
|
On the more Rust-y side of things the `wasm-bindgen` crate is designed to
|
||||||
|
ideally have as minimal impact on a Rust crate as possible. Ideally a few
|
||||||
|
`#[wasm_bindgen]` attributes are annotated in key locations and otherwise you're
|
||||||
|
off to the races, but otherwise it strives to both not invent new syntax and
|
||||||
|
work with existing idioms today.
|
||||||
|
|
||||||
|
For example the `#[no_mangle]` and `extern` ABI indicators are required for
|
||||||
|
annotated free functions with `#[wasm_bindgen]`, because these two snippets are
|
||||||
|
actually equivalent:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn only_integers(a: i32) -> u32 {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// is equivalent to...
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn only_integers_with_wasm_bindgen(a: i32) -> u32 {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally the design here with minimal intervention in Rust should allow us
|
||||||
|
to easily take advantage of the upcoming [host bindings][host] proposal. Ideally
|
||||||
|
you'd simply upgrade `wasm-bindgen`-the-crate as well as you're toolchain and
|
||||||
|
you're immediately getting raw access to host bindigns! (this is still a bit of
|
||||||
|
aways off though...)
|
||||||
|
|
||||||
|
[host]: https://github.com/WebAssembly/host-bindings
|
||||||
|
|
||||||
## Polyfill for "JS objects in wasm"
|
## Polyfill for "JS objects in wasm"
|
||||||
|
|
||||||
One of the main goals of `wasm-bindgen` is to allow working with and passing
|
One of the main goals of `wasm-bindgen` is to allow working with and passing
|
||||||
@ -52,10 +87,10 @@ Let's take a look at an example.
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
// foo.rs
|
// foo.rs
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub fn foo(a: &JsValue) {
|
#[no_mangle]
|
||||||
|
pub extern fn foo(a: &JsValue) {
|
||||||
// ...
|
// ...
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -108,11 +143,11 @@ Here we can see a few notable points of action:
|
|||||||
used, issuing a `pop` for what was pushed at the start of the function.
|
used, issuing a `pop` for what was pushed at the start of the function.
|
||||||
|
|
||||||
It's also helpful to dig into the Rust side of things to see what's going on
|
It's also helpful to dig into the Rust side of things to see what's going on
|
||||||
there! Let's take a look at the code that `wasm_bindgen!` generates in Rust:
|
there! Let's take a look at the code that `#[wasm_bindgen]` generates in Rust:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// what the user wrote
|
// what the user wrote, note that #[no_mangle] is removed
|
||||||
pub fn foo(a: &JsValue) {
|
pub extern fn foo(a: &JsValue) {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,10 +187,9 @@ example.
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
// foo.rs
|
// foo.rs
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub fn foo(a: JsValue) {
|
pub fn foo(a: JsValue) {
|
||||||
// ...
|
// ...
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -216,7 +250,7 @@ And finally, let's take a look at the Rust generated again too:
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
// what the user wrote
|
// what the user wrote
|
||||||
pub fn foo(a: JsValue) {
|
pub extern fn foo(a: JsValue) {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,10 +317,10 @@ The most interesting conversion here happens with strings so let's take a look
|
|||||||
at that.
|
at that.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub fn greet(a: &str) -> String {
|
#[no_mangle]
|
||||||
|
pub extern fn greet(a: &str) -> String {
|
||||||
format!("Hello, {}!", a)
|
format!("Hello, {}!", a)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -358,7 +392,7 @@ At this point it may be predictable, but let's take a look at the Rust side of
|
|||||||
things as well
|
things as well
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub fn greet(a: &str) -> String {
|
pub extern fn greet(a: &str) -> String {
|
||||||
format!("Hello, {}!", a)
|
format!("Hello, {}!", a)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +418,7 @@ then returned up to was for reading via the `__wbindgen_boxed_str_*` functions.
|
|||||||
So in general exporting a function involves a shim both in JS and in Rust with
|
So in general exporting a function involves a shim both in JS and in Rust with
|
||||||
each side translating to or from wasm arguments to the native types of each
|
each side translating to or from wasm arguments to the native types of each
|
||||||
language. The `wasm-bindgen` tool manages hooking up all these shims while the
|
language. The `wasm-bindgen` tool manages hooking up all these shims while the
|
||||||
`wasm_bindgen!` macro takes care of the Rust shim as well.
|
`#[wasm_bindgen]` macro takes care of the Rust shim as well.
|
||||||
|
|
||||||
Most arguments have a relatively clear way to convert them, bit if you've got
|
Most arguments have a relatively clear way to convert them, bit if you've got
|
||||||
any questions just let me know!
|
any questions just let me know!
|
||||||
@ -399,11 +433,9 @@ First up, let's say we invert the function above and instead want to generate
|
|||||||
greetings in JS but call it from Rust. We might have, for example:
|
greetings in JS but call it from Rust. We might have, for example:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./greet")]
|
||||||
#[wasm_module = "./greet"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
fn greet(a: &str) -> String;
|
fn greet(a: &str) -> String;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn other_code() {
|
fn other_code() {
|
||||||
@ -412,8 +444,9 @@ fn other_code() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The basic idea of exports is the same in that we'll have shims in both JS and
|
The basic idea of imports is the same as exports in that we'll have shims in
|
||||||
Rust doing the necessary translation. Let's first see the JS shim in action:
|
both JS and Rust doing the necessary translation. Let's first see the JS shim in
|
||||||
|
action:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import * as wasm from './foo_wasm';
|
import * as wasm from './foo_wasm';
|
||||||
@ -442,7 +475,7 @@ There's some tricky ABI business going on here so let's take a look at the
|
|||||||
generated Rust as well:
|
generated Rust as well:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn greet(a: &str) -> String {
|
extern fn greet(a: &str) -> String {
|
||||||
extern {
|
extern {
|
||||||
fn __wbg_f_greet(a_ptr: *const u8, a_len: usize, ret_len: *mut usize) -> *mut u8;
|
fn __wbg_f_greet(a_ptr: *const u8, a_len: usize, ret_len: *mut usize) -> *mut u8;
|
||||||
}
|
}
|
||||||
@ -473,16 +506,17 @@ sometimes, though, want to go even further and define a JS `class` in Rust. Or
|
|||||||
in other words, we want to expose an object with methods from Rust to JS rather
|
in other words, we want to expose an object with methods from Rust to JS rather
|
||||||
than just importing/exporting free functions.
|
than just importing/exporting free functions.
|
||||||
|
|
||||||
The `wasm_bindgen!` macro currently allows an `impl` block in Rust which does
|
The `#[wasm_bindgen]` attribute can annotate both a `struct` and `impl` blocks
|
||||||
just this:
|
to allow:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub struct Foo {
|
pub struct Foo {
|
||||||
internal: i32,
|
internal: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Foo {
|
#[wasm_bindgen]
|
||||||
|
impl Foo {
|
||||||
pub fn new(val: i32) -> Foo {
|
pub fn new(val: i32) -> Foo {
|
||||||
Foo { internal: val }
|
Foo { internal: val }
|
||||||
}
|
}
|
||||||
@ -494,14 +528,15 @@ wasm_bindgen! {
|
|||||||
pub fn set(&mut self, val: i32) {
|
pub fn set(&mut self, val: i32) {
|
||||||
self.internal = val;
|
self.internal = val;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This is a typical Rust `struct` definition for a type with a constructor and a
|
This is a typical Rust `struct` definition for a type with a constructor and a
|
||||||
few methods. By encasing it in the `wasm_bindgen!` macro we're ensuring that the
|
few methods. Annotating the struct with `#[wasm_bindgen]` means that we'll
|
||||||
types and methods are also available to JS. If we take a look at the generated
|
generate necessary trait impls to convert this type to/from the JS boundary. The
|
||||||
JS code for this we'll see:
|
annotated `impl` block here means that the functions inside will also be made
|
||||||
|
available to JS through generated shims. If we take a look at the generated JS
|
||||||
|
code for this we'll see:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import * as wasm from './foo_wasm';
|
import * as wasm from './foo_wasm';
|
||||||
@ -553,7 +588,7 @@ The real trickery with these bindings ends up happening in Rust, however, so
|
|||||||
let's take a look at that.
|
let's take a look at that.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// original input to `wasm_bindgen!` omitted ...
|
// original input to `#[wasm_bindgen]` omitted ...
|
||||||
|
|
||||||
#[export_name = "foo_new"]
|
#[export_name = "foo_new"]
|
||||||
pub extern fn __wasm_bindgen_generated_Foo_new(arg0: i32) -> u32
|
pub extern fn __wasm_bindgen_generated_Foo_new(arg0: i32) -> u32
|
||||||
@ -597,14 +632,14 @@ same as [`RefCell`] and can be mostly glossed over.
|
|||||||
The purpose for this type, if you're interested though, is to uphold Rust's
|
The purpose for this type, if you're interested though, is to uphold Rust's
|
||||||
guarantees about aliasing in a world where aliasing is rampant (JS).
|
guarantees about aliasing in a world where aliasing is rampant (JS).
|
||||||
Specifically the `&Foo` type means that there can be as much alaising as you'd
|
Specifically the `&Foo` type means that there can be as much alaising as you'd
|
||||||
like, but crucially `&mut Foo` means that it is the sole pointer to the data (no
|
like, but crucially `&mut Foo` means that it is the sole pointer to the data
|
||||||
other `&Foo` to the same instance exists). The [`RefCell`] type in libstd is a
|
(no other `&Foo` to the same instance exists). The [`RefCell`] type in libstd
|
||||||
way of dynamically enforcing this at runtime (as opposed to compile time where
|
is a way of dynamically enforcing this at runtime (as opposed to compile time
|
||||||
it usually happens). Baking in `WasmRefCell` is the same idea here, adding
|
where it usually happens). Baking in `WasmRefCell` is the same idea here,
|
||||||
runtime checks for aliasing which are typically happening at compile time. This
|
adding runtime checks for aliasing which are typically happening at compile
|
||||||
is currently a Rust-specific feature which isn't actually in the `wasm-bindgen`
|
time. This is currently a Rust-specific feature which isn't actually in the
|
||||||
tool itself, it's just in the Rust-generated code (aka the `wasm_bindgen!`
|
`wasm-bindgen` tool itself, it's just in the Rust-generated code (aka the
|
||||||
macro).
|
`#[wasm_bindgen]` attribute).
|
||||||
|
|
||||||
[`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
|
[`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
|
||||||
|
|
||||||
@ -619,15 +654,21 @@ object bindings describe above.
|
|||||||
As usual though, let's dive into an example!
|
As usual though, let's dive into an example!
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./bar")]
|
||||||
#[wasm_module = "./bar"]
|
extern {
|
||||||
extern struct Bar {
|
type Bar;
|
||||||
|
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
fn new(arg: i32) -> Bar;
|
fn new(arg: i32) -> Bar;
|
||||||
|
|
||||||
|
#[wasm_bindgen(static = Bar)]
|
||||||
fn another_function() -> i32;
|
fn another_function() -> i32;
|
||||||
fn get(&self) -> i32;
|
|
||||||
fn set(&self, val: i32);
|
#[wasm_bindgen(method)]
|
||||||
}
|
fn get(this: &Bar) -> i32;
|
||||||
|
|
||||||
|
#[wasm_bindgen(method)]
|
||||||
|
fn set(this: &Bar, val: i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run() {
|
fn run() {
|
||||||
@ -637,8 +678,28 @@ fn run() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we're going to do the opposite of the above example and instead import our
|
Unlike our previous imports, this one's a bit more chatty! Remember that one of
|
||||||
class and use it from Rust. First up, let's look at the JS:
|
the goals of `wasm-bindgen` is to use native Rust syntax wherever possible, so
|
||||||
|
this is mostly intended to use the `#[wasm_bindgen]` attribute to interpret
|
||||||
|
what's written down in Rust. Now there's a few attribute annotations here, so
|
||||||
|
let's go through one-by-one:
|
||||||
|
|
||||||
|
* `#[wasm_bindgen(module = "./bar")]` - seen before with imports this is declare
|
||||||
|
where all the subsequent functionality is imported form. For example the `Bar`
|
||||||
|
type is going to be imported from the `./bar` module.
|
||||||
|
* `type Bar` - this is a declaration of JS class as a new type in Rust. This
|
||||||
|
means that a new type `Bar` is generated which is "opaque" but is represented
|
||||||
|
as internally containing a `JsValue`. We'll see more on this later.
|
||||||
|
* `#[wasm_bindgen(constructor)]` - this indicates that the binding's name isn't
|
||||||
|
actually used in JS but rather translates to `new Bar()`. The return value of
|
||||||
|
this function must be a bare type, like `Bar`.
|
||||||
|
* `#[wasm_bindgen(static = Bar)]` - this attribute indicates that the function
|
||||||
|
declaration is a static function accessed through the `Bar` class in JS.
|
||||||
|
* `#[wasm_bindgen(method)]` - and finally, this attribute indicates that a
|
||||||
|
method call is going to happen. The first argument must be a JS struct, like
|
||||||
|
`Bar`, and the call in JS looks like `Bar.prototype.set.call(...)`.
|
||||||
|
|
||||||
|
With all tha tin mind, let's take a look at the JS generated.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import * as wasm from './foo_wasm';
|
import * as wasm from './foo_wasm';
|
||||||
@ -724,6 +785,14 @@ impl Bar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WasmBoundary for Bar {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToRefWasmBoundary for Bar {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In Rust we're seeing that a new type, `Bar`, is generated for this import of a
|
In Rust we're seeing that a new type, `Bar`, is generated for this import of a
|
||||||
@ -748,12 +817,10 @@ In the meantime though fear not! You can, if necessary, annotate some imports
|
|||||||
as whether they should catch an exception. For example:
|
as whether they should catch an exception. For example:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./bar")]
|
||||||
#[wasm_module = "./bar"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
#[wasm_bindgen(catch)]
|
#[wasm_bindgen(catch)]
|
||||||
fn foo() -> Result<(), JsValue>;
|
fn foo() -> Result<(), JsValue>;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
133
README.md
133
README.md
@ -77,16 +77,22 @@ extern crate wasm_bindgen;
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub fn greet(name: &str) -> String {
|
extern {
|
||||||
format!("Hello, {}!", name)
|
fn alert(s: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn greet(name: &str) {
|
||||||
|
alert(&format!("Hello, {}!", name));
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we're wrapping the code we'd like to export to JS in the `wasm_bindgen!`
|
And that's it! If we were to write the `greet` function naively without the
|
||||||
macro. We'll see more features later, but it suffices to say that most Rust
|
`#[wasm_bindgen]` attribute then JS wouldn't be able to communicate with the
|
||||||
syntax fits inside here, it's not too special beyond what it generates!
|
types like `str`, so slapping a `#[wasm_bindgen]` on the function and the import
|
||||||
|
of `alert` ensures that the right shims are generated.
|
||||||
|
|
||||||
Next up let's build our project:
|
Next up let's build our project:
|
||||||
|
|
||||||
@ -155,7 +161,7 @@ import { greet } from "./js_hello_world";
|
|||||||
import { booted } from "./js_hello_world_wasm";
|
import { booted } from "./js_hello_world_wasm";
|
||||||
|
|
||||||
booted.then(() => {
|
booted.then(() => {
|
||||||
alert(greet("World!"))
|
greet("World!");
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -188,31 +194,31 @@ If you open that in a browser you should see a `Hello, world!` dialog pop up!
|
|||||||
## What just happened?
|
## What just happened?
|
||||||
|
|
||||||
Phew! That was a lot of words and a lot ended up happening along the way. There
|
Phew! That was a lot of words and a lot ended up happening along the way. There
|
||||||
were two main pieces of magic happening: the `wasm_bindgen!` macro and the
|
were two main pieces of magic happening: the `#[wasm_bindgen]` attribute and the
|
||||||
`wasm-bindgen` CLI tool.
|
`wasm-bindgen` CLI tool.
|
||||||
|
|
||||||
**The `wasm_bindgen!` macro**
|
**The `#[wasm_bindgen]` attribute**
|
||||||
|
|
||||||
This macro, exported from the `wasm-bindgen` crate, is the entrypoint to
|
This attribute, exported from the `wasm-bindgen` crate, is the entrypoint to
|
||||||
exposing Rust functions to JS. This is a procedural macro (hence requiring the
|
exposing Rust functions to JS. This is a procedural macro (hence requiring the
|
||||||
nightly Rust toolchain) which will transform the definitions inside and prepare
|
nightly Rust toolchain) which will generate the appropriate shims in Rust to
|
||||||
appropriate wrappers to receive JS-compatible types and convert them to
|
translate from your type signature to one that JS can interface with. Finally
|
||||||
Rust-compatible types.
|
the attribute also serializes some information to the output artifact which
|
||||||
|
`wasm-bindgen`-the-tool will discard after it parses.
|
||||||
|
|
||||||
There's a more thorough explanation below of the various bits and pieces of the
|
There's a more thorough explanation below of the various bits and pieces of the
|
||||||
macro, but it suffices for now to say that you can have free functions, structs,
|
attribute, but it suffices for now to say that you can attach it to free
|
||||||
and impl blocks for those structs in the macro right now. Many Rust features
|
functions, structs, impl blocks for those structs and `extern { ... }` blocks.
|
||||||
aren't supported in these blocks like generics, lifetime parameters, etc.
|
Some Rust features like generics, lifetime parameters, etc, aren't supported on
|
||||||
Additionally not all types can be taken or returned from the functions. In
|
functions tagged with `#[wasm_bindgen]` right now.
|
||||||
general though simple-ish types should work just fine!
|
|
||||||
|
|
||||||
**The `wasm-bindgen` CLI tool**
|
**The `wasm-bindgen` CLI tool**
|
||||||
|
|
||||||
The next half of what happened here was all in the `wasm-bindgen` tool. This
|
The next half of what happened here was all in the `wasm-bindgen` tool. This
|
||||||
tool opened up the wasm module that rustc generated and found an encoded
|
tool opened up the wasm module that rustc generated and found an encoded
|
||||||
description of what was passed to the `wasm_bindgen!` macro. You can think of
|
description of what was passed to the `#[wasm_bindgen]` attribute. You can
|
||||||
this as the `wasm_bindgen!` macro created a special section of the output module
|
think of this as the `#[wasm_bindgen]` attribute created a special section of
|
||||||
which `wasm-bindgen` strips and processes.
|
the output module which `wasm-bindgen` strips and processes.
|
||||||
|
|
||||||
This information gave `wasm-bindgen` all it needed to know to generate the JS
|
This information gave `wasm-bindgen` all it needed to know to generate the JS
|
||||||
file that we then imported. The JS file wraps instantiating the underlying wasm
|
file that we then imported. The JS file wraps instantiating the underlying wasm
|
||||||
@ -231,20 +237,23 @@ extern crate wasm_bindgen;
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
// Strings can both be passed in and received
|
||||||
// Strings can both be passed in and received
|
#[wasm_bindgen]
|
||||||
pub fn concat(a: &str, b: &str) -> String {
|
#[no_mangle]
|
||||||
|
pub extern fn concat(a: &str, b: &str) -> String {
|
||||||
let mut a = a.to_string();
|
let mut a = a.to_string();
|
||||||
a.push_str(b);
|
a.push_str(b);
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
// A struct will show up as a class on the JS side of things
|
// A struct will show up as a class on the JS side of things
|
||||||
pub struct Foo {
|
#[wasm_bindgen]
|
||||||
|
pub struct Foo {
|
||||||
contents: u32,
|
contents: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Foo {
|
#[wasm_bindgen]
|
||||||
|
impl Foo {
|
||||||
pub fn new() -> Foo {
|
pub fn new() -> Foo {
|
||||||
Foo { contents: 0 }
|
Foo { contents: 0 }
|
||||||
}
|
}
|
||||||
@ -265,21 +274,33 @@ wasm_bindgen! {
|
|||||||
pub fn consume_other(&mut self, bar: Bar) {
|
pub fn consume_other(&mut self, bar: Bar) {
|
||||||
self.contents += bar.contents;
|
self.contents += bar.contents;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Bar {
|
#[wasm_bindgen]
|
||||||
|
pub struct Bar {
|
||||||
contents: u32,
|
contents: u32,
|
||||||
opaque: JsValue, // defined in `wasm_bindgen`, imported via prelude
|
opaque: JsValue, // defined in `wasm_bindgen`, imported via prelude
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_module = "./index"] // what ES6 module to import this functionality from
|
#[wasm_bindgen(module = "./index")] // what ES6 module to import from
|
||||||
extern "JS" {
|
extern {
|
||||||
fn bar_on_reset(to: &str, opaque: &JsValue);
|
fn bar_on_reset(to: &str, opaque: &JsValue);
|
||||||
}
|
|
||||||
|
|
||||||
impl Bar {
|
// We can import classes and annotate functionality on those classes as well
|
||||||
|
type Awesome;
|
||||||
|
#[wasm_bindgen(constructor)]
|
||||||
|
fn new() -> Awesome;
|
||||||
|
#[wasm_bindgen(method)]
|
||||||
|
fn get_internal(this: &Awesome) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
impl Bar {
|
||||||
pub fn from_str(s: &str, opaque: JsValue) -> Bar {
|
pub fn from_str(s: &str, opaque: JsValue) -> Bar {
|
||||||
Bar { contents: s.parse().unwrap_or(0), opaque }
|
let contents = s.parse().unwrap_or_else(|| {
|
||||||
|
Awesome::new().get_internal()
|
||||||
|
});
|
||||||
|
Bar { contents, opaque }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self, s: &str) {
|
pub fn reset(&mut self, s: &str) {
|
||||||
@ -288,14 +309,13 @@ wasm_bindgen! {
|
|||||||
self.contents = n;
|
self.contents = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The generated JS bindings for this invocation of the macro [look like
|
The generated JS bindings for this invocation of the macro [look like
|
||||||
this][bindings]. You can view them in action like so:
|
this][bindings]. You can view them in action like so:
|
||||||
|
|
||||||
[bindings]: https://gist.github.com/alexcrichton/12ccab3a18d7db0e0d7d777a0f4951b5
|
[bindings]: https://gist.github.com/alexcrichton/3d85c505e785fb8ff32e2c1cf9618367
|
||||||
|
|
||||||
and our corresponding `index.js`:
|
and our corresponding `index.js`:
|
||||||
|
|
||||||
@ -341,6 +361,16 @@ function main() {
|
|||||||
alert('all passed!')
|
alert('all passed!')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Awesome {
|
||||||
|
constructor() {
|
||||||
|
this.internal = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_internal() {
|
||||||
|
return this.internal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
booted.then(main);
|
booted.then(main);
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -352,21 +382,25 @@ should also be a great place to look for examples.
|
|||||||
|
|
||||||
[tests]: https://github.com/alexcrichton/wasm-bindgen/tree/master/tests
|
[tests]: https://github.com/alexcrichton/wasm-bindgen/tree/master/tests
|
||||||
|
|
||||||
In the `wasm_bindgen!` macro you can have four items: functions, structs,
|
The `#[wasm_bindgen]` attribute can be attached to functions, structs,
|
||||||
impls, and foreign modules. Impls can only contain functions. No lifetime
|
impls, and foreign modules. Impls can only contain functions, and the attribute
|
||||||
parameters or type parameters are allowed on any of these types. Foreign
|
cannot be attached to functions in an impl block or functions in a foreign
|
||||||
modules must have the `"JS"` abi and currently only allow integer/string
|
module. No lifetime parameters or type parameters are allowed on any of these
|
||||||
arguments and integer return values.
|
types. Foreign modules must have the `"C"` abi (or none listed). Free functions
|
||||||
|
with `#[wasm_bindgen]` must also have the `"C"` abi or none listed and also be
|
||||||
|
annotated with the `#[no_mangle]` attribute.
|
||||||
|
|
||||||
All structs referenced through arguments to functions should be defined in the
|
All structs referenced through arguments to functions should be defined in the
|
||||||
macro itself. Arguments allowed are:
|
macro itself. Arguments allowed implement the `WasmBoundary` trait, and examples
|
||||||
|
are:
|
||||||
|
|
||||||
* Integers (not u64/i64)
|
* Integers (not u64/i64)
|
||||||
* Floats
|
* Floats
|
||||||
* Borrowed strings (`&str`)
|
* Borrowed strings (`&str`)
|
||||||
* Owned strings (`String`)
|
* Owned strings (`String`)
|
||||||
* Owned structs (`Foo`) defined in the same bindgen macro
|
* Exported structs (`Foo`, annotated with `#[wasm_bindgen]`)
|
||||||
* Borrowed structs (`&Foo` or `&mut Bar`) defined in the same bindgen macro
|
* Imported types in a foreign module annotated with `#[wasm_bindgen]`
|
||||||
|
* Borrowed exported structs (`&Foo` or `&mut Bar`)
|
||||||
* The `JsValue` type and `&JsValue` (not mutable references)
|
* The `JsValue` type and `&JsValue` (not mutable references)
|
||||||
|
|
||||||
All of the above can also be returned except borrowed references. Strings are
|
All of the above can also be returned except borrowed references. Strings are
|
||||||
@ -382,7 +416,8 @@ safety with reentrancy and aliasing in JS. In general you shouldn't see
|
|||||||
|
|
||||||
JS-values-in-Rust are implemented through indexes that index a table generated
|
JS-values-in-Rust are implemented through indexes that index a table generated
|
||||||
as part of the JS bindings. This table is managed via the ownership specified in
|
as part of the JS bindings. This table is managed via the ownership specified in
|
||||||
Rust and through the bindings that we're returning.
|
Rust and through the bindings that we're returning. More information about this
|
||||||
|
can be found in the [design doc].
|
||||||
|
|
||||||
All of these constructs currently create relatively straightforward code on the
|
All of these constructs currently create relatively straightforward code on the
|
||||||
JS side of things, mostly having a 1:1 match in Rust with JS.
|
JS side of things, mostly having a 1:1 match in Rust with JS.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::char;
|
use std::char;
|
||||||
use std::collections::{HashSet, HashMap};
|
use std::collections::{HashSet, HashMap};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use shared;
|
use shared;
|
||||||
use parity_wasm::elements::*;
|
use parity_wasm::elements::*;
|
||||||
@ -16,6 +17,14 @@ pub struct Context<'a> {
|
|||||||
pub module: &'a mut Module,
|
pub module: &'a mut Module,
|
||||||
pub imports_to_rewrite: HashSet<String>,
|
pub imports_to_rewrite: HashSet<String>,
|
||||||
pub custom_type_names: HashMap<char, String>,
|
pub custom_type_names: HashMap<char, String>,
|
||||||
|
pub imported_names: HashSet<String>,
|
||||||
|
pub exported_classes: HashMap<String, ExportedClass>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ExportedClass {
|
||||||
|
pub contents: String,
|
||||||
|
pub typescript: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SubContext<'a, 'b: 'a> {
|
pub struct SubContext<'a, 'b: 'a> {
|
||||||
@ -37,6 +46,7 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn finalize(&mut self, module_name: &str) -> (String, String) {
|
pub fn finalize(&mut self, module_name: &str) -> (String, String) {
|
||||||
|
self.write_classes();
|
||||||
{
|
{
|
||||||
let mut bind = |name: &str, f: &Fn(&mut Self) -> String| {
|
let mut bind = |name: &str, f: &Fn(&mut Self) -> String| {
|
||||||
if !self.wasm_import_needed(name) {
|
if !self.wasm_import_needed(name) {
|
||||||
@ -202,6 +212,52 @@ impl<'a> Context<'a> {
|
|||||||
(js, self.typescript.clone())
|
(js, self.typescript.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_classes(&mut self) {
|
||||||
|
let classes = mem::replace(&mut self.exported_classes, Default::default());
|
||||||
|
for (class, exports) in classes {
|
||||||
|
let mut dst = String::new();
|
||||||
|
dst.push_str(&format!("export class {} {{", class));
|
||||||
|
let mut ts_dst = dst.clone();
|
||||||
|
ts_dst.push_str("
|
||||||
|
public ptr: number;
|
||||||
|
");
|
||||||
|
if self.config.debug {
|
||||||
|
self.expose_check_token();
|
||||||
|
dst.push_str(&format!("
|
||||||
|
constructor(ptr, sym) {{
|
||||||
|
_checkToken(sym);
|
||||||
|
this.ptr = ptr;
|
||||||
|
}}
|
||||||
|
"));
|
||||||
|
ts_dst.push_str("constructor(ptr: number, sym: Symbol);\n");
|
||||||
|
} else {
|
||||||
|
dst.push_str(&format!("
|
||||||
|
constructor(ptr) {{
|
||||||
|
this.ptr = ptr;
|
||||||
|
}}
|
||||||
|
"));
|
||||||
|
ts_dst.push_str("constructor(ptr: number);\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
dst.push_str(&format!("
|
||||||
|
free() {{
|
||||||
|
const ptr = this.ptr;
|
||||||
|
this.ptr = 0;
|
||||||
|
wasm.{}(ptr);
|
||||||
|
}}
|
||||||
|
", shared::free_function(&class)));
|
||||||
|
ts_dst.push_str("free(): void;\n");
|
||||||
|
|
||||||
|
dst.push_str(&exports.contents);
|
||||||
|
ts_dst.push_str(&exports.contents);
|
||||||
|
dst.push_str("}\n");
|
||||||
|
ts_dst.push_str("}\n");
|
||||||
|
|
||||||
|
self.globals.push_str(&dst);
|
||||||
|
self.typescript.push_str(&ts_dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn rewrite_imports(&mut self, module_name: &str) {
|
fn rewrite_imports(&mut self, module_name: &str) {
|
||||||
for section in self.module.sections_mut() {
|
for section in self.module.sections_mut() {
|
||||||
let imports = match *section {
|
let imports = match *section {
|
||||||
@ -584,27 +640,22 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
impl<'a, 'b> SubContext<'a, 'b> {
|
impl<'a, 'b> SubContext<'a, 'b> {
|
||||||
pub fn generate(&mut self) {
|
pub fn generate(&mut self) {
|
||||||
for f in self.program.free_functions.iter() {
|
for f in self.program.exports.iter() {
|
||||||
self.generate_free_function(f);
|
self.generate_export(f);
|
||||||
}
|
}
|
||||||
for f in self.program.imports.iter() {
|
for f in self.program.imports.iter() {
|
||||||
self.generate_import(f);
|
self.generate_import(f);
|
||||||
}
|
}
|
||||||
for s in self.program.structs.iter() {
|
|
||||||
self.generate_struct(s);
|
|
||||||
}
|
|
||||||
for s in self.program.imported_structs.iter() {
|
|
||||||
self.generate_import_struct(s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_free_function(&mut self, func: &shared::Function) {
|
pub fn generate_export(&mut self, export: &shared::Export) {
|
||||||
|
if let Some(ref class) = export.class {
|
||||||
|
return self.generate_export_for_class(class, export)
|
||||||
|
}
|
||||||
let (js, ts) = self.generate_function("function",
|
let (js, ts) = self.generate_function("function",
|
||||||
&func.name,
|
&export.function.name,
|
||||||
&func.name,
|
|
||||||
false,
|
false,
|
||||||
&func.arguments,
|
&export.function);
|
||||||
func.ret.as_ref());
|
|
||||||
self.cx.globals.push_str("export ");
|
self.cx.globals.push_str("export ");
|
||||||
self.cx.globals.push_str(&js);
|
self.cx.globals.push_str(&js);
|
||||||
self.cx.globals.push_str("\n");
|
self.cx.globals.push_str("\n");
|
||||||
@ -613,86 +664,37 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
self.cx.typescript.push_str("\n");
|
self.cx.typescript.push_str("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_struct(&mut self, s: &shared::Struct) {
|
pub fn generate_export_for_class(&mut self, class: &str, export: &shared::Export) {
|
||||||
let mut dst = String::new();
|
let (js, ts) = if export.method {
|
||||||
dst.push_str(&format!("export class {} {{", s.name));
|
self.generate_function(
|
||||||
let mut ts_dst = dst.clone();
|
|
||||||
ts_dst.push_str("
|
|
||||||
public ptr: number;
|
|
||||||
");
|
|
||||||
if self.cx.config.debug {
|
|
||||||
self.cx.expose_check_token();
|
|
||||||
dst.push_str(&format!("
|
|
||||||
constructor(ptr, sym) {{
|
|
||||||
_checkToken(sym);
|
|
||||||
this.ptr = ptr;
|
|
||||||
}}
|
|
||||||
"));
|
|
||||||
ts_dst.push_str("constructor(ptr: number, sym: Symbol);\n");
|
|
||||||
} else {
|
|
||||||
dst.push_str(&format!("
|
|
||||||
constructor(ptr) {{
|
|
||||||
this.ptr = ptr;
|
|
||||||
}}
|
|
||||||
"));
|
|
||||||
ts_dst.push_str("constructor(ptr: number);\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
dst.push_str(&format!("
|
|
||||||
free() {{
|
|
||||||
const ptr = this.ptr;
|
|
||||||
this.ptr = 0;
|
|
||||||
wasm.{}(ptr);
|
|
||||||
}}
|
|
||||||
", shared::free_function(&s.name)));
|
|
||||||
ts_dst.push_str("free(): void;\n");
|
|
||||||
|
|
||||||
for function in s.functions.iter() {
|
|
||||||
let (js, ts) = self.generate_function(
|
|
||||||
"static",
|
|
||||||
&function.name,
|
|
||||||
&shared::struct_function_export_name(&s.name, &function.name),
|
|
||||||
false,
|
|
||||||
&function.arguments,
|
|
||||||
function.ret.as_ref(),
|
|
||||||
);
|
|
||||||
dst.push_str(&js);
|
|
||||||
dst.push_str("\n");
|
|
||||||
ts_dst.push_str(&ts);
|
|
||||||
ts_dst.push_str("\n");
|
|
||||||
}
|
|
||||||
for method in s.methods.iter() {
|
|
||||||
let (js, ts) = self.generate_function(
|
|
||||||
"",
|
"",
|
||||||
&method.function.name,
|
&shared::struct_function_export_name(class, &export.function.name),
|
||||||
&shared::struct_function_export_name(&s.name, &method.function.name),
|
|
||||||
true,
|
true,
|
||||||
&method.function.arguments,
|
&export.function,
|
||||||
method.function.ret.as_ref(),
|
)
|
||||||
);
|
} else {
|
||||||
dst.push_str(&js);
|
self.generate_function(
|
||||||
dst.push_str("\n");
|
"static",
|
||||||
ts_dst.push_str(&ts);
|
&shared::struct_function_export_name(class, &export.function.name),
|
||||||
ts_dst.push_str("\n");
|
false,
|
||||||
}
|
&export.function,
|
||||||
dst.push_str("}\n");
|
)
|
||||||
ts_dst.push_str("}\n");
|
};
|
||||||
|
let class = self.cx.exported_classes.entry(class.to_string())
|
||||||
self.cx.globals.push_str(&dst);
|
.or_insert(ExportedClass::default());
|
||||||
self.cx.globals.push_str("\n");
|
class.contents.push_str(&js);
|
||||||
self.cx.typescript.push_str(&ts_dst);
|
class.contents.push_str("\n");
|
||||||
self.cx.typescript.push_str("\n");
|
class.typescript.push_str(&ts);
|
||||||
|
class.typescript.push_str("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_function(&mut self,
|
fn generate_function(&mut self,
|
||||||
prefix: &str,
|
prefix: &str,
|
||||||
name: &str,
|
|
||||||
wasm_name: &str,
|
wasm_name: &str,
|
||||||
is_method: bool,
|
is_method: bool,
|
||||||
arguments: &[shared::Type],
|
function: &shared::Function) -> (String, String) {
|
||||||
ret: Option<&shared::Type>) -> (String, String) {
|
let mut dst = format!("{}(", function.name);
|
||||||
let mut dst = format!("{}(", name);
|
let mut dst_ts = format!("{}(", function.name);
|
||||||
let mut dst_ts = format!("{}(", name);
|
|
||||||
let mut passed_args = String::new();
|
let mut passed_args = String::new();
|
||||||
let mut arg_conversions = String::new();
|
let mut arg_conversions = String::new();
|
||||||
let mut destructors = String::new();
|
let mut destructors = String::new();
|
||||||
@ -701,7 +703,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
passed_args.push_str("this.ptr");
|
passed_args.push_str("this.ptr");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, arg) in arguments.iter().enumerate() {
|
for (i, arg) in function.arguments.iter().enumerate() {
|
||||||
let name = format!("arg{}", i);
|
let name = format!("arg{}", i);
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
dst.push_str(", ");
|
dst.push_str(", ");
|
||||||
@ -799,25 +801,25 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
}
|
}
|
||||||
dst.push_str(")");
|
dst.push_str(")");
|
||||||
dst_ts.push_str(")");
|
dst_ts.push_str(")");
|
||||||
let convert_ret = match ret {
|
let convert_ret = match function.ret {
|
||||||
None => {
|
None => {
|
||||||
dst_ts.push_str(": void");
|
dst_ts.push_str(": void");
|
||||||
format!("return ret;")
|
format!("return ret;")
|
||||||
}
|
}
|
||||||
Some(&shared::TYPE_NUMBER) => {
|
Some(shared::TYPE_NUMBER) => {
|
||||||
dst_ts.push_str(": number");
|
dst_ts.push_str(": number");
|
||||||
format!("return ret;")
|
format!("return ret;")
|
||||||
}
|
}
|
||||||
Some(&shared::TYPE_BOOLEAN) => {
|
Some(shared::TYPE_BOOLEAN) => {
|
||||||
dst_ts.push_str(": boolean");
|
dst_ts.push_str(": boolean");
|
||||||
format!("return ret != 0;")
|
format!("return ret != 0;")
|
||||||
}
|
}
|
||||||
Some(&shared::TYPE_JS_OWNED) => {
|
Some(shared::TYPE_JS_OWNED) => {
|
||||||
dst_ts.push_str(": any");
|
dst_ts.push_str(": any");
|
||||||
self.cx.expose_take_object();
|
self.cx.expose_take_object();
|
||||||
format!("return takeObject(ret);")
|
format!("return takeObject(ret);")
|
||||||
}
|
}
|
||||||
Some(&shared::TYPE_STRING) => {
|
Some(shared::TYPE_STRING) => {
|
||||||
dst_ts.push_str(": string");
|
dst_ts.push_str(": string");
|
||||||
self.cx.expose_get_string_from_wasm();
|
self.cx.expose_get_string_from_wasm();
|
||||||
self.cx.required_internal_exports.insert("__wbindgen_boxed_str_ptr");
|
self.cx.required_internal_exports.insert("__wbindgen_boxed_str_ptr");
|
||||||
@ -831,10 +833,10 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
return realRet;
|
return realRet;
|
||||||
")
|
")
|
||||||
}
|
}
|
||||||
Some(&shared::TYPE_JS_REF) |
|
Some(shared::TYPE_JS_REF) |
|
||||||
Some(&shared::TYPE_BORROWED_STR) => panic!(),
|
Some(shared::TYPE_BORROWED_STR) => panic!(),
|
||||||
Some(&t) if (t as u32) & shared::TYPE_CUSTOM_REF_FLAG != 0 => panic!(),
|
Some(t) if (t as u32) & shared::TYPE_CUSTOM_REF_FLAG != 0 => panic!(),
|
||||||
Some(custom) => {
|
Some(ref custom) => {
|
||||||
let name = &self.cx.custom_type_names[custom];
|
let name = &self.cx.custom_type_names[custom];
|
||||||
dst_ts.push_str(": ");
|
dst_ts.push_str(": ");
|
||||||
dst_ts.push_str(name);
|
dst_ts.push_str(name);
|
||||||
@ -881,78 +883,35 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_import(&mut self, import: &shared::Import) {
|
pub fn generate_import(&mut self, import: &shared::Import) {
|
||||||
let imported_name = format!("import{}", self.cx.imports.len());
|
|
||||||
|
|
||||||
self.cx.imports.push_str(&format!("
|
|
||||||
import {{ {} as {} }} from '{}';
|
|
||||||
", import.function.name, imported_name, import.module));
|
|
||||||
|
|
||||||
let name = shared::mangled_import_name(None, &import.function.name);
|
|
||||||
self.gen_import_shim(&name,
|
|
||||||
&imported_name,
|
|
||||||
false,
|
|
||||||
import.catch,
|
|
||||||
&import.function);
|
|
||||||
self.cx.imports_to_rewrite.insert(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_import_struct(&mut self, import: &shared::ImportStruct) {
|
|
||||||
if let Some(ref module) = import.module {
|
if let Some(ref module) = import.module {
|
||||||
|
let name_to_import = import.class.as_ref().unwrap_or(&import.function.name);
|
||||||
|
|
||||||
|
if self.cx.imported_names.insert(name_to_import.clone()) {
|
||||||
self.cx.imports.push_str(&format!("
|
self.cx.imports.push_str(&format!("
|
||||||
import {{ {} }} from '{}';
|
import {{ {} }} from '{}';
|
||||||
", import.name, module));
|
", name_to_import, module));
|
||||||
}
|
|
||||||
|
|
||||||
for f in import.functions.iter() {
|
|
||||||
self.generate_import_struct_function(&import.name, f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_import_struct_function(
|
let name = shared::mangled_import_name(import.class.as_ref().map(|s| &**s),
|
||||||
&mut self,
|
&import.function.name);
|
||||||
class: &str,
|
self.cx.imports_to_rewrite.insert(name.clone());
|
||||||
f: &shared::ImportStructFunction,
|
|
||||||
) {
|
|
||||||
let delegate = if f.method {
|
|
||||||
format!("{}.prototype.{}.call", class, f.function.name)
|
|
||||||
} else if f.js_new {
|
|
||||||
format!("new {}", class)
|
|
||||||
} else {
|
|
||||||
format!("{}.{}", class, f.function.name)
|
|
||||||
};
|
|
||||||
|
|
||||||
let name = shared::mangled_import_name(Some(class), &f.function.name);
|
|
||||||
self.gen_import_shim(&name,
|
|
||||||
&delegate,
|
|
||||||
f.method,
|
|
||||||
f.catch,
|
|
||||||
&f.function);
|
|
||||||
self.cx.imports_to_rewrite.insert(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_import_shim(
|
|
||||||
&mut self,
|
|
||||||
shim_name: &str,
|
|
||||||
shim_delegate: &str,
|
|
||||||
is_method: bool,
|
|
||||||
catch: bool,
|
|
||||||
import: &shared::Function,
|
|
||||||
) {
|
|
||||||
let mut dst = String::new();
|
let mut dst = String::new();
|
||||||
|
|
||||||
dst.push_str(&format!("function {}(", shim_name));
|
dst.push_str(&format!("function {}(", name));
|
||||||
let mut invoc_args = Vec::new();
|
let mut invoc_args = Vec::new();
|
||||||
let mut abi_args = Vec::new();
|
let mut abi_args = Vec::new();
|
||||||
|
|
||||||
if is_method {
|
// if import.method {
|
||||||
abi_args.push("ptr".to_string());
|
// abi_args.push("ptr".to_string());
|
||||||
invoc_args.push("getObject(ptr)".to_string());
|
// invoc_args.push("getObject(ptr)".to_string());
|
||||||
self.cx.expose_get_object();
|
// self.cx.expose_get_object();
|
||||||
}
|
// }
|
||||||
|
|
||||||
let mut extra = String::new();
|
let mut extra = String::new();
|
||||||
|
|
||||||
for (i, arg) in import.arguments.iter().enumerate() {
|
for (i, arg) in import.function.arguments.iter().enumerate() {
|
||||||
match *arg {
|
match *arg {
|
||||||
shared::TYPE_NUMBER => {
|
shared::TYPE_NUMBER => {
|
||||||
invoc_args.push(format!("arg{}", i));
|
invoc_args.push(format!("arg{}", i));
|
||||||
@ -995,8 +954,21 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let invoc = format!("{}({})", shim_delegate, invoc_args.join(", "));
|
let invoc_args = invoc_args.join(", ");
|
||||||
let invoc = match import.ret {
|
let name = &import.function.name;
|
||||||
|
let invoc = match import.class {
|
||||||
|
Some(ref class) if import.method => {
|
||||||
|
format!("{}.prototype.{}.call({})", class, name, invoc_args)
|
||||||
|
}
|
||||||
|
Some(ref class) if import.js_new => {
|
||||||
|
format!("new {}({})", class, invoc_args)
|
||||||
|
}
|
||||||
|
Some(ref class) => {
|
||||||
|
format!("{}.{}({})", class, name, invoc_args)
|
||||||
|
}
|
||||||
|
None => format!("{}({})", name, invoc_args),
|
||||||
|
};
|
||||||
|
let invoc = match import.function.ret {
|
||||||
Some(shared::TYPE_NUMBER) => format!("return {};", invoc),
|
Some(shared::TYPE_NUMBER) => format!("return {};", invoc),
|
||||||
Some(shared::TYPE_BOOLEAN) => format!("return {} ? 1 : 0;", invoc),
|
Some(shared::TYPE_BOOLEAN) => format!("return {} ? 1 : 0;", invoc),
|
||||||
Some(shared::TYPE_JS_OWNED) => {
|
Some(shared::TYPE_JS_OWNED) => {
|
||||||
@ -1017,7 +989,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let invoc = if catch {
|
let invoc = if import.catch {
|
||||||
self.cx.expose_uint32_memory();
|
self.cx.expose_uint32_memory();
|
||||||
self.cx.expose_add_heap_object();
|
self.cx.expose_add_heap_object();
|
||||||
abi_args.push("exnptr".to_string());
|
abi_args.push("exnptr".to_string());
|
||||||
|
@ -78,6 +78,8 @@ impl Bindgen {
|
|||||||
required_internal_exports: Default::default(),
|
required_internal_exports: Default::default(),
|
||||||
imports_to_rewrite: Default::default(),
|
imports_to_rewrite: Default::default(),
|
||||||
custom_type_names: Default::default(),
|
custom_type_names: Default::default(),
|
||||||
|
imported_names: Default::default(),
|
||||||
|
exported_classes: Default::default(),
|
||||||
config: &self,
|
config: &self,
|
||||||
module: &mut module,
|
module: &mut module,
|
||||||
};
|
};
|
||||||
|
@ -1,50 +1,50 @@
|
|||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use quote::{Tokens, ToTokens};
|
use quote::{Tokens, ToTokens};
|
||||||
use shared;
|
use shared;
|
||||||
use syn;
|
use syn;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
pub structs: Vec<Struct>,
|
pub exports: Vec<Export>,
|
||||||
pub free_functions: Vec<Function>,
|
|
||||||
pub imports: Vec<Import>,
|
pub imports: Vec<Import>,
|
||||||
pub imported_structs: Vec<ImportStruct>,
|
pub imported_types: Vec<(syn::Visibility, syn::Ident)>,
|
||||||
|
pub structs: Vec<Struct>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Export {
|
||||||
|
pub class: Option<syn::Ident>,
|
||||||
|
pub method: bool,
|
||||||
|
pub mutable: bool,
|
||||||
|
pub function: Function,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Import {
|
||||||
|
pub module: Option<String>,
|
||||||
|
pub kind: ImportKind,
|
||||||
|
pub function: Function,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ImportKind {
|
||||||
|
Method { class: String, ty: syn::Type },
|
||||||
|
Static { class: String, ty: syn::Type },
|
||||||
|
JsConstructor { class: String, ty: syn::Type },
|
||||||
|
Normal,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub name: syn::Ident,
|
pub name: syn::Ident,
|
||||||
pub arguments: Vec<Type>,
|
pub arguments: Vec<Type>,
|
||||||
pub ret: Option<Type>,
|
pub ret: Option<Type>,
|
||||||
}
|
pub opts: BindgenAttrs,
|
||||||
|
pub rust_attrs: Vec<syn::Attribute>,
|
||||||
pub struct Import {
|
|
||||||
pub module: String,
|
|
||||||
pub function: ImportFunction,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ImportFunction {
|
|
||||||
pub catch: bool,
|
|
||||||
pub ident: syn::Ident,
|
|
||||||
pub wasm_function: Function,
|
|
||||||
pub rust_decl: Box<syn::FnDecl>,
|
pub rust_decl: Box<syn::FnDecl>,
|
||||||
pub rust_vis: syn::Visibility,
|
pub rust_vis: syn::Visibility,
|
||||||
pub rust_attrs: Vec<syn::Attribute>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ImportStruct {
|
pub struct Struct {
|
||||||
pub module: Option<String>,
|
|
||||||
pub name: syn::Ident,
|
pub name: syn::Ident,
|
||||||
pub functions: Vec<ImportStructFunction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ImportStructFunction {
|
|
||||||
pub kind: ImportFunctionKind,
|
|
||||||
pub function: ImportFunction,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ImportFunctionKind {
|
|
||||||
Method,
|
|
||||||
Static,
|
|
||||||
JsConstructor,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
@ -57,19 +57,54 @@ pub enum Type {
|
|||||||
ByValue(syn::Type),
|
ByValue(syn::Type),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Struct {
|
|
||||||
pub name: syn::Ident,
|
|
||||||
pub methods: Vec<Method>,
|
|
||||||
pub functions: Vec<Function>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Method {
|
|
||||||
pub mutable: bool,
|
|
||||||
pub function: Function,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
pub fn push_impl(&mut self, item: &syn::ItemImpl) {
|
pub fn push_item(&mut self,
|
||||||
|
item: syn::Item,
|
||||||
|
opts: Option<BindgenAttrs>,
|
||||||
|
tokens: &mut Tokens) {
|
||||||
|
match item {
|
||||||
|
syn::Item::Fn(mut f) => {
|
||||||
|
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs));
|
||||||
|
|
||||||
|
let no_mangle = f.attrs.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, m)| m.interpret_meta().map(|m| (i, m)))
|
||||||
|
.find(|&(_, ref m)| m.name() == "no_mangle");
|
||||||
|
match no_mangle {
|
||||||
|
Some((i, _)) => { f.attrs.remove(i); }
|
||||||
|
None => {
|
||||||
|
panic!("#[wasm_bindgen] can only be applied to #[no_mangle] \
|
||||||
|
functions, or those that would otherwise be exported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.to_tokens(tokens);
|
||||||
|
self.exports.push(Export {
|
||||||
|
class: None,
|
||||||
|
method: false,
|
||||||
|
mutable: false,
|
||||||
|
function: Function::from(f, opts),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
syn::Item::Struct(mut s) => {
|
||||||
|
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut s.attrs));
|
||||||
|
s.to_tokens(tokens);
|
||||||
|
self.structs.push(Struct::from(s, opts));
|
||||||
|
}
|
||||||
|
syn::Item::Impl(mut i) => {
|
||||||
|
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut i.attrs));
|
||||||
|
i.to_tokens(tokens);
|
||||||
|
self.push_impl(i, opts);
|
||||||
|
}
|
||||||
|
syn::Item::ForeignMod(mut f) => {
|
||||||
|
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs));
|
||||||
|
self.push_foreign_mod(f, opts);
|
||||||
|
}
|
||||||
|
_ => panic!("#[wasm_bindgen] can only be applied to a function, \
|
||||||
|
struct, impl, or extern block"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_impl(&mut self, item: syn::ItemImpl, _opts: BindgenAttrs) {
|
||||||
if item.defaultness.is_some() {
|
if item.defaultness.is_some() {
|
||||||
panic!("default impls are not supported");
|
panic!("default impls are not supported");
|
||||||
}
|
}
|
||||||
@ -91,70 +126,76 @@ impl Program {
|
|||||||
}
|
}
|
||||||
_ => panic!("unsupported self type in impl"),
|
_ => panic!("unsupported self type in impl"),
|
||||||
};
|
};
|
||||||
let dst = self.structs
|
for item in item.items.into_iter() {
|
||||||
.iter_mut()
|
self.push_impl_item(name, item);
|
||||||
.find(|s| s.name == name)
|
|
||||||
.expect(&format!("failed to definition of struct for impl of `{}`", name));
|
|
||||||
for item in item.items.iter() {
|
|
||||||
dst.push_item(item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_foreign_mod(&mut self, f: &syn::ItemForeignMod) {
|
fn push_impl_item(&mut self, class: syn::Ident, item: syn::ImplItem) {
|
||||||
match f.abi.name {
|
let mut method = match item {
|
||||||
Some(ref l) if l.value() == "JS" => {}
|
syn::ImplItem::Const(_) => panic!("const definitions aren't supported"),
|
||||||
_ => panic!("only foreign mods with the `JS` ABI are allowed"),
|
syn::ImplItem::Type(_) => panic!("type definitions in impls aren't supported"),
|
||||||
|
syn::ImplItem::Method(m) => m,
|
||||||
|
syn::ImplItem::Macro(_) => panic!("macros in impls aren't supported"),
|
||||||
|
syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
|
||||||
|
};
|
||||||
|
match method.vis {
|
||||||
|
syn::Visibility::Public(_) => {}
|
||||||
|
_ => return,
|
||||||
}
|
}
|
||||||
let module = f.attrs.iter()
|
if method.defaultness.is_some() {
|
||||||
.filter_map(|f| f.interpret_meta())
|
panic!("default methods are not supported");
|
||||||
.filter_map(|i| {
|
|
||||||
match i {
|
|
||||||
syn::Meta::NameValue(i) => {
|
|
||||||
if i.ident == "wasm_module" {
|
|
||||||
Some(i.lit)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
if method.sig.constness.is_some() {
|
||||||
|
panic!("can only bindgen non-const functions");
|
||||||
}
|
}
|
||||||
_ => None,
|
if method.sig.unsafety.is_some() {
|
||||||
|
panic!("can only bindgen safe functions");
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.next()
|
let opts = BindgenAttrs::find(&mut method.attrs);
|
||||||
.and_then(|lit| {
|
|
||||||
match lit {
|
let (function, mutable) = Function::from_decl(method.sig.ident,
|
||||||
syn::Lit::Str(v) => Some(v.value()),
|
Box::new(method.sig.decl),
|
||||||
_ => None,
|
method.attrs,
|
||||||
}
|
opts,
|
||||||
})
|
method.vis,
|
||||||
.expect("must specify `#[wasm_module = ...]` for module to import from");
|
true);
|
||||||
for item in f.items.iter() {
|
self.exports.push(Export {
|
||||||
let import = self.gen_foreign_item(item, false).0;
|
class: Some(class),
|
||||||
self.imports.push(Import {
|
method: mutable.is_some(),
|
||||||
module: module.clone(),
|
mutable: mutable.unwrap_or(false),
|
||||||
function: import,
|
function,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_foreign_mod(&mut self, f: syn::ItemForeignMod, opts: BindgenAttrs) {
|
||||||
|
match f.abi.name {
|
||||||
|
Some(ref l) if l.value() == "C" => {}
|
||||||
|
None => {}
|
||||||
|
_ => panic!("only foreign mods with the `C` ABI are allowed"),
|
||||||
|
}
|
||||||
|
for item in f.items.into_iter() {
|
||||||
|
match item {
|
||||||
|
syn::ForeignItem::Fn(f) => self.push_foreign_fn(f, &opts),
|
||||||
|
syn::ForeignItem::Type(t) => self.push_foreign_ty(t, &opts),
|
||||||
|
_ => panic!("only foreign functions/types allowed for now"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_foreign_item(&mut self,
|
pub fn push_foreign_fn(&mut self,
|
||||||
f: &syn::ForeignItem,
|
mut f: syn::ForeignItemFn,
|
||||||
allow_self: bool) -> (ImportFunction, bool) {
|
module_opts: &BindgenAttrs) {
|
||||||
let f = match *f {
|
let opts = BindgenAttrs::find(&mut f.attrs);
|
||||||
syn::ForeignItem::Fn(ref f) => f,
|
|
||||||
_ => panic!("only foreign functions allowed for now, not statics"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (mut wasm, mutable) = Function::from_decl(f.ident, &f.decl, allow_self);
|
let mut wasm = Function::from_decl(f.ident,
|
||||||
let is_method = match mutable {
|
f.decl,
|
||||||
Some(false) => true,
|
f.attrs,
|
||||||
None => false,
|
opts,
|
||||||
Some(true) => {
|
f.vis,
|
||||||
panic!("mutable self methods not allowed in extern structs");
|
false).0;
|
||||||
}
|
if wasm.opts.catch() {
|
||||||
};
|
|
||||||
let opts = BindgenOpts::from(&f.attrs);
|
|
||||||
|
|
||||||
if opts.catch {
|
|
||||||
// TODO: this assumes a whole bunch:
|
// TODO: this assumes a whole bunch:
|
||||||
//
|
//
|
||||||
// * The outer type is actually a `Result`
|
// * The outer type is actually a `Result`
|
||||||
@ -166,40 +207,75 @@ impl Program {
|
|||||||
.expect("can't `catch` without returning a Result");
|
.expect("can't `catch` without returning a Result");
|
||||||
}
|
}
|
||||||
|
|
||||||
(ImportFunction {
|
let kind = if wasm.opts.method() {
|
||||||
rust_attrs: f.attrs.clone(),
|
let class = wasm.arguments.get(0)
|
||||||
rust_vis: f.vis.clone(),
|
.expect("methods must have at least one argument");
|
||||||
rust_decl: f.decl.clone(),
|
let class = match *class {
|
||||||
ident: f.ident.clone(),
|
Type::ByRef(ref t) |
|
||||||
wasm_function: wasm,
|
Type::ByValue(ref t) => t,
|
||||||
catch: opts.catch,
|
Type::ByMutRef(_) => {
|
||||||
}, is_method)
|
panic!("first method argument cannot be mutable ref")
|
||||||
}
|
}
|
||||||
|
Type::String | Type::BorrowedStr => {
|
||||||
pub fn push_extern_class(&mut self, class: &ExternClass) {
|
panic!("method receivers cannot be strings")
|
||||||
let functions = class.functions.iter()
|
|
||||||
.map(|f| {
|
|
||||||
let (f, method) = self.gen_foreign_item(f, true);
|
|
||||||
let kind = if method {
|
|
||||||
ImportFunctionKind::Method
|
|
||||||
} else {
|
|
||||||
let opts = BindgenOpts::from(&f.rust_attrs);
|
|
||||||
if opts.constructor {
|
|
||||||
ImportFunctionKind::JsConstructor
|
|
||||||
} else {
|
|
||||||
ImportFunctionKind::Static
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ImportStructFunction { kind, function: f }
|
let class_name = match *class {
|
||||||
})
|
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path,
|
||||||
.collect();
|
_ => panic!("first argument of method must be a path"),
|
||||||
self.imported_structs.push(ImportStruct {
|
};
|
||||||
module: class.module.as_ref().map(|s| s.value()),
|
let class_name = extract_path_ident(class_name)
|
||||||
name: class.name,
|
.expect("first argument of method must be a bare type");
|
||||||
functions,
|
|
||||||
|
ImportKind::Method {
|
||||||
|
class: class_name.as_ref().to_string(),
|
||||||
|
ty: class.clone(),
|
||||||
|
}
|
||||||
|
} else if wasm.opts.constructor() {
|
||||||
|
let class = match wasm.ret {
|
||||||
|
Some(Type::ByValue(ref t)) => t,
|
||||||
|
_ => panic!("constructor returns must be bare types"),
|
||||||
|
};
|
||||||
|
let class_name = match *class {
|
||||||
|
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path,
|
||||||
|
_ => panic!("first argument of method must be a path"),
|
||||||
|
};
|
||||||
|
let class_name = extract_path_ident(class_name)
|
||||||
|
.expect("first argument of method must be a bare type");
|
||||||
|
|
||||||
|
ImportKind::JsConstructor {
|
||||||
|
class: class_name.as_ref().to_string(),
|
||||||
|
ty: class.clone(),
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if let Some(class) = wasm.opts.static_receiver() {
|
||||||
|
let class_name = match *class {
|
||||||
|
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path,
|
||||||
|
_ => panic!("first argument of method must be a path"),
|
||||||
|
};
|
||||||
|
let class_name = extract_path_ident(class_name)
|
||||||
|
.expect("first argument of method must be a bare type");
|
||||||
|
ImportKind::Static {
|
||||||
|
class: class_name.to_string(),
|
||||||
|
ty: class.clone(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImportKind::Normal
|
||||||
|
};
|
||||||
|
|
||||||
|
self.imports.push(Import {
|
||||||
|
module: module_opts.module().map(|s| s.to_string()),
|
||||||
|
kind,
|
||||||
|
function: wasm,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_foreign_ty(&mut self,
|
||||||
|
f: syn::ForeignItemType,
|
||||||
|
_module_opts: &BindgenAttrs) {
|
||||||
|
self.imported_types.push((f.vis, f.ident));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn wbg_literal(&self, dst: &mut Tokens) -> usize {
|
pub fn wbg_literal(&self, dst: &mut Tokens) -> usize {
|
||||||
let mut a = LiteralBuilder {
|
let mut a = LiteralBuilder {
|
||||||
dst,
|
dst,
|
||||||
@ -207,16 +283,17 @@ impl Program {
|
|||||||
};
|
};
|
||||||
a.append("wbg:");
|
a.append("wbg:");
|
||||||
a.fields(&[
|
a.fields(&[
|
||||||
("structs", &|a| a.list(&self.structs, Struct::wbg_literal)),
|
("exports", &|a| a.list(&self.exports, Export::wbg_literal)),
|
||||||
("free_functions", &|a| a.list(&self.free_functions, Function::wbg_literal)),
|
|
||||||
("imports", &|a| a.list(&self.imports, Import::wbg_literal)),
|
("imports", &|a| a.list(&self.imports, Import::wbg_literal)),
|
||||||
("imported_structs", &|a| a.list(&self.imported_structs, ImportStruct::wbg_literal)),
|
|
||||||
("custom_type_names", &|a| {
|
("custom_type_names", &|a| {
|
||||||
a.list(&self.structs, |s, a| {
|
let names = self.exports.iter()
|
||||||
let val = shared::name_to_descriptor(s.name.as_ref());
|
.filter_map(|e| e.class)
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
a.list(&names, |s, a| {
|
||||||
|
let val = shared::name_to_descriptor(s.as_ref());
|
||||||
a.fields(&[
|
a.fields(&[
|
||||||
("descriptor", &|a| a.char(val)),
|
("descriptor", &|a| a.char(val)),
|
||||||
("name", &|a| a.str(s.name.as_ref()))
|
("name", &|a| a.str(s.as_ref()))
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
@ -226,7 +303,7 @@ impl Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn from(input: &syn::ItemFn) -> Function {
|
pub fn from(input: syn::ItemFn, opts: BindgenAttrs) -> Function {
|
||||||
match input.vis {
|
match input.vis {
|
||||||
syn::Visibility::Public(_) => {}
|
syn::Visibility::Public(_) => {}
|
||||||
_ => panic!("can only bindgen public functions"),
|
_ => panic!("can only bindgen public functions"),
|
||||||
@ -237,18 +314,24 @@ impl Function {
|
|||||||
if input.unsafety.is_some() {
|
if input.unsafety.is_some() {
|
||||||
panic!("can only bindgen safe functions");
|
panic!("can only bindgen safe functions");
|
||||||
}
|
}
|
||||||
if !input.abi.is_none() {
|
if input.abi.is_none() {
|
||||||
panic!("can only bindgen Rust ABI functions")
|
panic!("can only bindgen `extern` ABI functions, or those that \
|
||||||
}
|
would otherwise be exported")
|
||||||
if !input.abi.is_none() {
|
|
||||||
panic!("can only bindgen Rust ABI functions")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Function::from_decl(input.ident, &input.decl, false).0
|
Function::from_decl(input.ident,
|
||||||
|
input.decl,
|
||||||
|
input.attrs,
|
||||||
|
opts,
|
||||||
|
input.vis,
|
||||||
|
false).0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_decl(name: syn::Ident,
|
pub fn from_decl(name: syn::Ident,
|
||||||
decl: &syn::FnDecl,
|
decl: Box<syn::FnDecl>,
|
||||||
|
attrs: Vec<syn::Attribute>,
|
||||||
|
opts: BindgenAttrs,
|
||||||
|
vis: syn::Visibility,
|
||||||
allow_self: bool) -> (Function, Option<bool>) {
|
allow_self: bool) -> (Function, Option<bool>) {
|
||||||
if decl.variadic.is_some() {
|
if decl.variadic.is_some() {
|
||||||
panic!("can't bindgen variadic functions")
|
panic!("can't bindgen variadic functions")
|
||||||
@ -281,31 +364,15 @@ impl Function {
|
|||||||
syn::ReturnType::Type(_, ref t) => Some(Type::from(t)),
|
syn::ReturnType::Type(_, ref t) => Some(Type::from(t)),
|
||||||
};
|
};
|
||||||
|
|
||||||
(Function { name, arguments, ret }, mutable)
|
(Function {
|
||||||
}
|
name,
|
||||||
|
arguments,
|
||||||
pub fn free_function_export_name(&self) -> syn::LitStr {
|
ret,
|
||||||
let name = shared::free_function_export_name(self.name.as_ref());
|
opts,
|
||||||
syn::LitStr::new(&name, Span::def_site())
|
rust_vis: vis,
|
||||||
}
|
rust_decl: decl,
|
||||||
|
rust_attrs: attrs,
|
||||||
pub fn struct_function_export_name(&self, s: syn::Ident) -> syn::LitStr {
|
}, mutable)
|
||||||
let name = shared::struct_function_export_name(
|
|
||||||
s.as_ref(),
|
|
||||||
self.name.as_ref(),
|
|
||||||
);
|
|
||||||
syn::LitStr::new(&name, Span::def_site())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rust_symbol(&self, namespace: Option<syn::Ident>) -> syn::Ident {
|
|
||||||
let mut generated_name = format!("__wasm_bindgen_generated");
|
|
||||||
if let Some(ns) = namespace {
|
|
||||||
generated_name.push_str("_");
|
|
||||||
generated_name.push_str(ns.as_ref());
|
|
||||||
}
|
|
||||||
generated_name.push_str("_");
|
|
||||||
generated_name.push_str(self.name.as_ref());
|
|
||||||
syn::Ident::from(generated_name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||||
@ -389,74 +456,68 @@ impl Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Struct {
|
impl Export {
|
||||||
pub fn from(s: &syn::ItemStruct) -> Struct {
|
pub fn rust_symbol(&self) -> syn::Ident {
|
||||||
Struct {
|
let mut generated_name = format!("__wasm_bindgen_generated");
|
||||||
name: s.ident,
|
if let Some(class) = self.class {
|
||||||
methods: Vec::new(),
|
generated_name.push_str("_");
|
||||||
functions: Vec::new(),
|
generated_name.push_str(class.as_ref());
|
||||||
}
|
}
|
||||||
|
generated_name.push_str("_");
|
||||||
|
generated_name.push_str(self.function.name.as_ref());
|
||||||
|
syn::Ident::from(generated_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free_function(&self) -> syn::Ident {
|
pub fn export_name(&self) -> syn::LitStr {
|
||||||
syn::Ident::from(shared::free_function(self.name.as_ref()))
|
let name = match self.class {
|
||||||
}
|
Some(class) => {
|
||||||
|
shared::struct_function_export_name(
|
||||||
pub fn push_item(&mut self, item: &syn::ImplItem) {
|
class.as_ref(),
|
||||||
let method = match *item {
|
self.function.name.as_ref(),
|
||||||
syn::ImplItem::Const(_) => panic!("const definitions aren't supported"),
|
)
|
||||||
syn::ImplItem::Type(_) => panic!("type definitions in impls aren't supported"),
|
|
||||||
syn::ImplItem::Method(ref m) => m,
|
|
||||||
syn::ImplItem::Macro(_) => panic!("macros in impls aren't supported"),
|
|
||||||
syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
|
|
||||||
};
|
|
||||||
match method.vis {
|
|
||||||
syn::Visibility::Public(_) => {}
|
|
||||||
_ => return,
|
|
||||||
}
|
|
||||||
if method.defaultness.is_some() {
|
|
||||||
panic!("default methods are not supported");
|
|
||||||
}
|
|
||||||
if method.sig.constness.is_some() {
|
|
||||||
panic!("can only bindgen non-const functions");
|
|
||||||
}
|
|
||||||
if method.sig.unsafety.is_some() {
|
|
||||||
panic!("can only bindgen safe functions");
|
|
||||||
}
|
|
||||||
|
|
||||||
let (function, mutable) = Function::from_decl(method.sig.ident,
|
|
||||||
&method.sig.decl,
|
|
||||||
true);
|
|
||||||
match mutable {
|
|
||||||
Some(mutable) => {
|
|
||||||
self.methods.push(Method { mutable, function });
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.functions.push(function);
|
shared::free_function_export_name(self.function.name.as_ref())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
syn::LitStr::new(&name, Span::def_site())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||||
a.fields(&[
|
a.fields(&[
|
||||||
("name", &|a| a.str(self.name.as_ref())),
|
("class", &|a| {
|
||||||
("functions", &|a| a.list(&self.functions, Function::wbg_literal)),
|
match self.class {
|
||||||
("methods", &|a| a.list(&self.methods, Method::wbg_literal)),
|
Some(ref s) => a.str(s.as_ref()),
|
||||||
]);
|
None => a.append("null"),
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
|
("method", &|a| a.bool(self.method)),
|
||||||
impl Method {
|
|
||||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
|
||||||
a.fields(&[
|
|
||||||
("mutable", &|a| a.bool(self.mutable)),
|
|
||||||
("function", &|a| self.function.wbg_literal(a)),
|
("function", &|a| self.function.wbg_literal(a)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImportStruct {
|
impl Import {
|
||||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||||
|
let mut method = false;
|
||||||
|
let mut js_new = false;
|
||||||
|
let mut statik = false;
|
||||||
|
let mut class_name = None;
|
||||||
|
match self.kind {
|
||||||
|
ImportKind::Method { ref class, .. } => {
|
||||||
|
method = true;
|
||||||
|
class_name = Some(class);
|
||||||
|
}
|
||||||
|
ImportKind::JsConstructor { ref class, .. } => {
|
||||||
|
js_new = true;
|
||||||
|
class_name = Some(class);
|
||||||
|
}
|
||||||
|
ImportKind::Static { ref class, .. } => {
|
||||||
|
statik = true;
|
||||||
|
class_name = Some(class);
|
||||||
|
}
|
||||||
|
ImportKind::Normal => {}
|
||||||
|
}
|
||||||
a.fields(&[
|
a.fields(&[
|
||||||
("module", &|a| {
|
("module", &|a| {
|
||||||
match self.module {
|
match self.module {
|
||||||
@ -464,99 +525,27 @@ impl ImportStruct {
|
|||||||
None => a.append("null"),
|
None => a.append("null"),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
("name", &|a| a.str(self.name.as_ref())),
|
("catch", &|a| a.bool(self.function.opts.catch())),
|
||||||
("functions", &|a| {
|
|
||||||
a.list(&self.functions,
|
|
||||||
|f, a| {
|
|
||||||
let (method, new) = match f.kind {
|
|
||||||
ImportFunctionKind::Method => (true, false),
|
|
||||||
ImportFunctionKind::JsConstructor => (false, true),
|
|
||||||
ImportFunctionKind::Static => (false, false),
|
|
||||||
};
|
|
||||||
a.fields(&[
|
|
||||||
("method", &|a| a.bool(method)),
|
("method", &|a| a.bool(method)),
|
||||||
("js_new", &|a| a.bool(new)),
|
("js_new", &|a| a.bool(js_new)),
|
||||||
("catch", &|a| a.bool(f.function.catch)),
|
("statik", &|a| a.bool(statik)),
|
||||||
("function", &|a| f.function.wasm_function.wbg_literal(a)),
|
("function", &|a| self.function.wbg_literal(a)),
|
||||||
]);
|
("class", &|a| {
|
||||||
})
|
match class_name {
|
||||||
|
Some(s) => a.str(s),
|
||||||
|
None => a.append("null"),
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Import {
|
impl Struct {
|
||||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
fn from(s: syn::ItemStruct, _opts: BindgenAttrs) -> Struct {
|
||||||
a.fields(&[
|
Struct { name: s.ident }
|
||||||
("module", &|a| a.str(&self.module)),
|
|
||||||
("catch", &|a| a.bool(self.function.catch)),
|
|
||||||
("function", &|a| self.function.wasm_function.wbg_literal(a)),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct File {
|
|
||||||
pub items: Vec<MyItem>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl syn::synom::Synom for File {
|
|
||||||
named!(parse -> Self, map!(many0!(syn!(MyItem)), |items| File { items }));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum MyItem {
|
|
||||||
Normal(syn::Item),
|
|
||||||
ExternClass(ExternClass),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl syn::synom::Synom for MyItem {
|
|
||||||
named!(parse -> Self, alt!(
|
|
||||||
syn!(syn::Item) => { MyItem::Normal }
|
|
||||||
|
|
|
||||||
syn!(ExternClass) => { MyItem::ExternClass }
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ExternClass {
|
|
||||||
name: syn::Ident,
|
|
||||||
module: Option<syn::LitStr>,
|
|
||||||
functions: Vec<syn::ForeignItem>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl syn::synom::Synom for ExternClass {
|
|
||||||
named!(parse -> Self, do_parse!(
|
|
||||||
module: option!(do_parse!(
|
|
||||||
punct!(#) >>
|
|
||||||
name: brackets!(do_parse!(
|
|
||||||
call!(term, "wasm_module") >>
|
|
||||||
punct!(=) >>
|
|
||||||
val: syn!(syn::LitStr) >>
|
|
||||||
(val)
|
|
||||||
)) >>
|
|
||||||
(name.1)
|
|
||||||
)) >>
|
|
||||||
keyword!(extern) >>
|
|
||||||
keyword!(struct) >>
|
|
||||||
name: syn!(syn::Ident) >>
|
|
||||||
items: braces!(many0!(syn!(syn::ForeignItem))) >>
|
|
||||||
(ExternClass {
|
|
||||||
name,
|
|
||||||
module,
|
|
||||||
functions: items.1,
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str)
|
|
||||||
-> syn::synom::PResult<'a, ()>
|
|
||||||
{
|
|
||||||
if let Some((_span, term, next)) = cursor.term() {
|
|
||||||
if term.as_str() == name {
|
|
||||||
return Ok(((), next))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syn::parse_error()
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LiteralBuilder<'a> {
|
struct LiteralBuilder<'a> {
|
||||||
dst: &'a mut Tokens,
|
dst: &'a mut Tokens,
|
||||||
cnt: usize,
|
cnt: usize,
|
||||||
@ -634,51 +623,124 @@ impl<'a> LiteralBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct BindgenOpts {
|
pub struct BindgenAttrs {
|
||||||
catch: bool,
|
attrs: Vec<BindgenAttr>,
|
||||||
constructor: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BindgenOpts {
|
impl BindgenAttrs {
|
||||||
fn from(attrs: &[syn::Attribute]) -> BindgenOpts {
|
pub fn find(attrs: &mut Vec<syn::Attribute>) -> BindgenAttrs {
|
||||||
let mut opts = BindgenOpts::default();
|
let pos = attrs.iter()
|
||||||
let attrs = attrs.iter()
|
.enumerate()
|
||||||
.filter_map(|a| a.interpret_meta())
|
.find(|&(_, ref m)| m.path.segments[0].ident == "wasm_bindgen")
|
||||||
.filter_map(|m| {
|
.map(|a| a.0);
|
||||||
match m {
|
let pos = match pos {
|
||||||
syn::Meta::List(i) => {
|
Some(i) => i,
|
||||||
if i.ident == "wasm_bindgen" {
|
None => return BindgenAttrs::default(),
|
||||||
Some(i.nested)
|
};
|
||||||
} else {
|
syn::parse(attrs.remove(pos).tts.into())
|
||||||
None
|
.expect("malformed #[wasm_bindgen] attribute")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn module(&self) -> Option<&str> {
|
||||||
|
self.attrs.iter()
|
||||||
|
.filter_map(|a| {
|
||||||
|
match *a {
|
||||||
|
BindgenAttr::Module(ref s) => Some(&s[..]),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.flat_map(|a| a)
|
.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn catch(&self) -> bool {
|
||||||
|
self.attrs.iter()
|
||||||
|
.any(|a| {
|
||||||
|
match *a {
|
||||||
|
BindgenAttr::Catch => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constructor(&self) -> bool {
|
||||||
|
self.attrs.iter()
|
||||||
|
.any(|a| {
|
||||||
|
match *a {
|
||||||
|
BindgenAttr::Constructor => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method(&self) -> bool {
|
||||||
|
self.attrs.iter()
|
||||||
|
.any(|a| {
|
||||||
|
match *a {
|
||||||
|
BindgenAttr::Method => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn static_receiver(&self) -> Option<&syn::Type> {
|
||||||
|
self.attrs.iter()
|
||||||
.filter_map(|a| {
|
.filter_map(|a| {
|
||||||
match a {
|
match *a {
|
||||||
syn::NestedMeta::Meta(a) => Some(a),
|
BindgenAttr::Static(ref s) => Some(s),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
for attr in attrs {
|
.next()
|
||||||
match attr {
|
|
||||||
syn::Meta::Word(a) => {
|
|
||||||
if a == "constructor" {
|
|
||||||
opts.constructor = true;
|
|
||||||
} else if a == "catch" {
|
|
||||||
opts.catch = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return opts
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl syn::synom::Synom for BindgenAttrs {
|
||||||
|
named!(parse -> Self, alt!(
|
||||||
|
do_parse!(
|
||||||
|
opts: parens!(call!(
|
||||||
|
syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated
|
||||||
|
)) >>
|
||||||
|
(BindgenAttrs {
|
||||||
|
attrs: opts.1.into_iter().collect(),
|
||||||
|
})
|
||||||
|
) => { |s| s }
|
||||||
|
|
|
||||||
|
epsilon!() => { |_| BindgenAttrs { attrs: Vec::new() } }
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BindgenAttr {
|
||||||
|
Catch,
|
||||||
|
Constructor,
|
||||||
|
Method,
|
||||||
|
Static(syn::Type),
|
||||||
|
Module(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl syn::synom::Synom for BindgenAttr {
|
||||||
|
named!(parse -> Self, alt!(
|
||||||
|
call!(term, "catch") => { |_| BindgenAttr::Catch }
|
||||||
|
|
|
||||||
|
call!(term, "constructor") => { |_| BindgenAttr::Constructor }
|
||||||
|
|
|
||||||
|
call!(term, "method") => { |_| BindgenAttr::Method }
|
||||||
|
|
|
||||||
|
do_parse!(
|
||||||
|
call!(term, "static") >>
|
||||||
|
punct!(=) >>
|
||||||
|
s: syn!(syn::Type) >>
|
||||||
|
(s)
|
||||||
|
)=> { BindgenAttr::Static }
|
||||||
|
|
|
||||||
|
do_parse!(
|
||||||
|
call!(term, "module") >>
|
||||||
|
punct!(=) >>
|
||||||
|
s: syn!(syn::LitStr) >>
|
||||||
|
(s.value())
|
||||||
|
)=> { BindgenAttr::Module }
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> {
|
fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> {
|
||||||
let ty = match ty {
|
let ty = match ty {
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
@ -707,3 +769,14 @@ fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> {
|
|||||||
}
|
}
|
||||||
Some(Some(Type::from(ty)))
|
Some(Some(Type::from(ty)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str)
|
||||||
|
-> syn::synom::PResult<'a, ()>
|
||||||
|
{
|
||||||
|
if let Some((_span, term, next)) = cursor.term() {
|
||||||
|
if term.as_str() == name {
|
||||||
|
return Ok(((), next))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syn::parse_error()
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ extern crate wasm_bindgen_shared as shared;
|
|||||||
use std::sync::atomic::*;
|
use std::sync::atomic::*;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::{Span, TokenNode, Delimiter, TokenTree};
|
use proc_macro2::Span;
|
||||||
use quote::{Tokens, ToTokens};
|
use quote::{Tokens, ToTokens};
|
||||||
|
|
||||||
macro_rules! my_quote {
|
macro_rules! my_quote {
|
||||||
@ -22,75 +22,41 @@ macro_rules! my_quote {
|
|||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro_attribute]
|
||||||
pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
// Parse the input as a list of Rust items, reusing the `syn::File` parser.
|
let item = syn::parse::<syn::Item>(input.clone())
|
||||||
let file = syn::parse::<ast::File>(input)
|
.expect("expected a valid Rust item");
|
||||||
.expect("expected a set of valid Rust items");
|
let opts = syn::parse::<ast::BindgenAttrs>(attr)
|
||||||
|
.expect("invalid arguments to #[wasm_bindgen]");
|
||||||
|
|
||||||
let mut ret = Tokens::new();
|
let mut ret = Tokens::new();
|
||||||
|
let mut program = ast::Program::default();
|
||||||
|
program.push_item(item, Some(opts), &mut ret);
|
||||||
|
generate_wrappers(program, &mut ret);
|
||||||
|
|
||||||
let mut program = ast::Program {
|
// println!("{}", ret);
|
||||||
structs: Vec::new(),
|
|
||||||
free_functions: Vec::new(),
|
|
||||||
imports: Vec::new(),
|
|
||||||
imported_structs: Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Translate all input items into our own internal representation (the `ast`
|
ret.into()
|
||||||
// module). We'll be panicking here on anything that we can't process
|
}
|
||||||
|
|
||||||
for item in file.items.iter() {
|
// Generate wrappers for all the items that we've found
|
||||||
let item = match *item {
|
fn generate_wrappers(program: ast::Program, tokens: &mut Tokens) {
|
||||||
ast::MyItem::ExternClass(ref c) => {
|
for export in program.exports.iter() {
|
||||||
program.push_extern_class(c);
|
bindgen_export(export, tokens);
|
||||||
continue
|
|
||||||
}
|
|
||||||
ast::MyItem::Normal(ref item) => item,
|
|
||||||
};
|
|
||||||
|
|
||||||
match *item {
|
|
||||||
syn::Item::Fn(ref f) => {
|
|
||||||
item.to_tokens(&mut ret);
|
|
||||||
program.free_functions.push(ast::Function::from(f));
|
|
||||||
}
|
|
||||||
syn::Item::Struct(ref s) => {
|
|
||||||
item.to_tokens(&mut ret);
|
|
||||||
let s = ast::Struct::from(s);
|
|
||||||
if program.structs.iter().any(|a| a.name == s.name) {
|
|
||||||
panic!("redefinition of struct: {}", s.name);
|
|
||||||
}
|
|
||||||
program.structs.push(s);
|
|
||||||
}
|
|
||||||
syn::Item::Impl(ref i) => {
|
|
||||||
item.to_tokens(&mut ret);
|
|
||||||
program.push_impl(i);
|
|
||||||
}
|
|
||||||
syn::Item::ForeignMod(ref f) => {
|
|
||||||
program.push_foreign_mod(f);
|
|
||||||
}
|
|
||||||
_ => panic!("unexpected item in bindgen macro"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate wrappers for all the items that we've found
|
|
||||||
|
|
||||||
for function in program.free_functions.iter() {
|
|
||||||
bindgen_fn(function, &mut ret);
|
|
||||||
}
|
}
|
||||||
for s in program.structs.iter() {
|
for s in program.structs.iter() {
|
||||||
bindgen_struct(s, &mut ret);
|
bindgen_struct(s, tokens);
|
||||||
}
|
}
|
||||||
for i in program.imports.iter() {
|
for i in program.imports.iter() {
|
||||||
bindgen_import(i, &mut ret);
|
bindgen_import(i, tokens);
|
||||||
}
|
}
|
||||||
for i in program.imported_structs.iter() {
|
for &(ref vis, ref t) in program.imported_types.iter() {
|
||||||
bindgen_imported_struct(i, &mut ret);
|
bindgen_imported_type(vis, t, tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally generate a static which will eventually be what lives in a custom
|
// Generate a static which will eventually be what lives in a custom section
|
||||||
// section of the wasm executable. For now it's just a plain old static, but
|
// of the wasm executable. For now it's just a plain old static, but we'll
|
||||||
// we'll eventually have it actually in its own section.
|
// eventually have it actually in its own section.
|
||||||
|
|
||||||
static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
|
static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||||
let generated_static_name = format!("__WASM_BINDGEN_GENERATED{}",
|
let generated_static_name = format!("__WASM_BINDGEN_GENERATED{}",
|
||||||
@ -104,32 +70,12 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
|||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub static #generated_static_name: [u32; #generated_static_length] =
|
pub static #generated_static_name: [u32; #generated_static_length] =
|
||||||
[#generated_static_value];
|
[#generated_static_value];
|
||||||
}).to_tokens(&mut ret);
|
}).to_tokens(tokens);
|
||||||
|
|
||||||
// println!("{}", ret);
|
|
||||||
|
|
||||||
ret.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bindgen_fn(function: &ast::Function, into: &mut Tokens) {
|
|
||||||
bindgen(&function.free_function_export_name(),
|
|
||||||
function.rust_symbol(None),
|
|
||||||
Receiver::FreeFunction(function.name),
|
|
||||||
&function.arguments,
|
|
||||||
function.ret.as_ref(),
|
|
||||||
into)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bindgen_struct(s: &ast::Struct, into: &mut Tokens) {
|
fn bindgen_struct(s: &ast::Struct, into: &mut Tokens) {
|
||||||
for f in s.functions.iter() {
|
|
||||||
bindgen_struct_fn(s, f, into);
|
|
||||||
}
|
|
||||||
for f in s.methods.iter() {
|
|
||||||
bindgen_struct_method(s, f, into);
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = &s.name;
|
let name = &s.name;
|
||||||
let free_fn = s.free_function();
|
let free_fn = syn::Ident::from(shared::free_function(s.name.as_ref()));
|
||||||
let c = shared::name_to_descriptor(name.as_ref()) as u32;
|
let c = shared::name_to_descriptor(name.as_ref()) as u32;
|
||||||
(my_quote! {
|
(my_quote! {
|
||||||
impl ::wasm_bindgen::convert::WasmBoundary for #name {
|
impl ::wasm_bindgen::convert::WasmBoundary for #name {
|
||||||
@ -175,43 +121,17 @@ fn bindgen_struct(s: &ast::Struct, into: &mut Tokens) {
|
|||||||
}).to_tokens(into);
|
}).to_tokens(into);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bindgen_struct_fn(s: &ast::Struct, f: &ast::Function, into: &mut Tokens) {
|
fn bindgen_export(export: &ast::Export, into: &mut Tokens) {
|
||||||
bindgen(&f.struct_function_export_name(s.name),
|
let generated_name = export.rust_symbol();
|
||||||
f.rust_symbol(Some(s.name)),
|
let export_name = export.export_name();
|
||||||
Receiver::StructFunction(s.name, f.name),
|
|
||||||
&f.arguments,
|
|
||||||
f.ret.as_ref(),
|
|
||||||
into)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bindgen_struct_method(s: &ast::Struct, m: &ast::Method, into: &mut Tokens) {
|
|
||||||
bindgen(&m.function.struct_function_export_name(s.name),
|
|
||||||
m.function.rust_symbol(Some(s.name)),
|
|
||||||
Receiver::StructMethod(s.name, m.mutable, m.function.name),
|
|
||||||
&m.function.arguments,
|
|
||||||
m.function.ret.as_ref(),
|
|
||||||
into)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Receiver {
|
|
||||||
FreeFunction(syn::Ident),
|
|
||||||
StructFunction(syn::Ident, syn::Ident),
|
|
||||||
StructMethod(syn::Ident, bool, syn::Ident),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bindgen(export_name: &syn::LitStr,
|
|
||||||
generated_name: syn::Ident,
|
|
||||||
receiver: Receiver,
|
|
||||||
arguments: &[ast::Type],
|
|
||||||
ret_type: Option<&ast::Type>,
|
|
||||||
into: &mut Tokens) {
|
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
let mut arg_conversions = vec![];
|
let mut arg_conversions = vec![];
|
||||||
let mut converted_arguments = vec![];
|
let mut converted_arguments = vec![];
|
||||||
let ret = syn::Ident::from("_ret");
|
let ret = syn::Ident::from("_ret");
|
||||||
|
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
if let Receiver::StructMethod(class, _, _) = receiver {
|
if export.method {
|
||||||
|
let class = export.class.unwrap();
|
||||||
args.push(my_quote! { me: *mut ::wasm_bindgen::__rt::WasmRefCell<#class> });
|
args.push(my_quote! { me: *mut ::wasm_bindgen::__rt::WasmRefCell<#class> });
|
||||||
arg_conversions.push(my_quote! {
|
arg_conversions.push(my_quote! {
|
||||||
::wasm_bindgen::__rt::assert_not_null(me);
|
::wasm_bindgen::__rt::assert_not_null(me);
|
||||||
@ -220,7 +140,7 @@ fn bindgen(export_name: &syn::LitStr,
|
|||||||
offset = 1;
|
offset = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, ty) in arguments.iter().enumerate() {
|
for (i, ty) in export.function.arguments.iter().enumerate() {
|
||||||
let i = i + offset;
|
let i = i + offset;
|
||||||
let ident = syn::Ident::from(format!("arg{}", i));
|
let ident = syn::Ident::from(format!("arg{}", i));
|
||||||
match *ty {
|
match *ty {
|
||||||
@ -288,12 +208,12 @@ fn bindgen(export_name: &syn::LitStr,
|
|||||||
}
|
}
|
||||||
let ret_ty;
|
let ret_ty;
|
||||||
let convert_ret;
|
let convert_ret;
|
||||||
match ret_type {
|
match export.function.ret {
|
||||||
Some(&ast::Type::String) => {
|
Some(ast::Type::String) => {
|
||||||
ret_ty = my_quote! { -> *mut String };
|
ret_ty = my_quote! { -> *mut String };
|
||||||
convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) };
|
convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) };
|
||||||
}
|
}
|
||||||
Some(&ast::Type::ByValue(ref t)) => {
|
Some(ast::Type::ByValue(ref t)) => {
|
||||||
ret_ty = my_quote! {
|
ret_ty = my_quote! {
|
||||||
-> <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
-> <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||||
};
|
};
|
||||||
@ -301,9 +221,9 @@ fn bindgen(export_name: &syn::LitStr,
|
|||||||
<#t as ::wasm_bindgen::convert::WasmBoundary>::into_js(#ret)
|
<#t as ::wasm_bindgen::convert::WasmBoundary>::into_js(#ret)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Some(&ast::Type::BorrowedStr) |
|
Some(ast::Type::BorrowedStr) |
|
||||||
Some(&ast::Type::ByMutRef(_)) |
|
Some(ast::Type::ByMutRef(_)) |
|
||||||
Some(&ast::Type::ByRef(_)) => {
|
Some(ast::Type::ByRef(_)) => {
|
||||||
panic!("can't return a borrowed ref");
|
panic!("can't return a borrowed ref");
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@ -312,6 +232,19 @@ fn bindgen(export_name: &syn::LitStr,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let name = export.function.name;
|
||||||
|
let receiver = match export.class {
|
||||||
|
Some(_) if export.method => {
|
||||||
|
if export.mutable {
|
||||||
|
my_quote! { me.borrow_mut().#name }
|
||||||
|
} else {
|
||||||
|
my_quote! { me.borrow().#name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(class) => my_quote! { #class::#name },
|
||||||
|
None => my_quote!{ #name },
|
||||||
|
};
|
||||||
|
|
||||||
let tokens = my_quote! {
|
let tokens = my_quote! {
|
||||||
#[export_name = #export_name]
|
#[export_name = #export_name]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
@ -324,65 +257,14 @@ fn bindgen(export_name: &syn::LitStr,
|
|||||||
tokens.to_tokens(into);
|
tokens.to_tokens(into);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for Receiver {
|
fn bindgen_imported_type(vis: &syn::Visibility,
|
||||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
name: &syn::Ident,
|
||||||
match *self {
|
tokens: &mut Tokens) {
|
||||||
Receiver::FreeFunction(name) => name.to_tokens(tokens),
|
|
||||||
Receiver::StructFunction(s, name) => {
|
|
||||||
s.to_tokens(tokens);
|
|
||||||
syn::token::Colon2::default().to_tokens(tokens);
|
|
||||||
name.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
Receiver::StructMethod(_, mutable, name) => {
|
|
||||||
(my_quote! { me }).to_tokens(tokens);
|
|
||||||
syn::token::Dot::default().to_tokens(tokens);
|
|
||||||
if mutable {
|
|
||||||
syn::Ident::from("borrow_mut").to_tokens(tokens);
|
|
||||||
} else {
|
|
||||||
syn::Ident::from("borrow").to_tokens(tokens);
|
|
||||||
}
|
|
||||||
tokens.append(TokenTree {
|
|
||||||
span: Span::def_site(),
|
|
||||||
kind: TokenNode::Group(Delimiter::Parenthesis,
|
|
||||||
proc_macro2::TokenStream::empty()),
|
|
||||||
});
|
|
||||||
syn::token::Dot::default().to_tokens(tokens);
|
|
||||||
name.to_tokens(tokens);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
|
||||||
let import_name = shared::mangled_import_name(
|
|
||||||
None,
|
|
||||||
import.function.wasm_function.name.as_ref(),
|
|
||||||
);
|
|
||||||
bindgen_import_function(&import.function, &import_name, tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bindgen_imported_struct(import: &ast::ImportStruct, tokens: &mut Tokens) {
|
|
||||||
let name = import.name;
|
|
||||||
|
|
||||||
let mut methods = Tokens::new();
|
|
||||||
|
|
||||||
for f in import.functions.iter() {
|
|
||||||
let import_name = shared::mangled_import_name(
|
|
||||||
Some(&import.name.to_string()),
|
|
||||||
f.function.wasm_function.name.as_ref(),
|
|
||||||
);
|
|
||||||
bindgen_import_function(&f.function, &import_name, &mut methods);
|
|
||||||
}
|
|
||||||
|
|
||||||
(my_quote! {
|
(my_quote! {
|
||||||
pub struct #name {
|
#vis struct #name {
|
||||||
obj: ::wasm_bindgen::JsValue,
|
obj: ::wasm_bindgen::JsValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl #name {
|
|
||||||
#methods
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::wasm_bindgen::convert::WasmBoundary for #name {
|
impl ::wasm_bindgen::convert::WasmBoundary for #name {
|
||||||
type Js = <::wasm_bindgen::JsValue as
|
type Js = <::wasm_bindgen::JsValue as
|
||||||
::wasm_bindgen::convert::WasmBoundary>::Js;
|
::wasm_bindgen::convert::WasmBoundary>::Js;
|
||||||
@ -397,42 +279,50 @@ fn bindgen_imported_struct(import: &ast::ImportStruct, tokens: &mut Tokens) {
|
|||||||
#name { obj: ::wasm_bindgen::JsValue::from_js(js) }
|
#name { obj: ::wasm_bindgen::JsValue::from_js(js) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::wasm_bindgen::convert::ToRefWasmBoundary for #name {
|
||||||
|
fn to_js_ref(&self) -> u32 {
|
||||||
|
self.obj.to_js_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
}).to_tokens(tokens);
|
}).to_tokens(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bindgen_import_function(import: &ast::ImportFunction,
|
|
||||||
import_name: &str,
|
fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
||||||
tokens: &mut Tokens) {
|
let mut class_ty = None;
|
||||||
let vis = &import.rust_vis;
|
let mut is_method = false;
|
||||||
let ret = &import.rust_decl.output;
|
let mut class_name = None;
|
||||||
let fn_token = &import.rust_decl.fn_token;
|
match import.kind {
|
||||||
let arguments = &import.rust_decl.inputs;
|
ast::ImportKind::Method { ref ty, ref class } => {
|
||||||
|
is_method = true;
|
||||||
|
class_ty = Some(ty);
|
||||||
|
class_name = Some(class);
|
||||||
|
}
|
||||||
|
ast::ImportKind::Static { ref ty, ref class } |
|
||||||
|
ast::ImportKind::JsConstructor { ref ty, ref class } => {
|
||||||
|
class_ty = Some(ty);
|
||||||
|
class_name = Some(class);
|
||||||
|
}
|
||||||
|
ast::ImportKind::Normal => {}
|
||||||
|
}
|
||||||
|
let import_name = shared::mangled_import_name(
|
||||||
|
class_name.map(|s| &**s),
|
||||||
|
import.function.name.as_ref(),
|
||||||
|
);
|
||||||
|
let vis = &import.function.rust_vis;
|
||||||
|
let ret = &import.function.rust_decl.output;
|
||||||
|
let fn_token = &import.function.rust_decl.fn_token;
|
||||||
|
|
||||||
let mut abi_argument_names = Vec::new();
|
let mut abi_argument_names = Vec::new();
|
||||||
let mut abi_arguments = Vec::new();
|
let mut abi_arguments = Vec::new();
|
||||||
let mut arg_conversions = Vec::new();
|
let mut arg_conversions = Vec::new();
|
||||||
let ret_ident = syn::Ident::from("_ret");
|
let ret_ident = syn::Ident::from("_ret");
|
||||||
|
|
||||||
let inputs = import.rust_decl.inputs.iter().collect::<Vec<_>>();
|
let names = import.function.rust_decl.inputs
|
||||||
let (is_method, inputs) = match inputs.get(0) {
|
|
||||||
Some(&&syn::FnArg::Captured(_)) => (false, &inputs[..]),
|
|
||||||
Some(_) => (true, &inputs[1..]),
|
|
||||||
None => (false, &inputs[..]),
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_method {
|
|
||||||
let ptr = syn::Ident::from("ptr");
|
|
||||||
abi_argument_names.push(ptr);
|
|
||||||
abi_arguments.push(my_quote! { #ptr: u32 });
|
|
||||||
arg_conversions.push(my_quote! {
|
|
||||||
let #ptr = ::wasm_bindgen::convert::ToRefWasmBoundary::to_js_ref(&self.obj);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let names = inputs
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| {
|
.map(|arg| {
|
||||||
match **arg {
|
match *arg {
|
||||||
syn::FnArg::Captured(ref c) => c,
|
syn::FnArg::Captured(ref c) => c,
|
||||||
_ => panic!("arguments cannot be `self` or ignored"),
|
_ => panic!("arguments cannot be `self` or ignored"),
|
||||||
}
|
}
|
||||||
@ -451,7 +341,7 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (ty, name) in import.wasm_function.arguments.iter().zip(names) {
|
for (i, (ty, name)) in import.function.arguments.iter().zip(names).enumerate() {
|
||||||
match *ty {
|
match *ty {
|
||||||
ast::Type::BorrowedStr => {
|
ast::Type::BorrowedStr => {
|
||||||
let ptr = syn::Ident::from(format!("{}_ptr", name));
|
let ptr = syn::Ident::from(format!("{}_ptr", name));
|
||||||
@ -470,20 +360,34 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
|||||||
abi_arguments.push(my_quote! {
|
abi_arguments.push(my_quote! {
|
||||||
#name: <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
#name: <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||||
});
|
});
|
||||||
|
if i == 0 && is_method {
|
||||||
|
arg_conversions.push(my_quote! {
|
||||||
|
let #name = <#t as ::wasm_bindgen::convert::WasmBoundary>
|
||||||
|
::into_js(self);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
arg_conversions.push(my_quote! {
|
arg_conversions.push(my_quote! {
|
||||||
let #name = <#t as ::wasm_bindgen::convert::WasmBoundary>
|
let #name = <#t as ::wasm_bindgen::convert::WasmBoundary>
|
||||||
::into_js(#name);
|
::into_js(#name);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ast::Type::ByMutRef(_) => panic!("urgh mut"),
|
ast::Type::ByMutRef(_) => panic!("urgh mut"),
|
||||||
ast::Type::ByRef(ref t) => {
|
ast::Type::ByRef(ref t) => {
|
||||||
abi_argument_names.push(name);
|
abi_argument_names.push(name);
|
||||||
abi_arguments.push(my_quote! { #name: u32 });
|
abi_arguments.push(my_quote! { #name: u32 });
|
||||||
|
if i == 0 && is_method {
|
||||||
|
arg_conversions.push(my_quote! {
|
||||||
|
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
|
||||||
|
::to_js_ref(self);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
arg_conversions.push(my_quote! {
|
arg_conversions.push(my_quote! {
|
||||||
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
|
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
|
||||||
::to_js_ref(#name);
|
::to_js_ref(#name);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// TODO: need to test this
|
// TODO: need to test this
|
||||||
ast::Type::String => {
|
ast::Type::String => {
|
||||||
let ptr = syn::Ident::from(format!("{}_ptr", name));
|
let ptr = syn::Ident::from(format!("{}_ptr", name));
|
||||||
@ -502,7 +406,7 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
|||||||
}
|
}
|
||||||
let abi_ret;
|
let abi_ret;
|
||||||
let mut convert_ret;
|
let mut convert_ret;
|
||||||
match import.wasm_function.ret {
|
match import.function.ret {
|
||||||
Some(ast::Type::ByValue(ref t)) => {
|
Some(ast::Type::ByValue(ref t)) => {
|
||||||
abi_ret = my_quote! {
|
abi_ret = my_quote! {
|
||||||
<#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
<#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||||
@ -539,7 +443,7 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut exceptional_ret = my_quote! {};
|
let mut exceptional_ret = my_quote! {};
|
||||||
if import.catch {
|
if import.function.opts.catch() {
|
||||||
let exn_data = syn::Ident::from("exn_data");
|
let exn_data = syn::Ident::from("exn_data");
|
||||||
let exn_data_ptr = syn::Ident::from("exn_data_ptr");
|
let exn_data_ptr = syn::Ident::from("exn_data_ptr");
|
||||||
abi_argument_names.push(exn_data_ptr);
|
abi_argument_names.push(exn_data_ptr);
|
||||||
@ -557,10 +461,24 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = import.ident;
|
let name = import.function.name;
|
||||||
let import_name = syn::Ident::from(import_name);
|
let import_name = syn::Ident::from(import_name);
|
||||||
(quote! {
|
let attrs = &import.function.rust_attrs;
|
||||||
#vis #fn_token #name(#arguments) #ret {
|
|
||||||
|
let arguments = import.function.rust_decl.inputs
|
||||||
|
.iter()
|
||||||
|
.skip(if is_method { 1 } else { 0 })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let me = if is_method {
|
||||||
|
my_quote! { &self, }
|
||||||
|
} else {
|
||||||
|
quote!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let invocation = quote! {
|
||||||
|
#(#attrs)*
|
||||||
|
#vis extern #fn_token #name(#me #(#arguments),*) #ret {
|
||||||
extern {
|
extern {
|
||||||
fn #import_name(#(#abi_arguments),*) -> #abi_ret;
|
fn #import_name(#(#abi_arguments),*) -> #abi_ret;
|
||||||
}
|
}
|
||||||
@ -571,5 +489,15 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
|||||||
#convert_ret
|
#convert_ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(class) = class_ty {
|
||||||
|
(quote! {
|
||||||
|
impl #class {
|
||||||
|
#invocation
|
||||||
|
}
|
||||||
}).to_tokens(tokens);
|
}).to_tokens(tokens);
|
||||||
|
} else {
|
||||||
|
invocation.to_tokens(tokens);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,58 +5,39 @@ extern crate fnv;
|
|||||||
use std::char;
|
use std::char;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
pub structs: Vec<Struct>,
|
pub exports: Vec<Export>,
|
||||||
pub free_functions: Vec<Function>,
|
|
||||||
pub imports: Vec<Import>,
|
pub imports: Vec<Import>,
|
||||||
pub imported_structs: Vec<ImportStruct>,
|
|
||||||
pub custom_type_names: Vec<CustomTypeName>,
|
pub custom_type_names: Vec<CustomTypeName>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Struct {
|
|
||||||
pub name: String,
|
|
||||||
pub functions: Vec<Function>,
|
|
||||||
pub methods: Vec<Method>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Import {
|
pub struct Import {
|
||||||
pub module: String,
|
|
||||||
pub catch: bool,
|
|
||||||
pub function: Function,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct ImportStruct {
|
|
||||||
pub module: Option<String>,
|
pub module: Option<String>,
|
||||||
pub name: String,
|
pub catch: bool,
|
||||||
pub functions: Vec<ImportStructFunction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct ImportStructFunction {
|
|
||||||
pub method: bool,
|
pub method: bool,
|
||||||
pub js_new: bool,
|
pub js_new: bool,
|
||||||
pub catch: bool,
|
pub statik: bool,
|
||||||
|
pub class: Option<String>,
|
||||||
pub function: Function,
|
pub function: Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Method {
|
pub struct Export {
|
||||||
pub mutable: bool,
|
pub class: Option<String>,
|
||||||
|
pub method: bool,
|
||||||
pub function: Function,
|
pub function: Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub arguments: Vec<Type>,
|
pub arguments: Vec<Type>,
|
||||||
pub ret: Option<Type>,
|
pub ret: Option<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct CustomTypeName {
|
pub struct CustomTypeName {
|
||||||
pub descriptor: char,
|
pub descriptor: char,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
//! Runtime support for the `wasm-bindgen` tool
|
//! Runtime support for the `wasm-bindgen` tool
|
||||||
//!
|
//!
|
||||||
//! This crate contains the runtime support necessary for `wasm-bindgen` the
|
//! This crate contains the runtime support necessary for `wasm-bindgen` the
|
||||||
//! macro and tool. Crates pull in the `wasm_bindgen!` macro through this crate
|
//! attribute and tool. Crates pull in the `#[wasm_bindgen]` attribute through
|
||||||
//! and this crate also provides JS bindings through the `JsValue` interface.
|
//! this crate and this crate also provides JS bindings through the `JsValue`
|
||||||
|
//! interface.
|
||||||
|
|
||||||
#![feature(use_extern_macros)]
|
#![feature(use_extern_macros)]
|
||||||
|
|
||||||
|
62
tests/api.rs
62
tests/api.rs
@ -10,33 +10,46 @@ fn works() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub fn foo() -> JsValue {
|
#[no_mangle]
|
||||||
|
pub extern fn foo() -> JsValue {
|
||||||
JsValue::from("foo")
|
JsValue::from("foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bar(s: &str) -> JsValue {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bar(s: &str) -> JsValue {
|
||||||
JsValue::from(s)
|
JsValue::from(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn baz() -> JsValue {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn baz() -> JsValue {
|
||||||
JsValue::from(1.0)
|
JsValue::from(1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn baz2(a: &JsValue, b: &JsValue) {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn baz2(a: &JsValue, b: &JsValue) {
|
||||||
assert_eq!(a.as_f64(), Some(2.0));
|
assert_eq!(a.as_f64(), Some(2.0));
|
||||||
assert_eq!(b.as_f64(), None);
|
assert_eq!(b.as_f64(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn js_null() -> JsValue {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn js_null() -> JsValue {
|
||||||
JsValue::null()
|
JsValue::null()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn js_undefined() -> JsValue {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn js_undefined() -> JsValue {
|
||||||
JsValue::undefined()
|
JsValue::undefined()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_is_null_undefined(
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn test_is_null_undefined(
|
||||||
a: &JsValue,
|
a: &JsValue,
|
||||||
b: &JsValue,
|
b: &JsValue,
|
||||||
c: &JsValue,
|
c: &JsValue,
|
||||||
@ -51,15 +64,21 @@ fn works() {
|
|||||||
assert!(!c.is_undefined());
|
assert!(!c.is_undefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_true() -> JsValue {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn get_true() -> JsValue {
|
||||||
JsValue::from(true)
|
JsValue::from(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_false() -> JsValue {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn get_false() -> JsValue {
|
||||||
JsValue::from(false)
|
JsValue::from(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_bool(
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn test_bool(
|
||||||
a: &JsValue,
|
a: &JsValue,
|
||||||
b: &JsValue,
|
b: &JsValue,
|
||||||
c: &JsValue,
|
c: &JsValue,
|
||||||
@ -69,32 +88,41 @@ fn works() {
|
|||||||
assert_eq!(c.as_bool(), None);
|
assert_eq!(c.as_bool(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mk_symbol() -> JsValue {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn mk_symbol() -> JsValue {
|
||||||
let a = JsValue::symbol(None);
|
let a = JsValue::symbol(None);
|
||||||
assert!(a.is_symbol());
|
assert!(a.is_symbol());
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mk_symbol2(s: &str) -> JsValue {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn mk_symbol2(s: &str) -> JsValue {
|
||||||
let a = JsValue::symbol(Some(s));
|
let a = JsValue::symbol(Some(s));
|
||||||
assert!(a.is_symbol());
|
assert!(a.is_symbol());
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_symbols(a: &JsValue, b: &JsValue) {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn assert_symbols(a: &JsValue, b: &JsValue) {
|
||||||
assert!(a.is_symbol());
|
assert!(a.is_symbol());
|
||||||
assert!(!b.is_symbol());
|
assert!(!b.is_symbol());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acquire_string(a: &JsValue, b: &JsValue) {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn acquire_string(a: &JsValue, b: &JsValue) {
|
||||||
assert_eq!(a.as_string().unwrap(), "foo");
|
assert_eq!(a.as_string().unwrap(), "foo");
|
||||||
assert_eq!(b.as_string(), None);
|
assert_eq!(b.as_string(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acquire_string2(a: &JsValue) -> String {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn acquire_string2(a: &JsValue) -> String {
|
||||||
a.as_string().unwrap_or("wrong".to_string())
|
a.as_string().unwrap_or("wrong".to_string())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as assert from "assert";
|
import * as assert from "assert";
|
||||||
|
@ -10,11 +10,12 @@ fn simple() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub struct Foo {
|
pub struct Foo {
|
||||||
contents: u32,
|
contents: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
impl Foo {
|
impl Foo {
|
||||||
pub fn new() -> Foo {
|
pub fn new() -> Foo {
|
||||||
Foo::with_contents(0)
|
Foo::with_contents(0)
|
||||||
@ -29,7 +30,6 @@ fn simple() {
|
|||||||
self.contents
|
self.contents
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as assert from "assert";
|
import * as assert from "assert";
|
||||||
@ -62,15 +62,17 @@ fn strings() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub struct Foo {
|
pub struct Foo {
|
||||||
name: u32,
|
name: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
pub struct Bar {
|
pub struct Bar {
|
||||||
contents: String,
|
contents: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
impl Foo {
|
impl Foo {
|
||||||
pub fn new() -> Foo {
|
pub fn new() -> Foo {
|
||||||
Foo { name: 0 }
|
Foo { name: 0 }
|
||||||
@ -85,12 +87,12 @@ fn strings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
impl Bar {
|
impl Bar {
|
||||||
pub fn name(&self) -> String {
|
pub fn name(&self) -> String {
|
||||||
self.contents.clone()
|
self.contents.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as assert from "assert";
|
import * as assert from "assert";
|
||||||
@ -118,10 +120,11 @@ fn exceptions() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub struct A {
|
pub struct A {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
impl A {
|
impl A {
|
||||||
pub fn new() -> A {
|
pub fn new() -> A {
|
||||||
A {}
|
A {}
|
||||||
@ -134,15 +137,16 @@ fn exceptions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
pub struct B {
|
pub struct B {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
impl B {
|
impl B {
|
||||||
pub fn new() -> B {
|
pub fn new() -> B {
|
||||||
B {}
|
B {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.js", r#"
|
.file("test.js", r#"
|
||||||
import * as assert from "assert";
|
import * as assert from "assert";
|
||||||
@ -181,9 +185,10 @@ fn pass_one_to_another() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub struct A {}
|
pub struct A {}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
impl A {
|
impl A {
|
||||||
pub fn new() -> A {
|
pub fn new() -> A {
|
||||||
A {}
|
A {}
|
||||||
@ -196,14 +201,15 @@ fn pass_one_to_another() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
pub struct B {}
|
pub struct B {}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
impl B {
|
impl B {
|
||||||
pub fn new() -> B {
|
pub fn new() -> B {
|
||||||
B {}
|
B {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import { A, B } from "./out";
|
import { A, B } from "./out";
|
||||||
@ -218,46 +224,3 @@ fn pass_one_to_another() {
|
|||||||
"#)
|
"#)
|
||||||
.test();
|
.test();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bindgen_twice() {
|
|
||||||
test_support::project()
|
|
||||||
.file("src/lib.rs", r#"
|
|
||||||
#![feature(proc_macro)]
|
|
||||||
|
|
||||||
extern crate wasm_bindgen;
|
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
wasm_bindgen! {
|
|
||||||
pub struct A {}
|
|
||||||
|
|
||||||
impl A {
|
|
||||||
pub fn new() -> A {
|
|
||||||
A {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wasm_bindgen! {
|
|
||||||
pub struct B {}
|
|
||||||
|
|
||||||
impl B {
|
|
||||||
pub fn new(a: &A) -> B {
|
|
||||||
B {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#)
|
|
||||||
.file("test.ts", r#"
|
|
||||||
import { A, B } from "./out";
|
|
||||||
|
|
||||||
export function test() {
|
|
||||||
let a = A.new();
|
|
||||||
let b = B.new(a);
|
|
||||||
a.free();
|
|
||||||
b.free();
|
|
||||||
}
|
|
||||||
"#)
|
|
||||||
.test();
|
|
||||||
}
|
|
||||||
|
@ -10,19 +10,25 @@ fn simple() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
extern struct Math {
|
#[no_mangle]
|
||||||
fn random() -> f64;
|
pub extern fn get_random() -> f64 {
|
||||||
fn log(a: f64) -> f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_random() -> f64 {
|
|
||||||
Math::random()
|
Math::random()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_log(a: f64) -> f64 {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn do_log(a: f64) -> f64 {
|
||||||
Math::log(a)
|
Math::log(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern {
|
||||||
|
type Math;
|
||||||
|
#[wasm_bindgen(static = Math)]
|
||||||
|
fn random() -> f64;
|
||||||
|
#[wasm_bindgen(static = Math)]
|
||||||
|
fn log(a: f64) -> f64;
|
||||||
}
|
}
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
@ -47,16 +53,18 @@ fn import_class() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern struct Foo {
|
type Foo;
|
||||||
|
#[wasm_bindgen(static = Foo)]
|
||||||
fn bar();
|
fn bar();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bar() {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bar() {
|
||||||
Foo::bar();
|
Foo::bar();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as wasm from "./out";
|
import * as wasm from "./out";
|
||||||
@ -88,22 +96,27 @@ fn construct() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern struct Foo {
|
type Foo;
|
||||||
|
#[wasm_bindgen(static = Foo)]
|
||||||
fn create() -> Foo;
|
fn create() -> Foo;
|
||||||
fn get_internal_string(&self) -> String;
|
#[wasm_bindgen(method)]
|
||||||
fn append_to_internal_string(&self, s: &str);
|
fn get_internal_string(this: &Foo) -> String;
|
||||||
fn assert_internal_string(&self, s: &str);
|
#[wasm_bindgen(method)]
|
||||||
|
fn append_to_internal_string(this: &Foo, s: &str);
|
||||||
|
#[wasm_bindgen(method)]
|
||||||
|
fn assert_internal_string(this: &Foo, s: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn run() {
|
||||||
let f = Foo::create();
|
let f = Foo::create();
|
||||||
assert_eq!(f.get_internal_string(), "this");
|
assert_eq!(f.get_internal_string(), "this");
|
||||||
f.append_to_internal_string(" foo");
|
f.append_to_internal_string(" foo");
|
||||||
f.assert_internal_string("this foo");
|
f.assert_internal_string("this foo");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as wasm from "./out";
|
import * as wasm from "./out";
|
||||||
@ -152,19 +165,21 @@ fn new_constructors() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern struct Foo {
|
type Foo;
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
fn new(arg: i32) -> Foo;
|
fn new(arg: i32) -> Foo;
|
||||||
fn get(&self) -> i32;
|
#[wasm_bindgen(method)]
|
||||||
|
fn get(this: &Foo) -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn run() {
|
||||||
let f = Foo::new(1);
|
let f = Foo::new(1);
|
||||||
assert_eq!(f.get(), 2);
|
assert_eq!(f.get(), 2);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import { run } from "./out";
|
import { run } from "./out";
|
||||||
|
104
tests/imports.rs
104
tests/imports.rs
@ -10,28 +10,37 @@ fn simple() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
fn foo(s: &str);
|
fn foo(s: &str);
|
||||||
fn another(a: u32) -> i32;
|
fn another(a: u32) -> i32;
|
||||||
fn take_and_return_bool(a: bool) -> bool;
|
fn take_and_return_bool(a: bool) -> bool;
|
||||||
fn return_object() -> JsValue;
|
fn return_object() -> JsValue;
|
||||||
}
|
}
|
||||||
pub fn bar(s: &str) {
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bar(s: &str) {
|
||||||
foo(s);
|
foo(s);
|
||||||
}
|
}
|
||||||
pub fn another_thunk(a: u32) -> i32 {
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn another_thunk(a: u32) -> i32 {
|
||||||
another(a)
|
another(a)
|
||||||
}
|
}
|
||||||
pub fn bool_thunk(a: bool) -> bool {
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bool_thunk(a: bool) -> bool {
|
||||||
take_and_return_bool(a)
|
take_and_return_bool(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_the_object() -> JsValue {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn get_the_object() -> JsValue {
|
||||||
return_object()
|
return_object()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as wasm from "./out";
|
import * as wasm from "./out";
|
||||||
@ -87,14 +96,14 @@ fn unused() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
fn debug_print(s: &str);
|
fn debug_print(s: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bar() {}
|
#[wasm_bindgen]
|
||||||
}
|
#[no_mangle]
|
||||||
|
pub extern fn bar() {}
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as wasm from "./out";
|
import * as wasm from "./out";
|
||||||
@ -118,20 +127,22 @@ fn strings() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
fn foo(a: String) -> String;
|
fn foo(a: String) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bar(a: &str) -> String {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bar(a: &str) -> String {
|
||||||
foo(a.to_string())
|
foo(a.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bar2(a: String) -> String {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bar2(a: String) -> String {
|
||||||
foo(a)
|
foo(a)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as wasm from "./out";
|
import * as wasm from "./out";
|
||||||
@ -159,25 +170,27 @@ fn exceptions() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
fn foo();
|
fn foo();
|
||||||
fn bar();
|
fn bar();
|
||||||
#[wasm_bindgen(catch)]
|
#[wasm_bindgen(catch)]
|
||||||
fn baz() -> Result<(), JsValue>;
|
fn baz() -> Result<(), JsValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn run() {
|
||||||
foo();
|
foo();
|
||||||
bar();
|
bar();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run2() {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn run2() {
|
||||||
assert!(baz().is_err());
|
assert!(baz().is_err());
|
||||||
bar();
|
bar();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import { run, run2 } from "./out";
|
import { run, run2 } from "./out";
|
||||||
@ -217,17 +230,17 @@ fn exn_caught() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
#[wasm_bindgen(catch)]
|
#[wasm_bindgen(catch)]
|
||||||
fn foo() -> Result<(), JsValue>;
|
fn foo() -> Result<(), JsValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() -> JsValue {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn run() -> JsValue {
|
||||||
foo().unwrap_err()
|
foo().unwrap_err()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import { run } from "./out";
|
import { run } from "./out";
|
||||||
@ -245,3 +258,34 @@ fn exn_caught() {
|
|||||||
"#)
|
"#)
|
||||||
.test();
|
.test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn free_imports() {
|
||||||
|
test_support::project()
|
||||||
|
.file("src/lib.rs", r#"
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
|
extern crate wasm_bindgen;
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern {
|
||||||
|
fn parseInt(a: &str) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn run() {
|
||||||
|
assert_eq!(parseInt("3"), 3);
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.file("test.ts", r#"
|
||||||
|
import { run } from "./out";
|
||||||
|
|
||||||
|
export function test() {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.test();
|
||||||
|
}
|
||||||
|
@ -10,15 +10,16 @@ fn simple() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
fn foo(s: &JsValue);
|
fn foo(s: &JsValue);
|
||||||
}
|
}
|
||||||
pub fn bar(s: &JsValue) {
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bar(s: &JsValue) {
|
||||||
foo(s);
|
foo(s);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as wasm from "./out";
|
import * as wasm from "./out";
|
||||||
@ -51,15 +52,16 @@ fn owned() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
fn foo(s: JsValue);
|
fn foo(s: JsValue);
|
||||||
}
|
}
|
||||||
pub fn bar(s: JsValue) {
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bar(s: JsValue) {
|
||||||
foo(s);
|
foo(s);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as wasm from "./out";
|
import * as wasm from "./out";
|
||||||
@ -92,9 +94,8 @@ fn clone() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
fn foo1(s: JsValue);
|
fn foo1(s: JsValue);
|
||||||
fn foo2(s: &JsValue);
|
fn foo2(s: &JsValue);
|
||||||
fn foo3(s: JsValue);
|
fn foo3(s: JsValue);
|
||||||
@ -102,14 +103,15 @@ fn clone() {
|
|||||||
fn foo5(s: JsValue);
|
fn foo5(s: JsValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bar(s: JsValue) {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bar(s: JsValue) {
|
||||||
foo1(s.clone());
|
foo1(s.clone());
|
||||||
foo2(&s);
|
foo2(&s);
|
||||||
foo3(s.clone());
|
foo3(s.clone());
|
||||||
foo4(&s);
|
foo4(&s);
|
||||||
foo5(s);
|
foo5(s);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as wasm from "./out";
|
import * as wasm from "./out";
|
||||||
@ -140,22 +142,22 @@ fn promote() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen(module = "./test")]
|
||||||
#[wasm_module = "./test"]
|
extern {
|
||||||
extern "JS" {
|
|
||||||
fn foo1(s: &JsValue);
|
fn foo1(s: &JsValue);
|
||||||
fn foo2(s: JsValue);
|
fn foo2(s: JsValue);
|
||||||
fn foo3(s: &JsValue);
|
fn foo3(s: &JsValue);
|
||||||
fn foo4(s: JsValue);
|
fn foo4(s: JsValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bar(s: &JsValue) {
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn bar(s: &JsValue) {
|
||||||
foo1(s);
|
foo1(s);
|
||||||
foo2(s.clone());
|
foo2(s.clone());
|
||||||
foo3(s);
|
foo3(s);
|
||||||
foo4(s.clone());
|
foo4(s.clone());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as wasm from "./out";
|
import * as wasm from "./out";
|
||||||
|
@ -11,19 +11,22 @@ fn works() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[wasm_bindgen]
|
||||||
pub struct A {}
|
pub struct A {}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
impl A {
|
impl A {
|
||||||
pub fn new() -> A {
|
pub fn new() -> A {
|
||||||
A {}
|
A {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn clone(a: &JsValue) -> JsValue {
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn clone(a: &JsValue) -> JsValue {
|
||||||
drop(a.clone());
|
drop(a.clone());
|
||||||
a.clone()
|
a.clone()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as assert from "assert";
|
import * as assert from "assert";
|
||||||
|
@ -10,30 +10,38 @@ fn add() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[no_mangle]
|
||||||
pub fn add(a: u32, b: u32) -> u32 {
|
#[wasm_bindgen]
|
||||||
|
pub extern fn add(a: u32, b: u32) -> u32 {
|
||||||
a + b
|
a + b
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add3(a: u32) -> u32 {
|
#[no_mangle]
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub extern fn add3(a: u32) -> u32 {
|
||||||
a + 3
|
a + 3
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get2(_b: bool) -> u32 {
|
#[no_mangle]
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub extern fn get2(_b: bool) -> u32 {
|
||||||
2
|
2
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_and_take_bool(a: bool, b: bool) -> bool {
|
#[no_mangle]
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub extern fn return_and_take_bool(a: bool, b: bool) -> bool {
|
||||||
a && b
|
a && b
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw_pointers_work(a: *mut u32, b: *const u8) -> *const u32 {
|
#[no_mangle]
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub extern fn raw_pointers_work(a: *mut u32, b: *const u8) -> *const u32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
(*a) = (*b) as u32;
|
(*a) = (*b) as u32;
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as assert from "assert";
|
import * as assert from "assert";
|
||||||
@ -60,16 +68,18 @@ fn string_arguments() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[no_mangle]
|
||||||
pub fn assert_foo_and_bar(a: &str, b: &str) {
|
#[wasm_bindgen]
|
||||||
|
pub extern fn assert_foo_and_bar(a: &str, b: &str) {
|
||||||
assert_eq!(a, "foo2");
|
assert_eq!(a, "foo2");
|
||||||
assert_eq!(b, "bar");
|
assert_eq!(b, "bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_foo(a: &str) {
|
#[no_mangle]
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub extern fn assert_foo(a: &str) {
|
||||||
assert_eq!(a, "foo");
|
assert_eq!(a, "foo");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as wasm from "./out";
|
import * as wasm from "./out";
|
||||||
@ -92,17 +102,17 @@ fn return_a_string() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[no_mangle]
|
||||||
pub fn clone(a: &str) -> String {
|
#[wasm_bindgen]
|
||||||
|
pub extern fn clone(a: &str) -> String {
|
||||||
a.to_string()
|
a.to_string()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[no_mangle]
|
||||||
pub fn concat(a: &str, b: &str, c: i8) -> String {
|
#[wasm_bindgen]
|
||||||
|
pub extern fn concat(a: &str, b: &str, c: i8) -> String {
|
||||||
format!("{} {} {}", a, b, c)
|
format!("{} {} {}", a, b, c)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
import * as assert from "assert";
|
import * as assert from "assert";
|
||||||
@ -128,10 +138,13 @@ fn exceptions() {
|
|||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
wasm_bindgen! {
|
#[no_mangle]
|
||||||
pub fn foo(_a: u32) {}
|
#[wasm_bindgen]
|
||||||
pub fn bar(_a: &str) {}
|
pub extern fn foo(_a: u32) {}
|
||||||
}
|
|
||||||
|
#[no_mangle]
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub extern fn bar(_a: &str) {}
|
||||||
"#)
|
"#)
|
||||||
.file("test.js", r#"
|
.file("test.js", r#"
|
||||||
import * as assert from "assert";
|
import * as assert from "assert";
|
||||||
|
Reference in New Issue
Block a user