pub(crate) fn remove_stream(&mut self, stream: Stream) {
if let Some(alias) = stream.options().get_alias() {
debug_assert_eq!(self.stream_aliases.get(&alias), Some(&stream));
+ assert!(!is_protected_alias(alias));
+
self.stream_aliases.swap_remove(&alias);
}
self.streams.remove(&stream);
callback(options);
if options.get_alias() != prev_alias {
+ if prev_alias.map(is_protected_alias).unwrap_or(false)
+ || options
+ .get_alias()
+ .map(|alias| self.has_stream(alias))
+ .unwrap_or(false)
+ {
+ // user_input, user_output and user_error cannot be realiased,
+ // and realiasing cannot shadow an existing stream.
+ options.set_alias_to_atom_opt(prev_alias);
+ return;
+ }
+
if let Some(prev_alias) = prev_alias {
self.stream_aliases.swap_remove(&prev_alias);
}
self.stream_aliases.contains_key(&alias)
}
+ /// ## Warning
+ ///
+ /// The returned stream's options should only be modified through
+ /// [`IndexStore::update_stream_options`], to avoid breaking the
+ /// invariants of [`IndexStore`].
pub(crate) fn get_stream(&self, alias: Atom) -> Option<Stream> {
self.stream_aliases.get(&alias).copied()
}
}
}
+ /// ## Warning
+ ///
+ /// The options of streams stored in `Machine::indices` should only
+ /// be modified through [`IndexStore::update_stream_options`].
pub(crate) fn get_stream_options(
&mut self,
alias: HeapCellValue,
options
}
+ /// If `addr` is a [`Cons`](HeapCellValueTag::Cons) to a stream, then returns it.
+ ///
+ /// If it is an atom or a string, then this searches for the corresponding stream
+ /// inside of [`self.indices`], returning it.
+ ///
+ /// ## Warning
+ ///
+ /// **Do not directly modify [`stream.options_mut()`](Stream::options_mut)
+ /// on the returned stream.**
+ ///
+ /// Other functions rely on the invariants of [`IndexStore`], which may
+ /// become invalidated by the direct modification of a stream's option (namely,
+ /// its alias name). Instead, use [`IndexStore::update_stream_options`].
pub(crate) fn get_stream_or_alias(
&mut self,
addr: HeapCellValue,
let mut machine = MachineBuilder::new().build();
let results = machine
- .run_query(r#"
+ .run_query(
+ r#"
\+ \+ (
open("README.md", read, S, [alias(readme)]),
open(stream(S), read, _, [alias(another_alias)]),
close(S)
),
open("README.md", read, _, [alias(readme)]).
- "#)
+ "#,
+ )
+ .collect::<Vec<_>>();
+
+ assert_eq!(results.len(), 1);
+ assert!(results[0].is_ok());
+ }
+
+ #[test]
+ #[cfg_attr(miri, ignore)]
+ fn close_realiased_user_output() {
+ let mut machine = MachineBuilder::new()
+ .with_streams(StreamConfig::in_memory())
+ .build();
+
+ let results = machine
+ .run_query(
+ r#"
+ \+ \+ (
+ open("README.md", read, S),
+ open(stream(S), read, _, [alias(user_output)]),
+ close(S)
+ ),
+ write(user_output, hello).
+ "#,
+ )
.collect::<Vec<_>>();
assert_eq!(results.len(), 1);