]> Repositorios git - scryer-prolog.git/commitdiff
Fix stream realiasing possibly shadowing other streams.
authorEmilie Burgun <[email protected]>
Mon, 3 Feb 2025 13:58:04 +0000 (14:58 +0100)
committerEmilie Burgun <[email protected]>
Thu, 6 Feb 2025 22:01:33 +0000 (23:01 +0100)
src/machine/machine_indices.rs
src/machine/streams.rs

index 4c8490683caa099435fb8a66040f2cce5b5087ff..e2d7b24887657c651a89eb58ba59d20eaa6ad4cf 100644 (file)
@@ -484,6 +484,8 @@ impl IndexStore {
     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);
@@ -503,6 +505,18 @@ impl IndexStore {
         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);
             }
@@ -516,6 +530,11 @@ impl IndexStore {
         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()
     }
index ca348a0ef172eb1d99ed2156be661025336e616e..b1ecba0354cf2d80ab3eca758e45759789aaeee6 100644 (file)
@@ -1476,6 +1476,10 @@ impl MachineState {
         }
     }
 
+    /// ## 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,
@@ -1591,6 +1595,19 @@ impl MachineState {
         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,
@@ -1953,14 +1970,40 @@ mod test {
         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);