diff --git a/.travis.yml b/.travis.yml index d71f20e0781..7520fa49129 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,13 @@ matrix: - cargo doc --no-deps --all-features after_success: - travis-cargo --only nightly doc-upload + + - rust: nightly + install: + - rustup target add wasm32-unknown-unknown + - cargo install --force cargo-web + script: + - cargo web test --target wasm32-unknown-unknown --nodejs # Trust cross-built/emulated targets. We must repeat all non-default values. - rust: stable diff --git a/Cargo.toml b/Cargo.toml index 49ecd95aa07..9fdac8a04ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,3 +48,6 @@ cloudabi = "0.0.3" [target.'cfg(target_os = "fuchsia")'.dependencies] fuchsia-zircon = "0.3.2" + +[target.wasm32-unknown-unknown.dependencies] +stdweb = "0.4" diff --git a/src/lib.rs b/src/lib.rs index caedb4d842b..57e0fee9624 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,6 +249,7 @@ #![cfg_attr(not(feature="std"), no_std)] #![cfg_attr(all(feature="alloc", not(feature="std")), feature(alloc))] #![cfg_attr(feature = "i128_support", feature(i128_type, i128))] +#![cfg_attr(all(target_arch = "wasm32", not(target_os = "emscripten")), recursion_limit="128")] #[cfg(feature="std")] extern crate std as core; #[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc; @@ -257,6 +258,10 @@ #[cfg(feature="serde-1")] extern crate serde; #[cfg(feature="serde-1")] #[macro_use] extern crate serde_derive; +#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] +#[macro_use] +extern crate stdweb; + #[cfg(feature = "log")] #[macro_use] extern crate log; #[cfg(not(feature = "log"))] macro_rules! trace { ($($x:tt)*) => () } #[cfg(not(feature = "log"))] macro_rules! debug { ($($x:tt)*) => () } @@ -1553,6 +1558,7 @@ mod test { #[test] #[cfg(feature="std")] + #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] fn test_thread_rng() { let mut r = thread_rng(); r.gen::(); diff --git a/src/os.rs b/src/os.rs index ea5e9eac22e..ddfbd12bf4a 100644 --- a/src/os.rs +++ b/src/os.rs @@ -584,27 +584,98 @@ mod imp { #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] mod imp { - use std::io; + use std::mem; + use stdweb::unstable::TryInto; + use stdweb::web::error::Error as WebError; + use {Error, ErrorKind}; #[derive(Debug)] - pub struct OsRng; + enum OsRngInner { + Browser, + Node + } + + #[derive(Debug)] + pub struct OsRng(OsRngInner); impl OsRng { pub fn new() -> Result { - Err(Error::new(ErrorKind::Unavailable, "not supported on WASM")) + let result = js! { + try { + if ( + typeof window === "object" && + typeof window.crypto === "object" && + typeof window.crypto.getRandomValues === "function" + ) { + return { success: true, ty: 1 }; + } + + if (typeof require("crypto").randomBytes === "function") { + return { success: true, ty: 2 }; + } + + return { success: false, error: new Error("not supported") }; + } catch(err) { + return { success: false, error: err }; + } + }; + + if js!{ return @{ result.as_ref() }.success } == true { + let ty = js!{ return @{ result }.ty }; + + if ty == 1 { Ok(OsRng(OsRngInner::Browser)) } + else if ty == 2 { Ok(OsRng(OsRngInner::Node)) } + else { unreachable!() } + } else { + let err: WebError = js!{ return @{ result }.error }.try_into().unwrap(); + Err(Error::with_cause(ErrorKind::Unavailable, "WASM Error", err)) + } } + pub fn try_fill_bytes(&mut self, v: &mut [u8]) -> Result<(), Error> { - Err(Error::new(ErrorKind::Unavailable, "not supported on WASM")) + assert_eq!(mem::size_of::(), 4); + + let len = v.len() as u32; + let ptr = v.as_mut_ptr() as i32; + + let result = match self.0 { + OsRngInner::Browser => js! { + try { + let array = new Uint8Array(@{ len }); + window.crypto.getRandomValues(array); + HEAPU8.set(array, @{ ptr }); + + return { success: true }; + } catch(err) { + return { success: false, error: err }; + } + }, + OsRngInner::Node => js! { + try { + let bytes = require("crypto").randomBytes(@{ len }); + HEAPU8.set(new Uint8Array(bytes), @{ ptr }); + + return { success: true }; + } catch(err) { + return { success: false, error: err }; + } + } + }; + + if js!{ return @{ result.as_ref() }.success } == true { + Ok(()) + } else { + let err: WebError = js!{ return @{ result }.error }.try_into().unwrap(); + Err(Error::with_cause(ErrorKind::Unexpected, "WASM Error", err)) + } } } } #[cfg(test)] mod test { - use std::sync::mpsc::channel; use RngCore; use OsRng; - use std::thread; #[test] fn test_os_rng() { @@ -615,10 +686,18 @@ mod test { let mut v = [0u8; 1000]; r.fill_bytes(&mut v); + + let mut v2 = [0u8; 1000]; + r.fill_bytes(&mut v2); + + assert_ne!(v[..], v2[..]); } + #[cfg(not(any(target_arch = "wasm32", target_arch = "asmjs")))] #[test] fn test_os_rng_tasks() { + use std::sync::mpsc::channel; + use std::thread; let mut txs = vec!(); for _ in 0..20 { diff --git a/src/seq.rs b/src/seq.rs index 8562f55a19b..6149ba36264 100644 --- a/src/seq.rs +++ b/src/seq.rs @@ -257,8 +257,8 @@ mod test { let mut r = ::test::rng(402); // sample 0 items - assert_eq!(&sample_slice(&mut r, empty, 0)[..], []); - assert_eq!(&sample_slice(&mut r, &[42, 2, 42], 0)[..], []); + assert_eq!(&sample_slice(&mut r, empty, 0)[..], [0u8; 0]); + assert_eq!(&sample_slice(&mut r, &[42, 2, 42], 0)[..], [0u8; 0]); // sample 1 item assert_eq!(&sample_slice(&mut r, &[42], 1)[..], [42]); @@ -269,12 +269,12 @@ mod test { let v = sample_slice(&mut r, &[42, 133], 2); assert!(&v[..] == [42, 133] || v[..] == [133, 42]); - assert_eq!(&sample_indices_inplace(&mut r, 0, 0)[..], []); - assert_eq!(&sample_indices_inplace(&mut r, 1, 0)[..], []); + assert_eq!(&sample_indices_inplace(&mut r, 0, 0)[..], [0usize; 0]); + assert_eq!(&sample_indices_inplace(&mut r, 1, 0)[..], [0usize; 0]); assert_eq!(&sample_indices_inplace(&mut r, 1, 1)[..], [0]); - assert_eq!(&sample_indices_cache(&mut r, 0, 0)[..], []); - assert_eq!(&sample_indices_cache(&mut r, 1, 0)[..], []); + assert_eq!(&sample_indices_cache(&mut r, 0, 0)[..], [0usize; 0]); + assert_eq!(&sample_indices_cache(&mut r, 1, 0)[..], [0usize; 0]); assert_eq!(&sample_indices_cache(&mut r, 1, 1)[..], [0]); // Make sure lucky 777's aren't lucky