55extern crate std;
66
77use crate :: Error ;
8- use core:: {
9- mem:: MaybeUninit ,
10- sync:: atomic:: { AtomicU8 , Ordering } ,
11- } ;
8+ use core:: mem:: MaybeUninit ;
129use std:: thread_local;
1310
1411pub use crate :: util:: { inner_u32, inner_u64} ;
1512
1613#[ cfg( not( all( target_arch = "wasm32" , any( target_os = "unknown" , target_os = "none" ) ) ) ) ]
1714compile_error ! ( "`wasm_js` backend can be enabled only for OS-less WASM targets!" ) ;
1815
19- use js_sys:: { SharedArrayBuffer , Uint8Array , WebAssembly :: Memory } ;
20- use wasm_bindgen:: { prelude:: wasm_bindgen, JsCast , JsValue } ;
16+ use js_sys:: { JsString , Uint8Array } ;
17+ use wasm_bindgen:: { prelude:: wasm_bindgen, JsValue } ;
2118
2219// Size of our temporary Uint8Array buffer used with WebCrypto methods
2320// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
2421const CRYPTO_BUFFER_SIZE : u16 = 256 ;
2522
26- const MEMORY_KIND_UNINIT : u8 = 0 ;
27- const MEMORY_KIND_NOT_SHARED : u8 = 1 ;
28- const MEMORY_KIND_SHARED : u8 = 2 ;
29-
30- static MEMORY_KIND : AtomicU8 = AtomicU8 :: new ( 0 ) ;
31-
3223pub fn fill_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
3324 CRYPTO . with ( |crypto| {
3425 let crypto = crypto. as_ref ( ) . ok_or ( Error :: WEB_CRYPTO ) ?;
3526
36- let shared = loop {
37- break match MEMORY_KIND . load ( Ordering :: Relaxed ) {
38- MEMORY_KIND_NOT_SHARED => false ,
39- MEMORY_KIND_SHARED => true ,
40- MEMORY_KIND_UNINIT => {
41- let memory: Memory = wasm_bindgen:: memory ( ) . unchecked_into ( ) ;
42- let val = if memory. buffer ( ) . is_instance_of :: < SharedArrayBuffer > ( ) {
43- MEMORY_KIND_SHARED
44- } else {
45- MEMORY_KIND_NOT_SHARED
46- } ;
47- MEMORY_KIND . store ( val, Ordering :: Relaxed ) ;
48- continue ;
49- }
50- _ => unreachable ! ( ) ,
51- } ;
52- } ;
53-
54- if shared {
27+ if is_sab ( ) {
5528 // getRandomValues does not work with all types of WASM memory,
5629 // so we initially write to browser memory to avoid exceptions.
5730 let buf = Uint8Array :: new_with_length ( CRYPTO_BUFFER_SIZE . into ( ) ) ;
@@ -86,7 +59,62 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
8659 } )
8760}
8861
62+ #[ cfg( not( target_feature = "atomics" ) ) ]
63+ fn is_sab ( ) -> bool {
64+ use core:: sync:: atomic:: { AtomicU8 , Ordering } ;
65+
66+ use js_sys:: WebAssembly :: Memory ;
67+ use js_sys:: { Object , SharedArrayBuffer } ;
68+ use wasm_bindgen:: JsCast ;
69+
70+ const MEMORY_KIND_UNINIT : u8 = 0 ;
71+ const MEMORY_KIND_NOT_SHARED : u8 = 1 ;
72+ const MEMORY_KIND_SHARED : u8 = 2 ;
73+
74+ static MEMORY_KIND : AtomicU8 = AtomicU8 :: new ( 0 ) ;
75+
76+ loop {
77+ break match MEMORY_KIND . load ( Ordering :: Relaxed ) {
78+ MEMORY_KIND_NOT_SHARED => false ,
79+ MEMORY_KIND_SHARED => true ,
80+ MEMORY_KIND_UNINIT => {
81+ let buffer = wasm_bindgen:: memory ( ) . unchecked_into :: < Memory > ( ) . buffer ( ) ;
82+
83+ // `SharedArrayBuffer` is only available with COOP & COEP. But even without its
84+ // possible to create a shared `WebAssembly.Memory`, so we check for that via
85+ // the constructor name.
86+ //
87+ // Keep in mind that `crossOriginIsolated` is not available on Node.js, in
88+ // which case we can still use `instanceof` because `SharedArrayBuffer` is
89+ // always available.
90+ let shared = match CROSS_ORIGIN_ISOLATED . with ( Option :: clone) {
91+ Some ( true ) | None => buffer. is_instance_of :: < SharedArrayBuffer > ( ) ,
92+ Some ( false ) => {
93+ let constructor_name = Object :: from ( buffer) . constructor ( ) . name ( ) ;
94+ SHARED_ARRAY_BUFFER_NAME . with ( |name| & constructor_name == name)
95+ }
96+ } ;
97+
98+ let val = if shared {
99+ MEMORY_KIND_SHARED
100+ } else {
101+ MEMORY_KIND_NOT_SHARED
102+ } ;
103+ MEMORY_KIND . store ( val, Ordering :: Relaxed ) ;
104+ continue ;
105+ }
106+ _ => unreachable ! ( ) ,
107+ } ;
108+ }
109+ }
110+
111+ #[ cfg( target_feature = "atomics" ) ]
112+ fn is_sab ( ) -> bool {
113+ true
114+ }
115+
89116#[ wasm_bindgen]
117+ #[ rustfmt:: skip]
90118extern "C" {
91119 // Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
92120 type Crypto ;
@@ -98,4 +126,9 @@ extern "C" {
98126 fn get_random_values ( this : & Crypto , buf : & Uint8Array ) -> Result < ( ) , JsValue > ;
99127 #[ wasm_bindgen( method, js_name = getRandomValues, catch) ]
100128 fn get_random_values_ref ( this : & Crypto , buf : & mut [ u8 ] ) -> Result < ( ) , JsValue > ;
129+ // Returns the [`crossOriginIsolated`](https://developer.mozilla.org/en-US/docs/Web/API/crossOriginIsolated) global property.
130+ #[ wasm_bindgen( thread_local_v2, js_namespace = globalThis, js_name = crossOriginIsolated) ]
131+ static CROSS_ORIGIN_ISOLATED : Option < bool > ;
132+ #[ wasm_bindgen( thread_local_v2, static_string) ]
133+ static SHARED_ARRAY_BUFFER_NAME : JsString = "SharedArrayBuffer" ;
101134}
0 commit comments