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