Skip to content

Commit d78953e

Browse files
committed
Add support for wasm-bindgen
This commit adds support to implement the `rand` crate on the wasm32-unknown-unknown target with the `wasm-bindgen`-based runtime. This supports being run both in node.js as well as browsers by delegating appropriately. Closes #478
1 parent 0faff20 commit d78953e

File tree

8 files changed

+200
-40
lines changed

8 files changed

+200
-40
lines changed

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ matrix:
5656
script:
5757
- cargo web test --target wasm32-unknown-unknown --nodejs --features=stdweb
5858

59+
- rust: nightly
60+
install:
61+
- rustup target add wasm32-unknown-unknown
62+
script:
63+
- cargo build --target wasm32-unknown-unknown --features wasm-bindgen
64+
5965
- rust: nightly
6066
install:
6167
- rustup target add thumbv6m-none-eabi

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ fuchsia-zircon = { version = "0.3.2", optional = true }
5050
[target.wasm32-unknown-unknown.dependencies]
5151
# use with `--target wasm32-unknown-unknown --features=stdweb`
5252
stdweb = { version = "0.4", optional = true }
53+
wasm-bindgen = { version = "0.2", optional = true }
5354

5455
[dev-dependencies]
5556
# This is for testing serde, unfortunately we can't specify feature-gated dev

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,12 @@ optional features are available:
117117
- `log` enables some logging via the `log` crate.
118118
- `nightly` enables all unstable features (`i128_support`).
119119
- `serde1` enables serialization for some types, via Serde version 1.
120-
- `stdweb` enables support for `OsRng` on `wasm-unknown-unknown` via `stdweb`
120+
- `stdweb` enables support for `OsRng` on `wasm32-unknown-unknown` via `stdweb`
121121
combined with `cargo-web`.
122+
- `wasm-bindgen` enables support for `OsRng` on `wasm32-unknown-unknown` via
123+
[`wasm-bindgen`]
124+
125+
[`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen
122126

123127
`no_std` mode is activated by setting `default-features = false`; this removes
124128
functionality depending on `std`:

src/deprecated.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ impl CryptoRng for StdRng {}
263263
target_os = "redox",
264264
target_os = "fuchsia",
265265
windows,
266-
all(target_arch = "wasm32", feature = "stdweb")
266+
all(target_arch = "wasm32", feature = "stdweb"),
267+
all(target_arch = "wasm32", feature = "wasm-bindgen"),
267268
)))]
268269
#[derive(Clone, Debug)]
269270
#[deprecated(since="0.6.0", note="import with rand::rngs::OsRng instead")]
@@ -283,7 +284,8 @@ pub struct OsRng(rngs::OsRng);
283284
target_os = "redox",
284285
target_os = "fuchsia",
285286
windows,
286-
all(target_arch = "wasm32", feature = "stdweb")
287+
all(target_arch = "wasm32", feature = "stdweb"),
288+
all(target_arch = "wasm32", feature = "wasm-bindgen"),
287289
)))]
288290
#[cfg(feature="std")]
289291
impl RngCore for OsRng {
@@ -322,7 +324,8 @@ impl RngCore for OsRng {
322324
target_os = "redox",
323325
target_os = "fuchsia",
324326
windows,
325-
all(target_arch = "wasm32", feature = "stdweb")
327+
all(target_arch = "wasm32", feature = "stdweb"),
328+
all(target_arch = "wasm32", feature = "wasm-bindgen"),
326329
)))]
327330
#[cfg(feature="std")]
328331
impl OsRng {
@@ -345,7 +348,8 @@ impl OsRng {
345348
target_os = "redox",
346349
target_os = "fuchsia",
347350
windows,
348-
all(target_arch = "wasm32", feature = "stdweb")
351+
all(target_arch = "wasm32", feature = "stdweb"),
352+
all(target_arch = "wasm32", feature = "wasm-bindgen"),
349353
)))]
350354
#[cfg(feature="std")]
351355
impl CryptoRng for OsRng {}

src/lib.rs

Lines changed: 98 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
//!
8585
//! The [`distributions` module] provides implementations
8686
//! of some other distributions, including Normal, Log-Normal and Exponential.
87-
//!
87+
//!
8888
//! It is worth noting that the functionality already mentioned is implemented
8989
//! with distributions: [`gen`] samples values using the [`Standard`]
9090
//! distribution, while [`gen_range`] uses [`Uniform`].
@@ -109,7 +109,7 @@
109109
//!
110110
//! // thread_rng is often the most convenient source of randomness:
111111
//! let mut rng = thread_rng();
112-
//!
112+
//!
113113
//! if rng.gen() { // random bool
114114
//! let x: f64 = rng.gen(); // random number in range [0, 1)
115115
//! println!("x is: {}", x);
@@ -230,6 +230,9 @@
230230
#![cfg_attr(all(feature="i128_support", feature="nightly"), feature(i128_type, i128))]
231231
#![cfg_attr(all(feature="simd_support", feature="nightly"), feature(stdsimd))]
232232
#![cfg_attr(feature = "stdweb", recursion_limit="128")]
233+
#![cfg_attr(feature = "wasm-bindgen", feature(proc_macro))]
234+
#![cfg_attr(feature = "wasm-bindgen", feature(wasm_import_module))]
235+
#![cfg_attr(feature = "wasm-bindgen", feature(wasm_custom_section))]
233236

234237
#[cfg(feature="std")] extern crate std as core;
235238
#[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc;
@@ -242,6 +245,9 @@
242245
#[macro_use]
243246
extern crate stdweb;
244247

248+
#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
249+
extern crate wasm_bindgen;
250+
245251
extern crate rand_core;
246252

247253
#[cfg(feature = "log")] #[macro_use] extern crate log;
@@ -297,7 +303,8 @@ pub mod seq;
297303
target_os = "redox",
298304
target_os = "fuchsia",
299305
windows,
300-
all(target_arch = "wasm32", feature = "stdweb")
306+
all(target_arch = "wasm32", feature = "stdweb"),
307+
all(target_arch = "wasm32", feature = "wasm-bindgen"),
301308
)))]
302309
#[doc(hidden)]
303310
pub use deprecated::OsRng;
@@ -329,7 +336,8 @@ pub mod jitter {
329336
target_os = "redox",
330337
target_os = "fuchsia",
331338
windows,
332-
all(target_arch = "wasm32", feature = "stdweb")
339+
all(target_arch = "wasm32", feature = "stdweb"),
340+
all(target_arch = "wasm32", feature = "wasm-bindgen"),
333341
)))]
334342
#[doc(hidden)]
335343
pub mod os {
@@ -364,20 +372,20 @@ use distributions::uniform::{SampleUniform, UniformSampler, SampleBorrow};
364372

365373
/// An automatically-implemented extension trait on [`RngCore`] providing high-level
366374
/// generic methods for sampling values and other convenience methods.
367-
///
375+
///
368376
/// This is the primary trait to use when generating random values.
369-
///
377+
///
370378
/// # Generic usage
371-
///
379+
///
372380
/// The basic pattern is `fn foo<R: Rng + ?Sized>(rng: &mut R)`. Some
373381
/// things are worth noting here:
374-
///
382+
///
375383
/// - Since `Rng: RngCore` and every `RngCore` implements `Rng`, it makes no
376384
/// difference whether we use `R: Rng` or `R: RngCore`.
377385
/// - The `+ ?Sized` un-bounding allows functions to be called directly on
378386
/// type-erased references; i.e. `foo(r)` where `r: &mut RngCore`. Without
379387
/// this it would be necessary to write `foo(&mut r)`.
380-
///
388+
///
381389
/// An alternative pattern is possible: `fn foo<R: Rng>(rng: R)`. This has some
382390
/// trade-offs. It allows the argument to be consumed directly without a `&mut`
383391
/// (which is how `from_rng(thread_rng())` works); also it still works directly
@@ -386,20 +394,20 @@ use distributions::uniform::{SampleUniform, UniformSampler, SampleBorrow};
386394
/// hence many uses of `rng` require an extra reference, either explicitly
387395
/// (`distr.sample(&mut rng)`) or implicitly (`rng.gen()`); one may hope the
388396
/// optimiser can remove redundant references later.
389-
///
397+
///
390398
/// Example:
391-
///
399+
///
392400
/// ```
393401
/// # use rand::thread_rng;
394402
/// use rand::Rng;
395-
///
403+
///
396404
/// fn foo<R: Rng + ?Sized>(rng: &mut R) -> f32 {
397405
/// rng.gen()
398406
/// }
399407
///
400408
/// # let v = foo(&mut thread_rng());
401409
/// ```
402-
///
410+
///
403411
/// [`RngCore`]: trait.RngCore.html
404412
pub trait Rng: RngCore {
405413
/// Return a random value supporting the [`Standard`] distribution.
@@ -624,7 +632,7 @@ pub trait Rng: RngCore {
624632
/// Return a random element from `values`.
625633
///
626634
/// Deprecated: use [`SliceRandom::choose`] instead.
627-
///
635+
///
628636
/// [`SliceRandom::choose`]: seq/trait.SliceRandom.html#method.choose
629637
#[deprecated(since="0.6.0", note="use SliceRandom::choose instead")]
630638
fn choose<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> {
@@ -635,7 +643,7 @@ pub trait Rng: RngCore {
635643
/// Return a mutable pointer to a random element from `values`.
636644
///
637645
/// Deprecated: use [`SliceRandom::choose_mut`] instead.
638-
///
646+
///
639647
/// [`SliceRandom::choose_mut`]: seq/trait.SliceRandom.html#method.choose_mut
640648
#[deprecated(since="0.6.0", note="use SliceRandom::choose_mut instead")]
641649
fn choose_mut<'a, T>(&mut self, values: &'a mut [T]) -> Option<&'a mut T> {
@@ -646,7 +654,7 @@ pub trait Rng: RngCore {
646654
/// Shuffle a mutable slice in place.
647655
///
648656
/// Deprecated: use [`SliceRandom::shuffle`] instead.
649-
///
657+
///
650658
/// [`SliceRandom::shuffle`]: seq/trait.SliceRandom.html#method.shuffle
651659
#[deprecated(since="0.6.0", note="use SliceRandom::shuffle instead")]
652660
fn shuffle<T>(&mut self, values: &mut [T]) {
@@ -658,15 +666,15 @@ pub trait Rng: RngCore {
658666
impl<R: RngCore + ?Sized> Rng for R {}
659667

660668
/// Trait for casting types to byte slices
661-
///
669+
///
662670
/// This is used by the [`fill`] and [`try_fill`] methods.
663-
///
671+
///
664672
/// [`fill`]: trait.Rng.html#method.fill
665673
/// [`try_fill`]: trait.Rng.html#method.try_fill
666674
pub trait AsByteSliceMut {
667675
/// Return a mutable reference to self as a byte slice
668676
fn as_byte_slice_mut(&mut self) -> &mut [u8];
669-
677+
670678
/// Call `to_le` on each element (i.e. byte-swap on Big Endian platforms).
671679
fn to_le(&mut self);
672680
}
@@ -675,7 +683,7 @@ impl AsByteSliceMut for [u8] {
675683
fn as_byte_slice_mut(&mut self) -> &mut [u8] {
676684
self
677685
}
678-
686+
679687
fn to_le(&mut self) {}
680688
}
681689

@@ -698,7 +706,7 @@ macro_rules! impl_as_byte_slice {
698706
}
699707
}
700708
}
701-
709+
702710
fn to_le(&mut self) {
703711
for x in self {
704712
*x = x.to_le();
@@ -724,7 +732,7 @@ macro_rules! impl_as_byte_slice_arrays {
724732
($n:expr,) => {};
725733
($n:expr, $N:ident, $($NN:ident,)*) => {
726734
impl_as_byte_slice_arrays!($n - 1, $($NN,)*);
727-
735+
728736
impl<T> AsByteSliceMut for [T; $n] where [T]: AsByteSliceMut {
729737
fn as_byte_slice_mut(&mut self) -> &mut [u8] {
730738
self[..].as_byte_slice_mut()
@@ -743,7 +751,7 @@ macro_rules! impl_as_byte_slice_arrays {
743751
fn as_byte_slice_mut(&mut self) -> &mut [u8] {
744752
self[..].as_byte_slice_mut()
745753
}
746-
754+
747755
fn to_le(&mut self) {
748756
self[..].to_le()
749757
}
@@ -874,6 +882,68 @@ pub fn random<T>() -> T where Standard: Distribution<T> {
874882
thread_rng().gen()
875883
}
876884

885+
// Due to rustwasm/wasm-bindgen#201 this can't be defined in the inner os
886+
// modules, so hack around it for now and place it at the root.
887+
#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
888+
#[doc(hidden)]
889+
#[allow(missing_debug_implementations)]
890+
pub mod __wbg_shims {
891+
892+
// `extern { type Foo; }` isn't supported on 1.22 syntactically, so use a
893+
// macro to work around that.
894+
macro_rules! rust_122_compat {
895+
($($t:tt)*) => ($($t)*)
896+
}
897+
898+
rust_122_compat! {
899+
extern crate wasm_bindgen;
900+
901+
pub use wasm_bindgen::prelude::*;
902+
903+
#[wasm_bindgen]
904+
extern {
905+
pub type This;
906+
pub static this: This;
907+
908+
#[wasm_bindgen(method, getter, structural)]
909+
pub fn window(me: &This) -> JsValue;
910+
#[wasm_bindgen(method, getter, structural)]
911+
pub fn crypto(me: &This) -> JsValue;
912+
913+
pub type BrowserCrypto;
914+
915+
// TODO: these `structural` annotations here ideally wouldn't be here to
916+
// avoid a JS shim, but for now with feature detection they're
917+
// unavoidable.
918+
#[wasm_bindgen(method, js_name = getRandomValues, structural, getter)]
919+
pub fn get_random_values_fn(me: &BrowserCrypto) -> JsValue;
920+
#[wasm_bindgen(method, js_name = getRandomValues, structural)]
921+
pub fn get_random_values(me: &BrowserCrypto, buf: &mut [u8]);
922+
923+
#[wasm_bindgen(js_name = require)]
924+
pub fn node_require(s: &str) -> NodeCrypto;
925+
926+
pub type NodeCrypto;
927+
928+
#[wasm_bindgen(method, js_name = randomFillSync, structural)]
929+
pub fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]);
930+
}
931+
932+
// TODO: replace with derive once rustwasm/wasm-bindgen#400 is merged
933+
impl Clone for BrowserCrypto {
934+
fn clone(&self) -> BrowserCrypto {
935+
BrowserCrypto { obj: self.obj.clone() }
936+
}
937+
}
938+
939+
impl Clone for NodeCrypto {
940+
fn clone(&self) -> NodeCrypto {
941+
NodeCrypto { obj: self.obj.clone() }
942+
}
943+
}
944+
}
945+
}
946+
877947
#[cfg(test)]
878948
mod test {
879949
use rngs::mock::StepRng;
@@ -936,25 +1006,25 @@ mod test {
9361006
}
9371007
}
9381008
}
939-
1009+
9401010
#[test]
9411011
fn test_fill() {
9421012
let x = 9041086907909331047; // a random u64
9431013
let mut rng = StepRng::new(x, 0);
944-
1014+
9451015
// Convert to byte sequence and back to u64; byte-swap twice if BE.
9461016
let mut array = [0u64; 2];
9471017
rng.fill(&mut array[..]);
9481018
assert_eq!(array, [x, x]);
9491019
assert_eq!(rng.next_u64(), x);
950-
1020+
9511021
// Convert to bytes then u32 in LE order
9521022
let mut array = [0u32; 2];
9531023
rng.fill(&mut array[..]);
9541024
assert_eq!(array, [x as u32, (x >> 32) as u32]);
9551025
assert_eq!(rng.next_u32(), x as u32);
9561026
}
957-
1027+
9581028
#[test]
9591029
fn test_fill_empty() {
9601030
let mut array = [0u32; 0];
@@ -1029,7 +1099,7 @@ mod test {
10291099
assert_eq!(r.gen_range(0, 1), 0);
10301100
let _c: u8 = Standard.sample(&mut r);
10311101
}
1032-
1102+
10331103
#[test]
10341104
#[cfg(feature="std")]
10351105
fn test_random() {

0 commit comments

Comments
 (0)