]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/wasm/mod.rs
Simplify SaveHandler trait
[rust.git] / src / libstd / sys / wasm / mod.rs
1 //! System bindings for the wasm/web platform
2 //!
3 //! This module contains the facade (aka platform-specific) implementations of
4 //! OS level functionality for wasm. Note that this wasm is *not* the emscripten
5 //! wasm, so we have no runtime here.
6 //!
7 //! This is all super highly experimental and not actually intended for
8 //! wide/production use yet, it's still all in the experimental category. This
9 //! will likely change over time.
10 //!
11 //! Currently all functions here are basically stubs that immediately return
12 //! errors. The hope is that with a portability lint we can turn actually just
13 //! remove all this and just omit parts of the standard library if we're
14 //! compiling for wasm. That way it's a compile time error for something that's
15 //! guaranteed to be a runtime error!
16
17 use crate::os::raw::c_char;
18 use crate::ptr;
19 use crate::sys::os_str::Buf;
20 use crate::sys_common::{AsInner, FromInner};
21 use crate::ffi::{OsString, OsStr};
22 use crate::time::Duration;
23
24 pub mod alloc;
25 pub mod args;
26 pub mod cmath;
27 pub mod env;
28 pub mod fs;
29 pub mod io;
30 pub mod memchr;
31 pub mod net;
32 pub mod os;
33 pub mod path;
34 pub mod pipe;
35 pub mod process;
36 pub mod stack_overflow;
37 pub mod thread;
38 pub mod time;
39 pub mod stdio;
40
41 pub use crate::sys_common::os_str_bytes as os_str;
42
43 cfg_if::cfg_if! {
44     if #[cfg(target_feature = "atomics")] {
45         #[path = "condvar_atomics.rs"]
46         pub mod condvar;
47         #[path = "mutex_atomics.rs"]
48         pub mod mutex;
49         #[path = "rwlock_atomics.rs"]
50         pub mod rwlock;
51         #[path = "thread_local_atomics.rs"]
52         pub mod thread_local;
53     } else {
54         pub mod condvar;
55         pub mod mutex;
56         pub mod rwlock;
57         pub mod thread_local;
58     }
59 }
60
61 #[cfg(not(test))]
62 pub fn init() {
63 }
64
65 pub fn unsupported<T>() -> crate::io::Result<T> {
66     Err(unsupported_err())
67 }
68
69 pub fn unsupported_err() -> crate::io::Error {
70     crate::io::Error::new(crate::io::ErrorKind::Other,
71                    "operation not supported on wasm yet")
72 }
73
74 pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {
75     crate::io::ErrorKind::Other
76 }
77
78 // This enum is used as the storage for a bunch of types which can't actually
79 // exist.
80 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
81 pub enum Void {}
82
83 pub unsafe fn strlen(mut s: *const c_char) -> usize {
84     let mut n = 0;
85     while *s != 0 {
86         n += 1;
87         s = s.offset(1);
88     }
89     return n
90 }
91
92 pub unsafe fn abort_internal() -> ! {
93     ExitSysCall::perform(1)
94 }
95
96 // We don't have randomness yet, but I totally used a random number generator to
97 // generate these numbers.
98 //
99 // More seriously though this is just for DOS protection in hash maps. It's ok
100 // if we don't do that on wasm just yet.
101 pub fn hashmap_random_keys() -> (u64, u64) {
102     (1, 2)
103 }
104
105 // Implement a minimal set of system calls to enable basic IO
106 pub enum SysCallIndex {
107     Read = 0,
108     Write = 1,
109     Exit = 2,
110     Args = 3,
111     GetEnv = 4,
112     SetEnv = 5,
113     Time = 6,
114 }
115
116 #[repr(C)]
117 pub struct ReadSysCall {
118     fd: usize,
119     ptr: *mut u8,
120     len: usize,
121     result: usize,
122 }
123
124 impl ReadSysCall {
125     pub fn perform(fd: usize, buffer: &mut [u8]) -> usize {
126         let mut call_record = ReadSysCall {
127             fd,
128             len: buffer.len(),
129             ptr: buffer.as_mut_ptr(),
130             result: 0
131         };
132         if unsafe { syscall(SysCallIndex::Read, &mut call_record) } {
133             call_record.result
134         } else {
135             0
136         }
137     }
138 }
139
140 #[repr(C)]
141 pub struct WriteSysCall {
142     fd: usize,
143     ptr: *const u8,
144     len: usize,
145 }
146
147 impl WriteSysCall {
148     pub fn perform(fd: usize, buffer: &[u8]) {
149         let mut call_record = WriteSysCall {
150             fd,
151             len: buffer.len(),
152             ptr: buffer.as_ptr()
153         };
154         unsafe { syscall(SysCallIndex::Write, &mut call_record); }
155     }
156 }
157
158 #[repr(C)]
159 pub struct ExitSysCall {
160     code: usize,
161 }
162
163 impl ExitSysCall {
164     pub fn perform(code: usize) -> ! {
165         let mut call_record = ExitSysCall {
166             code
167         };
168         unsafe {
169             syscall(SysCallIndex::Exit, &mut call_record);
170             crate::intrinsics::abort();
171         }
172     }
173 }
174
175 fn receive_buffer<E, F: FnMut(&mut [u8]) -> Result<usize, E>>(estimate: usize, mut f: F)
176     -> Result<Vec<u8>, E>
177 {
178     let mut buffer = vec![0; estimate];
179     loop {
180         let result = f(&mut buffer)?;
181         if result <= buffer.len() {
182             buffer.truncate(result);
183             break;
184         }
185         buffer.resize(result, 0);
186     }
187     Ok(buffer)
188 }
189
190 #[repr(C)]
191 pub struct ArgsSysCall {
192     ptr: *mut u8,
193     len: usize,
194     result: usize
195 }
196
197 impl ArgsSysCall {
198     pub fn perform() -> Vec<OsString> {
199         receive_buffer(1024, |buffer| -> Result<usize, !> {
200             let mut call_record = ArgsSysCall {
201                 len: buffer.len(),
202                 ptr: buffer.as_mut_ptr(),
203                 result: 0
204             };
205             if unsafe { syscall(SysCallIndex::Args, &mut call_record) } {
206                 Ok(call_record.result)
207             } else {
208                 Ok(0)
209             }
210         })
211             .unwrap()
212             .split(|b| *b == 0)
213             .map(|s| FromInner::from_inner(Buf { inner: s.to_owned() }))
214             .collect()
215     }
216 }
217
218 #[repr(C)]
219 pub struct GetEnvSysCall {
220     key_ptr: *const u8,
221     key_len: usize,
222     value_ptr: *mut u8,
223     value_len: usize,
224     result: usize
225 }
226
227 impl GetEnvSysCall {
228     pub fn perform(key: &OsStr) -> Option<OsString> {
229         let key_buf = &AsInner::as_inner(key).inner;
230         receive_buffer(64, |buffer| {
231             let mut call_record = GetEnvSysCall {
232                 key_len: key_buf.len(),
233                 key_ptr: key_buf.as_ptr(),
234                 value_len: buffer.len(),
235                 value_ptr: buffer.as_mut_ptr(),
236                 result: !0usize
237             };
238             if unsafe { syscall(SysCallIndex::GetEnv, &mut call_record) } {
239                 if call_record.result == !0usize {
240                     Err(())
241                 } else {
242                     Ok(call_record.result)
243                 }
244             } else {
245                 Err(())
246             }
247         }).ok().map(|s| {
248             FromInner::from_inner(Buf { inner: s })
249         })
250     }
251 }
252
253 #[repr(C)]
254 pub struct SetEnvSysCall {
255     key_ptr: *const u8,
256     key_len: usize,
257     value_ptr: *const u8,
258     value_len: usize
259 }
260
261 impl SetEnvSysCall {
262     pub fn perform(key: &OsStr, value: Option<&OsStr>) {
263         let key_buf = &AsInner::as_inner(key).inner;
264         let value_buf = value.map(|v| &AsInner::as_inner(v).inner);
265         let mut call_record = SetEnvSysCall {
266             key_len: key_buf.len(),
267             key_ptr: key_buf.as_ptr(),
268             value_len: value_buf.map(|v| v.len()).unwrap_or(!0usize),
269             value_ptr: value_buf.map(|v| v.as_ptr()).unwrap_or(ptr::null())
270         };
271         unsafe { syscall(SysCallIndex::SetEnv, &mut call_record); }
272     }
273 }
274
275 pub enum TimeClock {
276     Monotonic = 0,
277     System = 1,
278 }
279
280 #[repr(C)]
281 pub struct TimeSysCall {
282     clock: usize,
283     secs_hi: usize,
284     secs_lo: usize,
285     nanos: usize
286 }
287
288 impl TimeSysCall {
289     pub fn perform(clock: TimeClock) -> Duration {
290         let mut call_record = TimeSysCall {
291             clock: clock as usize,
292             secs_hi: 0,
293             secs_lo: 0,
294             nanos: 0
295         };
296         if unsafe { syscall(SysCallIndex::Time, &mut call_record) } {
297             Duration::new(
298                 ((call_record.secs_hi as u64) << 32) | (call_record.secs_lo as u64),
299                 call_record.nanos as u32
300             )
301         } else {
302             panic!("Time system call is not implemented by WebAssembly host");
303         }
304     }
305 }
306
307 unsafe fn syscall<T>(index: SysCallIndex, data: &mut T) -> bool {
308     #[cfg(feature = "wasm_syscall")]
309     extern {
310         #[no_mangle]
311         fn rust_wasm_syscall(index: usize, data: *mut Void) -> usize;
312     }
313
314     #[cfg(not(feature = "wasm_syscall"))]
315     unsafe fn rust_wasm_syscall(_index: usize, _data: *mut Void) -> usize { 0 }
316
317     rust_wasm_syscall(index as usize, data as *mut T as *mut Void) != 0
318 }