Work with #![no_std] contexts

This commit adds support for both `#![no_std]` in the wasm-bindgen runtime
support (disabled by default with an on-by-default `std` feature). This also
adds support to work and compile in the context of `#![no_std]` crates.

Closes #146
This commit is contained in:
Alex Crichton 2018-04-19 13:08:54 -07:00
parent 792a8e132e
commit 748184ae66
11 changed files with 318 additions and 127 deletions

View File

@ -16,6 +16,10 @@ Easy support for interacting between JS and Rust.
test = false
doctest = false
[features]
default = ["std"]
std = []
[dependencies]
wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.4" }

View File

@ -116,13 +116,22 @@ impl ToTokens for ast::Struct {
}
}
::wasm_bindgen::__wbindgen_if_not_std! {
compile_error! {
"exporting a class to JS requires the `std` feature to \
be enabled in the `wasm-bindgen` crate"
}
}
impl ::wasm_bindgen::convert::IntoWasmAbi for #name {
type Abi = u32;
fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack)
-> u32
{
Box::into_raw(Box::new(::wasm_bindgen::__rt::WasmRefCell::new(self))) as u32
use wasm_bindgen::__rt::std::boxed::Box;
use wasm_bindgen::__rt::WasmRefCell;
Box::into_raw(Box::new(WasmRefCell::new(self))) as u32
}
}
@ -132,14 +141,49 @@ impl ToTokens for ast::Struct {
unsafe fn from_abi(js: u32, _extra: &mut ::wasm_bindgen::convert::Stack)
-> Self
{
let ptr = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>;
::wasm_bindgen::__rt::assert_not_null(ptr);
use wasm_bindgen::__rt::std::boxed::Box;
use wasm_bindgen::__rt::{assert_not_null, WasmRefCell};
let ptr = js as *mut WasmRefCell<#name>;
assert_not_null(ptr);
let js = Box::from_raw(ptr);
js.borrow_mut(); // make sure no one's borrowing
js.into_inner()
}
}
impl ::wasm_bindgen::__rt::core::convert::From<#name> for
::wasm_bindgen::JsValue
{
fn from(value: #name) -> Self {
let ptr = ::wasm_bindgen::convert::IntoWasmAbi::into_abi(
value,
unsafe { &mut ::wasm_bindgen::convert::GlobalStack::new() },
);
#[wasm_import_module = "__wbindgen_placeholder__"]
extern {
fn #new_fn(ptr: u32) -> u32;
}
unsafe {
<::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi>
::from_abi(
#new_fn(ptr),
&mut ::wasm_bindgen::convert::GlobalStack::new(),
)
}
}
}
#[no_mangle]
pub unsafe extern fn #free_fn(ptr: u32) {
<#name as ::wasm_bindgen::convert::FromWasmAbi>::from_abi(
ptr,
&mut ::wasm_bindgen::convert::GlobalStack::new(),
);
}
impl ::wasm_bindgen::convert::RefFromWasmAbi for #name {
type Abi = u32;
type Anchor = ::wasm_bindgen::__rt::Ref<'static, #name>;
@ -167,36 +211,6 @@ impl ToTokens for ast::Struct {
(*js).borrow_mut()
}
}
impl ::std::convert::From<#name> for ::wasm_bindgen::JsValue {
fn from(value: #name) -> Self {
let ptr = ::wasm_bindgen::convert::IntoWasmAbi::into_abi(
value,
unsafe { &mut ::wasm_bindgen::convert::GlobalStack::new() },
);
#[wasm_import_module = "__wbindgen_placeholder__"]
extern {
fn #new_fn(ptr: u32) -> u32;
}
unsafe {
<::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi>
::from_abi(
#new_fn(ptr),
&mut ::wasm_bindgen::convert::GlobalStack::new(),
)
}
}
}
#[no_mangle]
pub unsafe extern fn #free_fn(ptr: u32) {
<#name as ::wasm_bindgen::convert::FromWasmAbi>::from_abi(
ptr,
&mut ::wasm_bindgen::convert::GlobalStack::new(),
);
}
}).to_tokens(tokens);
}
}
@ -419,7 +433,7 @@ impl ToTokens for ast::ImportType {
impl ::wasm_bindgen::convert::RefFromWasmAbi for #name {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::RefFromWasmAbi>::Abi;
type Anchor = ::std::mem::ManuallyDrop<#name>;
type Anchor = ::wasm_bindgen::__rt::core::mem::ManuallyDrop<#name>;
unsafe fn ref_from_abi(
js: Self::Abi,
@ -427,8 +441,8 @@ impl ToTokens for ast::ImportType {
) -> Self::Anchor {
let tmp = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::RefFromWasmAbi>
::ref_from_abi(js, extra);
::std::mem::ManuallyDrop::new(#name {
obj: ::std::mem::ManuallyDrop::into_inner(tmp),
::wasm_bindgen::__rt::core::mem::ManuallyDrop::new(#name {
obj: ::wasm_bindgen::__rt::core::mem::ManuallyDrop::into_inner(tmp),
})
}
}
@ -702,7 +716,7 @@ impl ToTokens for ast::ImportStatic {
}
}
::wasm_bindgen::JsStatic {
__inner: ::std::cell::UnsafeCell::new(None),
__inner: ::wasm_bindgen::__rt::core::cell::UnsafeCell::new(None),
__init: init,
}
};

View File

@ -119,9 +119,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.finally(&format!("\
wasm.__wbindgen_free(ptr{i}, len{i} * {size});\n\
", i = i, size = kind.size()));
self.cx.required_internal_exports.insert(
"__wbindgen_free",
);
self.cx.require_internal_export("__wbindgen_free");
}
self.rust_arguments.push(format!("ptr{}", i));
return
@ -216,7 +214,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
self.ret_ty = ty.js_ty().to_string();
let f = self.cx.expose_get_vector_from_wasm(ty);
self.cx.expose_get_global_argument();
self.cx.required_internal_exports.insert("__wbindgen_free");
self.cx.require_internal_export("__wbindgen_free");
self.ret_expr = format!("\
const ret = RET;\n\
const len = getGlobalArgument(0);\n\

View File

@ -62,6 +62,22 @@ impl<'a> Context<'a> {
self.global(&global);
}
fn require_internal_export(&mut self, name: &'static str) {
if !self.required_internal_exports.insert(name) {
return
}
if let Some(s) = self.module.export_section() {
if s.entries().iter().any(|e| e.field() == name) {
return
}
}
panic!("\n\nthe exported function `{}` is required to generate bindings \
but it was not found in the wasm file, perhaps the `std` feature \
of the `wasm-bindgen` crate needs to be enabled?\n\n",
name);
}
pub fn finalize(&mut self, module_name: &str) -> (String, String) {
self.unexport_unused_internal_exports();
self.gc();
@ -642,7 +658,7 @@ impl<'a> Context<'a> {
if !self.exposed_globals.insert("pass_string_to_wasm") {
return;
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.require_internal_export("__wbindgen_malloc");
self.expose_text_encoder();
self.expose_uint8_memory();
let debug = if self.config.debug {
@ -668,7 +684,7 @@ impl<'a> Context<'a> {
if !self.exposed_globals.insert("pass_array8_to_wasm") {
return;
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.require_internal_export("__wbindgen_malloc");
self.expose_uint8_memory();
self.global(&format!("
function passArray8ToWasm(arg) {{
@ -683,7 +699,7 @@ impl<'a> Context<'a> {
if !self.exposed_globals.insert("pass_array16_to_wasm") {
return;
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.require_internal_export("__wbindgen_malloc");
self.expose_uint16_memory();
self.global(&format!("
function passArray16ToWasm(arg) {{
@ -698,7 +714,7 @@ impl<'a> Context<'a> {
if !self.exposed_globals.insert("pass_array32_to_wasm") {
return;
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.require_internal_export("__wbindgen_malloc");
self.expose_uint32_memory();
self.global(&format!("
function passArray32ToWasm(arg) {{
@ -713,7 +729,7 @@ impl<'a> Context<'a> {
if !self.exposed_globals.insert("pass_array_f32_to_wasm") {
return;
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.require_internal_export("__wbindgen_malloc");
self.global(&format!("
function passArrayF32ToWasm(arg) {{
const ptr = wasm.__wbindgen_malloc(arg.length * 4);
@ -727,7 +743,7 @@ impl<'a> Context<'a> {
if !self.exposed_globals.insert("pass_array_f64_to_wasm") {
return;
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.require_internal_export("__wbindgen_malloc");
self.global(&format!("
function passArrayF64ToWasm(arg) {{
const ptr = wasm.__wbindgen_malloc(arg.length * 8);
@ -1172,7 +1188,7 @@ impl<'a> Context<'a> {
if !self.exposed_globals.insert("global_argument_ptr") {
return;
}
self.required_internal_exports.insert("__wbindgen_global_argument_ptr");
self.require_internal_export("__wbindgen_global_argument_ptr");
self.global("
let cachedGlobalArgumentPtr = null;
function globalArgumentPtr() {

View File

@ -91,9 +91,7 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
self.prelude(&format!("\
wasm.__wbindgen_free(arg{0}, len{0} * {size});\
", i, size = ty.size()));
self.cx.required_internal_exports.insert(
"__wbindgen_free"
);
self.cx.require_internal_export("__wbindgen_free");
}
self.js_arguments.push(format!("v{}", i));
return

View File

@ -7,6 +7,7 @@
use std::cell::UnsafeCell;
use std::marker::Unsize;
use std::mem::{self, ManuallyDrop};
use std::prelude::v1::*;
use JsValue;
use convert::*;

View File

@ -1,14 +1,17 @@
//! This is mostly an internal module, no stability guarantees are provied. Use
//! at your own risk.
use std::mem::{self, ManuallyDrop};
use std::ops::{Deref, DerefMut};
use std::slice;
use std::str;
use core::mem::{self, ManuallyDrop};
use core::ops::{Deref, DerefMut};
use core::slice;
use core::str;
use {JsValue, throw};
use describe::*;
#[cfg(feature = "std")]
use std::prelude::v1::*;
#[derive(PartialEq, Eq, Copy, Clone)]
pub struct Descriptor {
#[doc(hidden)]
@ -132,6 +135,7 @@ impl<T> FromWasmAbi for *mut T {
macro_rules! vectors {
($($t:ident)*) => ($(
#[cfg(feature = "std")]
impl IntoWasmAbi for Box<[$t]> {
type Abi = u32;
@ -144,6 +148,7 @@ macro_rules! vectors {
}
}
#[cfg(feature = "std")]
impl FromWasmAbi for Box<[$t]> {
type Abi = u32;
@ -183,34 +188,36 @@ vectors! {
u8 i8 u16 i16 u32 i32 f32 f64
}
impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi {
type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
self.into_boxed_slice().into_abi(extra)
if_std! {
impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi {
type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
self.into_boxed_slice().into_abi(extra)
}
}
}
impl<T> FromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi {
type Abi = <Box<[T]> as FromWasmAbi>::Abi;
impl<T> FromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi {
type Abi = <Box<[T]> as FromWasmAbi>::Abi;
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self {
<Box<[T]>>::from_abi(js, extra).into()
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self {
<Box<[T]>>::from_abi(js, extra).into()
}
}
}
impl IntoWasmAbi for String {
type Abi = u32;
impl IntoWasmAbi for String {
type Abi = u32;
fn into_abi(self, extra: &mut Stack) -> u32 {
self.into_bytes().into_abi(extra)
fn into_abi(self, extra: &mut Stack) -> u32 {
self.into_bytes().into_abi(extra)
}
}
}
impl FromWasmAbi for String {
type Abi = u32;
impl FromWasmAbi for String {
type Abi = u32;
unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self {
String::from_utf8_unchecked(<Vec<u8>>::from_abi(js, extra))
unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self {
String::from_utf8_unchecked(<Vec<u8>>::from_abi(js, extra))
}
}
}
@ -265,25 +272,27 @@ impl RefFromWasmAbi for JsValue {
}
}
impl IntoWasmAbi for Box<[JsValue]> {
type Abi = u32;
if_std! {
impl IntoWasmAbi for Box<[JsValue]> {
type Abi = u32;
fn into_abi(self, extra: &mut Stack) -> u32 {
let ptr = self.as_ptr();
let len = self.len();
mem::forget(self);
extra.push(len as u32);
ptr.into_abi(extra)
fn into_abi(self, extra: &mut Stack) -> u32 {
let ptr = self.as_ptr();
let len = self.len();
mem::forget(self);
extra.push(len as u32);
ptr.into_abi(extra)
}
}
}
impl FromWasmAbi for Box<[JsValue]> {
type Abi = u32;
impl FromWasmAbi for Box<[JsValue]> {
type Abi = u32;
unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self {
let ptr = <*mut JsValue>::from_abi(js, extra);
let len = extra.pop() as usize;
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self {
let ptr = <*mut JsValue>::from_abi(js, extra);
let len = extra.pop() as usize;
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
}
}
}

View File

@ -72,7 +72,6 @@ simple! {
f64 => F64
bool => BOOLEAN
str => STRING
String => STRING
JsValue => ANYREF
}
@ -105,16 +104,24 @@ impl<'a, T: WasmDescribe + ?Sized> WasmDescribe for &'a mut T {
}
}
impl<T: WasmDescribe> WasmDescribe for Box<[T]> {
fn describe() {
inform(VECTOR);
T::describe();
}
}
if_std! {
use std::prelude::v1::*;
impl<T> WasmDescribe for Vec<T> where Box<[T]>: WasmDescribe {
fn describe() {
<Box<[T]>>::describe();
impl WasmDescribe for String {
fn describe() { inform(STRING) }
}
impl<T: WasmDescribe> WasmDescribe for Box<[T]> {
fn describe() {
inform(VECTOR);
T::describe();
}
}
impl<T> WasmDescribe for Vec<T> where Box<[T]>: WasmDescribe {
fn describe() {
<Box<[T]>>::describe();
}
}
}

View File

@ -6,15 +6,22 @@
//! interface.
#![feature(use_extern_macros, wasm_import_module, try_reserve, unsize)]
#![no_std]
extern crate wasm_bindgen_macro;
use std::cell::UnsafeCell;
use std::ops::Deref;
use std::ptr;
use core::cell::UnsafeCell;
use core::ops::Deref;
use core::ptr;
use convert::FromWasmAbi;
macro_rules! if_std {
($($i:item)*) => ($(
#[cfg(feature = "std")] $i
)*)
}
/// A module which is typically glob imported from:
///
/// ```
@ -23,13 +30,21 @@ use convert::FromWasmAbi;
pub mod prelude {
pub use wasm_bindgen_macro::wasm_bindgen;
pub use JsValue;
pub use closure::Closure;
if_std! {
pub use closure::Closure;
}
}
pub mod convert;
pub mod closure;
pub mod describe;
if_std! {
extern crate std;
use std::prelude::v1::*;
pub mod closure;
}
/// Representation of an object owned by JS.
///
/// A `JsValue` doesn't actually live in Rust right now but actually in a table
@ -136,6 +151,7 @@ impl JsValue {
///
/// If this JS value is not an instance of a string or if it's not valid
/// utf-8 then this returns `None`.
#[cfg(feature = "std")]
pub fn as_string(&self) -> Option<String> {
unsafe {
let mut len = 0;
@ -192,9 +208,11 @@ impl<'a> From<&'a str> for JsValue {
}
}
impl<'a> From<&'a String> for JsValue {
fn from(s: &'a String) -> JsValue {
JsValue::from_str(s)
if_std! {
impl<'a> From<&'a String> for JsValue {
fn from(s: &'a String) -> JsValue {
JsValue::from_str(s)
}
}
}
@ -321,9 +339,23 @@ pub fn throw(s: &str) -> ! {
#[doc(hidden)]
pub mod __rt {
use std::cell::{Cell, UnsafeCell};
use std::mem;
use std::ops::{Deref, DerefMut};
use core::cell::{Cell, UnsafeCell};
use core::ops::{Deref, DerefMut};
pub extern crate core;
#[cfg(feature = "std")]
pub extern crate std;
#[macro_export]
#[cfg(feature = "std")]
macro_rules! __wbindgen_if_not_std {
($($i:item)*) => ()
}
#[macro_export]
#[cfg(not(feature = "std"))]
macro_rules! __wbindgen_if_not_std {
($($i:item)*) => ($($i)*)
}
#[inline]
pub fn assert_not_null<T>(s: *mut T) {
@ -457,20 +489,26 @@ pub mod __rt {
unsafe aliasing in rust");
}
#[no_mangle]
pub extern fn __wbindgen_malloc(size: usize) -> *mut u8 {
let mut ret = Vec::new();
if ret.try_reserve_exact(size).is_err() {
super::throw("invalid malloc request");
}
let ptr = ret.as_mut_ptr();
mem::forget(ret);
return ptr
}
if_std! {
use std::prelude::v1::*;
#[no_mangle]
pub unsafe extern fn __wbindgen_free(ptr: *mut u8, size: usize) {
drop(Vec::<u8>::from_raw_parts(ptr, 0, size));
#[no_mangle]
pub extern fn __wbindgen_malloc(size: usize) -> *mut u8 {
use core::mem;
let mut ret = Vec::new();
if ret.try_reserve_exact(size).is_err() {
super::throw("invalid malloc request");
}
let ptr = ret.as_mut_ptr();
mem::forget(ret);
return ptr
}
#[no_mangle]
pub unsafe extern fn __wbindgen_free(ptr: *mut u8, size: usize) {
drop(Vec::<u8>::from_raw_parts(ptr, 0, size));
}
}
pub fn link_this_library() {}

View File

@ -15,6 +15,7 @@ struct Project {
files: Vec<(String, String)>,
debug: bool,
node: bool,
no_std: bool,
}
fn project() -> Project {
@ -25,6 +26,7 @@ fn project() -> Project {
Project {
debug: true,
node: false,
no_std: false,
files: vec![
("Cargo.toml".to_string(), format!(r#"
[package]
@ -40,8 +42,7 @@ fn project() -> Project {
# XXX: It is important that `[dependencies]` is the last section
# here, so that `add_local_dependency` functions correctly!
[dependencies]
wasm-bindgen = {{ path = '{}' }}
"#, IDX.with(|x| *x), dir.display())),
"#, IDX.with(|x| *x))),
("Cargo.lock".to_string(), lockfile),
@ -138,6 +139,11 @@ impl Project {
self
}
fn no_std(&mut self, no_std: bool) -> &mut Project {
self.no_std = no_std;
self
}
fn add_local_dependency(&mut self, name: &str, path: &str) -> &mut Project {
{
let cargo_toml = self.files
@ -145,14 +151,28 @@ impl Project {
.find(|f| f.0 == "Cargo.toml")
.expect("should have Cargo.toml file!");
cargo_toml.1.push_str(name);
cargo_toml.1.push_str(" = { path = \"");
cargo_toml.1.push_str(" = { path = '");
cargo_toml.1.push_str(path);
cargo_toml.1.push_str("\" }");
cargo_toml.1.push_str("' }\n");
}
self
}
fn test(&mut self) {
{
let cargo_toml = self.files
.iter_mut()
.find(|f| f.0 == "Cargo.toml")
.expect("should have Cargo.toml file!");
cargo_toml.1.push_str("wasm-bindgen = { path = '");
cargo_toml.1.push_str(env!("CARGO_MANIFEST_DIR"));
if self.no_std {
cargo_toml.1.push_str("', default-features = false");
} else {
cargo_toml.1.push_str("'");
}
cargo_toml.1.push_str(" }\n");
}
let root = root();
drop(fs::remove_dir_all(&root));
for &(ref file, ref contents) in self.files.iter() {

View File

@ -201,3 +201,89 @@ fn other_exports() {
"#)
.test();
}
#[test]
fn no_std() {
project()
.no_std(true)
.file("src/lib.rs", r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
#![no_std]
#![allow(dead_code)]
extern crate wasm_bindgen;
extern crate std as _some_other_name;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
fn test(a: &str);
type Js;
#[wasm_bindgen(constructor)]
fn new() -> Js;
#[wasm_bindgen(method)]
fn init(this: &Js);
}
#[wasm_bindgen]
pub fn foo(_a: u32) {}
"#)
.file("test.ts", r#"
import * as wasm from "./out_bg";
export function test() {
// mostly just testing the project compiles here
wasm.foo(1);
}
"#)
.test();
}
#[test]
fn no_std_class() {
project()
.file("src/lib.rs", r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
#![no_std]
#![allow(dead_code)]
extern crate wasm_bindgen;
extern crate std as _some_other_name;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
fn test(a: &str);
type Js;
#[wasm_bindgen(constructor)]
fn new() -> Js;
#[wasm_bindgen(method, structural)]
fn init(this: &Js);
}
#[wasm_bindgen]
pub fn foo(_a: u32) {}
#[wasm_bindgen]
pub struct A {}
#[wasm_bindgen]
impl A {
pub fn foo(&self) {}
pub fn bar(&mut self) {}
}
"#)
.file("test.ts", r#"
import * as wasm from "./out_bg";
export function test() {
// mostly just testing the project compiles here
wasm.foo(1);
}
"#)
.test();
}