5
5
extern crate std;
6
6
7
7
use crate :: Error ;
8
- use core:: {
9
- mem:: MaybeUninit ,
10
- sync:: atomic:: { AtomicU8 , Ordering } ,
11
- } ;
8
+ use core:: mem:: MaybeUninit ;
12
9
use std:: thread_local;
13
10
14
11
pub use crate :: util:: { inner_u32, inner_u64} ;
15
12
16
13
#[ cfg( not( all( target_arch = "wasm32" , any( target_os = "unknown" , target_os = "none" ) ) ) ) ]
17
14
compile_error ! ( "`wasm_js` backend can be enabled only for OS-less WASM targets!" ) ;
18
15
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 } ;
21
18
22
19
// Size of our temporary Uint8Array buffer used with WebCrypto methods
23
20
// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
24
21
const CRYPTO_BUFFER_SIZE : u16 = 256 ;
25
22
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
-
32
23
pub fn fill_inner ( dest : & mut [ MaybeUninit < u8 > ] ) -> Result < ( ) , Error > {
33
24
CRYPTO . with ( |crypto| {
34
25
let crypto = crypto. as_ref ( ) . ok_or ( Error :: WEB_CRYPTO ) ?;
35
26
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 ( ) {
55
28
// getRandomValues does not work with all types of WASM memory,
56
29
// so we initially write to browser memory to avoid exceptions.
57
30
let buf = Uint8Array :: new_with_length ( CRYPTO_BUFFER_SIZE . into ( ) ) ;
@@ -86,7 +59,62 @@ pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
86
59
} )
87
60
}
88
61
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
+
89
116
#[ wasm_bindgen]
117
+ #[ rustfmt:: skip]
90
118
extern "C" {
91
119
// Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
92
120
type Crypto ;
@@ -98,4 +126,9 @@ extern "C" {
98
126
fn get_random_values ( this : & Crypto , buf : & Uint8Array ) -> Result < ( ) , JsValue > ;
99
127
#[ wasm_bindgen( method, js_name = getRandomValues, catch) ]
100
128
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" ;
101
134
}
0 commit comments