11use std:: any:: Any ;
22use std:: collections:: BTreeMap ;
3- use std:: io:: { IsTerminal , Read , SeekFrom , Write } ;
3+ use std:: io:: { IsTerminal , SeekFrom , Write } ;
44use std:: marker:: CoercePointee ;
55use std:: ops:: Deref ;
66use std:: rc:: { Rc , Weak } ;
@@ -140,8 +140,8 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
140140 _communicate_allowed : bool ,
141141 _ptr : Pointer ,
142142 _len : usize ,
143- _dest : & MPlaceTy < ' tcx > ,
144143 _ecx : & mut MiriInterpCx < ' tcx > ,
144+ _finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
145145 ) -> InterpResult < ' tcx > {
146146 throw_unsup_format ! ( "cannot read from {}" , self . name( ) ) ;
147147 }
@@ -154,8 +154,8 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
154154 _communicate_allowed : bool ,
155155 _ptr : Pointer ,
156156 _len : usize ,
157- _dest : & MPlaceTy < ' tcx > ,
158157 _ecx : & mut MiriInterpCx < ' tcx > ,
158+ _finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
159159 ) -> InterpResult < ' tcx > {
160160 throw_unsup_format ! ( "cannot write to {}" , self . name( ) ) ;
161161 }
@@ -207,19 +207,16 @@ impl FileDescription for io::Stdin {
207207 communicate_allowed : bool ,
208208 ptr : Pointer ,
209209 len : usize ,
210- dest : & MPlaceTy < ' tcx > ,
211210 ecx : & mut MiriInterpCx < ' tcx > ,
211+ finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
212212 ) -> InterpResult < ' tcx > {
213- let mut bytes = vec ! [ 0 ; len] ;
214213 if !communicate_allowed {
215214 // We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
216215 helpers:: isolation_abort_error ( "`read` from stdin" ) ?;
217216 }
218- let result = Read :: read ( & mut & * self , & mut bytes) ;
219- match result {
220- Ok ( read_size) => ecx. return_read_success ( ptr, & bytes, read_size, dest) ,
221- Err ( e) => ecx. set_last_error_and_return ( e, dest) ,
222- }
217+
218+ let result = ecx. read_from_host ( & * self , len, ptr) ?;
219+ finish. call ( ecx, result)
223220 }
224221
225222 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -237,22 +234,19 @@ impl FileDescription for io::Stdout {
237234 _communicate_allowed : bool ,
238235 ptr : Pointer ,
239236 len : usize ,
240- dest : & MPlaceTy < ' tcx > ,
241237 ecx : & mut MiriInterpCx < ' tcx > ,
238+ finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
242239 ) -> InterpResult < ' tcx > {
243- let bytes = ecx. read_bytes_ptr_strip_provenance ( ptr, Size :: from_bytes ( len) ) ?;
244- // We allow writing to stderr even with isolation enabled.
245- let result = Write :: write ( & mut & * self , bytes) ;
240+ // We allow writing to stdout even with isolation enabled.
241+ let result = ecx. write_to_host ( & * self , len, ptr) ?;
246242 // Stdout is buffered, flush to make sure it appears on the
247243 // screen. This is the write() syscall of the interpreted
248244 // program, we want it to correspond to a write() syscall on
249245 // the host -- there is no good in adding extra buffering
250246 // here.
251247 io:: stdout ( ) . flush ( ) . unwrap ( ) ;
252- match result {
253- Ok ( write_size) => ecx. return_write_success ( write_size, dest) ,
254- Err ( e) => ecx. set_last_error_and_return ( e, dest) ,
255- }
248+
249+ finish. call ( ecx, result)
256250 }
257251
258252 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -270,17 +264,13 @@ impl FileDescription for io::Stderr {
270264 _communicate_allowed : bool ,
271265 ptr : Pointer ,
272266 len : usize ,
273- dest : & MPlaceTy < ' tcx > ,
274267 ecx : & mut MiriInterpCx < ' tcx > ,
268+ finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
275269 ) -> InterpResult < ' tcx > {
276- let bytes = ecx. read_bytes_ptr_strip_provenance ( ptr, Size :: from_bytes ( len) ) ?;
277270 // We allow writing to stderr even with isolation enabled.
271+ let result = ecx. write_to_host ( & * self , len, ptr) ?;
278272 // No need to flush, stderr is not buffered.
279- let result = Write :: write ( & mut & * self , bytes) ;
280- match result {
281- Ok ( write_size) => ecx. return_write_success ( write_size, dest) ,
282- Err ( e) => ecx. set_last_error_and_return ( e, dest) ,
283- }
273+ finish. call ( ecx, result)
284274 }
285275
286276 fn is_tty ( & self , communicate_allowed : bool ) -> bool {
@@ -302,11 +292,11 @@ impl FileDescription for NullOutput {
302292 _communicate_allowed : bool ,
303293 _ptr : Pointer ,
304294 len : usize ,
305- dest : & MPlaceTy < ' tcx > ,
306295 ecx : & mut MiriInterpCx < ' tcx > ,
296+ finish : DynMachineCallback < ' tcx , Result < usize , IoError > > ,
307297 ) -> InterpResult < ' tcx > {
308298 // We just don't write anything, but report to the user that we did.
309- ecx . return_write_success ( len , dest )
299+ finish . call ( ecx , Ok ( len ) )
310300 }
311301}
312302
@@ -405,40 +395,41 @@ impl FdTable {
405395
406396impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
407397pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
408- /// Helper to implement `FileDescription::read`:
409- /// This is only used when `read` is successful.
410- /// `actual_read_size` should be the return value of some underlying `read` call that used
411- /// `bytes` as its output buffer.
412- /// The length of `bytes` must not exceed either the host's or the target's `isize`.
413- /// `bytes` is written to `buf` and the size is written to `dest`.
414- fn return_read_success (
398+ /// Read data from a host `Read` type, store the result into machine memory,
399+ /// and return whether that worked.
400+ fn read_from_host (
415401 & mut self ,
416- buf : Pointer ,
417- bytes : & [ u8 ] ,
418- actual_read_size : usize ,
419- dest : & MPlaceTy < ' tcx > ,
420- ) -> InterpResult < ' tcx > {
402+ mut file : impl io:: Read ,
403+ len : usize ,
404+ ptr : Pointer ,
405+ ) -> InterpResult < ' tcx , Result < usize , IoError > > {
421406 let this = self . eval_context_mut ( ) ;
422- // If reading to `bytes` did not fail, we write those bytes to the buffer.
423- // Crucially, if fewer than `bytes.len()` bytes were read, only write
424- // that much into the output buffer!
425- this. write_bytes_ptr ( buf, bytes[ ..actual_read_size] . iter ( ) . copied ( ) ) ?;
426407
427- // The actual read size is always less than what got originally requested so this cannot fail.
428- this. write_int ( u64:: try_from ( actual_read_size) . unwrap ( ) , dest) ?;
429- interp_ok ( ( ) )
408+ let mut bytes = vec ! [ 0 ; len] ;
409+ let result = file. read ( & mut bytes) ;
410+ match result {
411+ Ok ( read_size) => {
412+ // If reading to `bytes` did not fail, we write those bytes to the buffer.
413+ // Crucially, if fewer than `bytes.len()` bytes were read, only write
414+ // that much into the output buffer!
415+ this. write_bytes_ptr ( ptr, bytes[ ..read_size] . iter ( ) . copied ( ) ) ?;
416+ interp_ok ( Ok ( read_size) )
417+ }
418+ Err ( e) => interp_ok ( Err ( IoError :: HostError ( e) ) ) ,
419+ }
430420 }
431421
432- /// Helper to implement `FileDescription::write`:
433- /// This function is only used when `write` is successful, and writes `actual_write_size` to `dest`
434- fn return_write_success (
422+ /// Write data to a host `Write` type, withthe bytes taken from machine memory.
423+ fn write_to_host (
435424 & mut self ,
436- actual_write_size : usize ,
437- dest : & MPlaceTy < ' tcx > ,
438- ) -> InterpResult < ' tcx > {
425+ mut file : impl io:: Write ,
426+ len : usize ,
427+ ptr : Pointer ,
428+ ) -> InterpResult < ' tcx , Result < usize , IoError > > {
439429 let this = self . eval_context_mut ( ) ;
440- // The actual write size is always less than what got originally requested so this cannot fail.
441- this. write_int ( u64:: try_from ( actual_write_size) . unwrap ( ) , dest) ?;
442- interp_ok ( ( ) )
430+
431+ let bytes = this. read_bytes_ptr_strip_provenance ( ptr, Size :: from_bytes ( len) ) ?;
432+ let result = file. write ( bytes) ;
433+ interp_ok ( result. map_err ( IoError :: HostError ) )
443434 }
444435}
0 commit comments