Skip to content

Commit

Permalink
Add initial wasm-bindgen compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
koute committed Apr 7, 2019
1 parent f9bbd99 commit be5f620
Show file tree
Hide file tree
Showing 13 changed files with 242 additions and 7 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Expand Up @@ -22,6 +22,8 @@ futures-channel-preview = { version = "0.3.0-alpha.13", optional = true }
futures-util-preview = { version = "0.3.0-alpha.13", optional = true }
futures-executor-preview = { version = "0.3.0-alpha.13", optional = true }

wasm-bindgen = { version = "0.2", optional = true }

stdweb-derive = { version = "= 0.5.1", path = "stdweb-derive" }
stdweb-internal-macros = { version = "= 0.2.6", path = "stdweb-internal-macros" }
stdweb-internal-runtime = { version = "0.1", path = "stdweb-internal-runtime" }
Expand Down
2 changes: 1 addition & 1 deletion examples/Cargo.toml
@@ -1,2 +1,2 @@
[workspace]
members = ["canvas", "drag", "echo", "gamepad", "hasher", "minimal", "todomvc", "webgl"]
members = ["canvas", "drag", "echo", "gamepad", "hasher", "minimal", "todomvc", "webgl", "wasm-bindgen-minimal"]
12 changes: 12 additions & 0 deletions examples/wasm-bindgen-minimal/Cargo.toml
@@ -0,0 +1,12 @@
[package]
name = "wasm-bindgen-minimal"
version = "0.1.0"
authors = ["Jan Bujak <j@exia.io>"]
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2.40"
stdweb = { path = "../..", features = ["wasm-bindgen"] }
6 changes: 6 additions & 0 deletions examples/wasm-bindgen-minimal/build.sh
@@ -0,0 +1,6 @@
#!/bin/sh

set -ex

wasm-pack build --target web
python3 -m http.server
39 changes: 39 additions & 0 deletions examples/wasm-bindgen-minimal/index.html
@@ -0,0 +1,39 @@
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
</head>
<body>
<!-- Note the usage of `type=module` here as this is an ES6 module -->
<script type="module">
// Use ES module import syntax to import functionality from the module
// that we have compiled.
//
// Note that the `default` import is an initialization function which
// will "boot" the module and make it ready to use. Currently browsers
// don't support natively imported WebAssembly as an ES module, but
// eventually the manual initialization won't be required!
import { default as init } from './pkg/wasm_bindgen_minimal.js';

async function run() {
// First up we need to actually load the wasm file, so we use the
// default export to inform it where the wasm file is located on the
// server, and then we wait on the returned promise to wait for the
// wasm to be loaded.
//
// Note that instead of a string here you can also pass in an instance
// of `WebAssembly.Module` which allows you to compile your own module.
// Also note that the promise, when resolved, yields the wasm module's
// exports which is the same as importing the `*_bg` module in other
// modes
// await init('./pkg/wasm_bindgen_minimal_bg.wasm');

const url = await fetch('http://localhost:8000/pkg/wasm_bindgen_minimal_bg.wasm');
const body = await url.arrayBuffer();
const module = await WebAssembly.compile(body);
await init(module);
}

run();
</script>
</body>
</html>
14 changes: 14 additions & 0 deletions examples/wasm-bindgen-minimal/src/lib.rs
@@ -0,0 +1,14 @@
#[macro_use]
extern crate stdweb;

use wasm_bindgen::prelude::*;

#[wasm_bindgen(start)]
pub fn main() -> Result<(), JsValue> {
let message = "Hello, 世界!";
js! {
alert( @{message} );
}

Ok(())
}
22 changes: 19 additions & 3 deletions src/lib.rs
Expand Up @@ -130,6 +130,9 @@ extern crate serde_json;
#[macro_use]
extern crate serde_derive;

#[cfg(feature = "wasm-bindgen")]
extern crate wasm_bindgen;

extern crate stdweb_internal_macros;

#[cfg(all(
Expand Down Expand Up @@ -522,6 +525,12 @@ pub mod traits {

#[doc(hidden)]
pub mod private {
#[cfg(feature = "wasm-bindgen")]
pub extern crate wasm_bindgen;

#[cfg(feature = "wasm-bindgen")]
pub use webcore::ffi::get_module;

pub use webcore::ffi::exports::*;
pub use webcore::serialization::{
JsSerialize,
Expand All @@ -543,13 +552,20 @@ pub mod private {
pub use webcore::global_arena::ArenaRestorePoint;
pub use webcore::global_arena::serialize_value;

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
#[cfg(all(target_arch = "wasm32", target_os = "unknown", not(feature = "wasm-bindgen")))]
pub use stdweb_internal_macros::wasm32_unknown_unknown_js_attr as js_attr;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
#[cfg(all(target_arch = "wasm32", target_os = "unknown", not(feature = "wasm-bindgen")))]
pub use stdweb_internal_macros::wasm32_unknown_unknown_js_no_return_attr as js_no_return_attr;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
#[cfg(all(target_arch = "wasm32", target_os = "unknown", not(feature = "wasm-bindgen")))]
pub use stdweb_internal_macros::wasm32_unknown_unknown_js_raw_attr as js_raw_attr;

#[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "wasm-bindgen"))]
pub use stdweb_internal_macros::wasm_bindgen_js_attr as js_attr;
#[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "wasm-bindgen"))]
pub use stdweb_internal_macros::wasm_bindgen_js_no_return_attr as js_no_return_attr;
#[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "wasm-bindgen"))]
pub use stdweb_internal_macros::wasm_bindgen_js_raw_attr as js_raw_attr;

#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub use stdweb_internal_macros::emscripten_js_attr as js_attr;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
Expand Down
7 changes: 5 additions & 2 deletions src/webcore/ffi/mod.rs
@@ -1,5 +1,8 @@
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] mod emscripten;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] pub use self::emscripten::*;

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] mod wasm;
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] pub use self::wasm::*;
#[cfg(all(target_arch = "wasm32", target_os = "unknown", not(feature = "wasm-bindgen")))] mod wasm;
#[cfg(all(target_arch = "wasm32", target_os = "unknown", not(feature = "wasm-bindgen")))] pub use self::wasm::*;

#[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "wasm-bindgen"))] mod wasm_bindgen;
#[cfg(all(target_arch = "wasm32", target_os = "unknown", feature = "wasm-bindgen"))] pub use self::wasm_bindgen::*;
95 changes: 95 additions & 0 deletions src/webcore/ffi/wasm_bindgen.rs
@@ -0,0 +1,95 @@
use std::mem;
use wasm_bindgen::prelude::*;

use webcore::initialization::initialize as initialize_crate;

pub fn event_loop() {
}

fn alloc( size: usize ) -> *mut u8 {
let mut buffer = Vec::with_capacity( size );
let ptr = buffer.as_mut_ptr();
mem::forget( buffer );
ptr
}

fn free( ptr: *mut u8, capacity: usize ) {
unsafe {
let _ = Vec::from_raw_parts( ptr, 0, capacity );
}
}

pub unsafe fn dealloc( ptr: *mut u8, capacity: usize ) {
free( ptr, capacity )
}

struct Module( Option< JsValue > );
unsafe impl Sync for Module {}

static mut MODULE: Module = Module( None );

pub fn initialize() {
#[wasm_bindgen(inline_js = r#"export function wasm_bindgen_initialize( memory, alloc, free ) {
var Module = {};
Module.web_malloc = alloc;
Module.web_free = free;
Module.web_table = null;
function define_heap( target ) {
Object.defineProperty( target, "HEAP8", {
get: function() { return new Int8Array( memory.buffer ); }
});
Object.defineProperty( target, "HEAP16", {
get: function() { return new Int16Array( memory.buffer ); }
});
Object.defineProperty( target, "HEAP32", {
get: function() { return new Int32Array( memory.buffer ); }
});
Object.defineProperty( target, "HEAPU8", {
get: function() { return new Uint8Array( memory.buffer ); }
});
Object.defineProperty( target, "HEAPU16", {
get: function() { return new Uint16Array( memory.buffer ); }
});
Object.defineProperty( target, "HEAPU32", {
get: function() { return new Uint32Array( memory.buffer ); }
});
Object.defineProperty( target, "HEAPF32", {
get: function() { return new Float32Array( memory.buffer ); }
});
Object.defineProperty( target, "HEAPF64", {
get: function() { return new Float64Array( memory.buffer ); }
});
}
if( typeof global !== "undefined" ) {
define_heap( global );
}
if( typeof window !== "undefined" ) {
define_heap( window );
}
return Module;
}"#)]
extern "C" {
fn wasm_bindgen_initialize(
memory: JsValue,
alloc: &dyn Fn( usize ) -> *mut u8,
free: &dyn Fn( *mut u8, usize )
) -> JsValue;
}

let memory = wasm_bindgen::memory();
unsafe {
let module = wasm_bindgen_initialize( memory, &alloc, &free );
MODULE = Module( Some( module ) );
}
}

#[doc(hidden)]
pub fn get_module() -> JsValue {
initialize_crate();

unsafe {
MODULE.0.as_ref().unwrap().clone()
}
}

pub mod exports {}
3 changes: 3 additions & 0 deletions src/webcore/initialization.rs
Expand Up @@ -16,6 +16,9 @@ pub fn initialize() {
INITIALIZED = true;
}

#[cfg(feature = "wasm-bindgen")]
ffi::initialize();

#[cfg(not(feature = "docs-rs"))]
stdweb_internal_runtime_initialize!( __js_raw_asm );

Expand Down
29 changes: 29 additions & 0 deletions stdweb-internal-macros/src/js_shim.rs
@@ -1,6 +1,7 @@
use std::env;
use std::path::PathBuf;
use std::fs;
use std::fmt::Write;

use syn;
use proc_macro2::{TokenStream, Span};
Expand Down Expand Up @@ -84,6 +85,34 @@ pub fn js_shim_extern_code( target: Target, code: &str, arg_count: usize ) -> (s
pub fn #shim_name( #(#shim_args),* ) -> i32;
}
}
},
Target::WasmBindgen => {
let mut code_string = String::new();
write!( &mut code_string, "export function {}(", &snippet.name ).unwrap();
write!( &mut code_string, "Module" ).unwrap();
if arg_count != 0 {
write!( &mut code_string, ", " ).unwrap();
}
for nth in 0..arg_count {
write!( &mut code_string, "${}", nth ).unwrap();
if nth + 1 != arg_count {
write!( &mut code_string, ", " ).unwrap();
}
}
write!( &mut code_string, ") {{ {} }}", code ).unwrap();
let shim_name = &shim_name;
let shim_args = &shim_args;
quote! {
use ::stdweb::private::wasm_bindgen::prelude::*;
unsafe fn #shim_name( #(#shim_args),* ) -> i32 {
#[wasm_bindgen(inline_js = #code_string)]
extern "C" {
pub fn #shim_name( module: JsValue, #(#shim_args),* ) -> i32;
}

#shim_name( ::stdweb::private::get_module(), #(#shim_args_passthrough),* )
}
}
}
};

Expand Down
15 changes: 15 additions & 0 deletions stdweb-internal-macros/src/lib.rs
Expand Up @@ -85,3 +85,18 @@ pub fn emscripten_js_attr( _: proc_macro::TokenStream, input: proc_macro::TokenS
pub fn emscripten_js_no_return_attr( _: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream {
emit( macro_js::js_attr( Target::Emscripten, input.into(), true ) )
}

#[proc_macro_attribute]
pub fn wasm_bindgen_js_raw_attr( _: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream {
emit( macro_js_raw::js_raw_attr( Target::WasmBindgen, input.into() ) )
}

#[proc_macro_attribute]
pub fn wasm_bindgen_js_attr( _: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream {
emit( macro_js::js_attr( Target::WasmBindgen, input.into(), false ) )
}

#[proc_macro_attribute]
pub fn wasm_bindgen_js_no_return_attr( _: proc_macro::TokenStream, input: proc_macro::TokenStream ) -> proc_macro::TokenStream {
emit( macro_js::js_attr( Target::WasmBindgen, input.into(), true ) )
}
3 changes: 2 additions & 1 deletion stdweb-internal-macros/src/utils.rs
Expand Up @@ -4,7 +4,8 @@ use proc_macro2::Span;
#[derive(Copy, Clone)]
pub enum Target {
Emscripten,
NativeWebAssembly
NativeWebAssembly,
WasmBindgen
}

pub fn dummy_idents( count: usize ) -> impl Iterator< Item = syn::Ident > {
Expand Down

0 comments on commit be5f620

Please sign in to comment.