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