From 143430dd9c5e90681abc39208a6f6dbae054ffeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= <3877590+Skgland@users.noreply.github.com> Date: Thu, 23 Jan 2025 19:11:08 +0100 Subject: [PATCH 01/67] Update README.md --- README.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1e7cb24bc..1dac50c3a 100644 --- a/README.md +++ b/README.md @@ -125,10 +125,15 @@ Rust updated to the latest stable release; any existing Rust distribution should be uninstalled from your system before rustup is used. +The minimum rust toolchain version required can be found in the [Cargo.toml](https://github.com/mthom/scryer-prolog/blob/master/Cargo.toml#L13) +under the `package.rust-version` key. + Currently the only way to install the latest version of Scryer is to clone directly from this git repository, and compile the system. This can be done as follows: +### From a local git checkout + ``` $> git clone https://github.com/mthom/scryer-prolog $> cd scryer-prolog @@ -141,6 +146,32 @@ faster executable. After compilation, the executable `scryer-prolog` is available in the directory `target/release` and can be invoked to run the system. +### Via `cargo install` + +#### From git + +``` +cargo install --locked --git https://github.com/mthom/scryer-prolog.git +``` + +Afterwards the binary will be under `$HOME/.cargo/bin` which is usually added to your PATH +during the installation of the rust toolchain. + +#### From Crates.io [![Crates.io Version](https://img.shields.io/crates/v/scryer-prolog)](https://crates.io/crates/scryer-prolog) ![Crates.io MSRV](https://img.shields.io/crates/msrv/scryer-prolog) + +> [!NOTE] +> The lates crates.io release can be significantly behind the version available in the git repository +> The crates.io badge in this sections title is a link to the crates.io page. +> The msrv badge in the section title referece to the minimum rust toolchain version required to compile the latest crates.io release + +`scryer-prolog` is also release on crates.io and can be installed with + +``` +cargo install --locked scryer-prolog +``` + +### Caveates for Windows + On Windows, Scryer Prolog is easier to build inside a [MSYS2](https://www.msys2.org/) environment as some crates may require native C compilation. However, the resulting binary does not need MSYS2 to run. When executing Scryer in a shell, it is recommended to use a more advanced shell than mintty (the default MSYS2 shell). The [Windows Terminal](https://github.com/microsoft/terminal) works correctly. @@ -152,8 +183,6 @@ light.exe scryer-prolog.wixobj ``` It will generate a very basic MSI file which installs the main executable and a shortcut in the Start Menu. It can be installed with a double-click. To uninstall, go to the Control Panel and uninstall as usual. -Scryer Prolog must be built with **Rust 1.70 and up**. - ### Building WebAssembly Scryer Prolog has basic WebAssembly support. You can follow `wasm-pack`'s [official instructions](https://rustwasm.github.io/docs/wasm-pack/quickstart.html) to install `wasm-pack` and build it in any way you like. From f1100271e27d7a46f13b586f1f0db3a4eb53a221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= <3877590+Skgland@users.noreply.github.com> Date: Thu, 23 Jan 2025 19:18:15 +0100 Subject: [PATCH 02/67] Update README.md --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1dac50c3a..f59d8f4ab 100644 --- a/README.md +++ b/README.md @@ -125,12 +125,11 @@ Rust updated to the latest stable release; any existing Rust distribution should be uninstalled from your system before rustup is used. -The minimum rust toolchain version required can be found in the [Cargo.toml](https://github.com/mthom/scryer-prolog/blob/master/Cargo.toml#L13) -under the `package.rust-version` key. -Currently the only way to install the latest version of Scryer is to -clone directly from this git repository, and compile the system. This -can be done as follows: +> [!NOTE] +> The minimum rust toolchain version required can be found in the [Cargo.toml](https://github.com/mthom/scryer-prolog/blob/master/Cargo.toml#L13) +under the `package.rust-version` key. +> The acurracy of this values is validated in CI ### From a local git checkout From 75d499bf8a11ee94c21a59f572ab22b9f007ea84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= <3877590+Skgland@users.noreply.github.com> Date: Thu, 23 Jan 2025 19:20:19 +0100 Subject: [PATCH 03/67] update install link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f59d8f4ab..c582edc6a 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ at: ### Native Compilation First, install the latest stable version of -[Rust](https://www.rust-lang.org/en-US/install.html) using your +[Rust](https://www.rust-lang.org/tools/install) using your preferred method. Scryer tends to use features from newer Rust releases, whereas Rust packages in Linux distributions, Macports, etc. tend to lag behind. [rustup](http://rustup.rs) will keep your From b1e66f686b6a5c5a2a04294c242246fa653036e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= <3877590+Skgland@users.noreply.github.com> Date: Thu, 23 Jan 2025 20:04:43 +0100 Subject: [PATCH 04/67] Update README.md fix ambiguity regarding the install location for the cargo install method, also include the binary name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c582edc6a..201c736cd 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ directory `target/release` and can be invoked to run the system. cargo install --locked --git https://github.com/mthom/scryer-prolog.git ``` -Afterwards the binary will be under `$HOME/.cargo/bin` which is usually added to your PATH +Afterwards the `scryer-prolog` binary will be in the `$HOME/.cargo/bin` directory which is usually added to your PATH during the installation of the rust toolchain. #### From Crates.io [![Crates.io Version](https://img.shields.io/crates/v/scryer-prolog)](https://crates.io/crates/scryer-prolog) ![Crates.io MSRV](https://img.shields.io/crates/msrv/scryer-prolog) From bc0f614bf934581db71aafd46ea1dd8277300707 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 23 Jan 2025 20:18:55 +0100 Subject: [PATCH 05/67] FIXED: instantiation and type check for string arguments This addresses #2790. The issue first appeared in an example reported by @ak-1 in #2788, and was successfully analyzed by @flexoron. Many thanks! --- src/lib/format.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/format.pl b/src/lib/format.pl index 2ac4744b8..96266c5df 100644 --- a/src/lib/format.pl +++ b/src/lib/format.pl @@ -1,5 +1,5 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2020-2024 by Markus Triska (triska@metalevel.at) + Written 2020-2025 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. I place this code in the public domain. Use it in any way you want. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -187,7 +187,8 @@ elements_gluevars(Es, N1, N). element_gluevar(chars(Cs), N0, N) --> - { length(Cs, L), + { must_be(chars, Cs), + length(Cs, L), N is N0 + L }. element_gluevar(glue(_,V), N, N) --> [V]. element_gluevar(goal(G), N, N) --> { G }. From a7598184bf78985fd57215704542ef1d198c788e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= <3877590+Skgland@users.noreply.github.com> Date: Fri, 24 Jan 2025 18:47:42 +0100 Subject: [PATCH 06/67] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 201c736cd..07b09cd08 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ during the installation of the rust toolchain. cargo install --locked scryer-prolog ``` -### Caveates for Windows +### Caveats for Windows On Windows, Scryer Prolog is easier to build inside a [MSYS2](https://www.msys2.org/) environment as some crates may require native C compilation. However, From 7a5a5bb7956131cceda90f1aad47acb6fea06ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= Date: Mon, 27 Jan 2025 20:40:12 +0100 Subject: [PATCH 07/67] fix spelling and grammar errors found by mthom https://github.com/mthom/scryer-prolog/pull/2791#discussion_r1930025598 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07b09cd08..0e8438d41 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ used. > [!NOTE] > The minimum rust toolchain version required can be found in the [Cargo.toml](https://github.com/mthom/scryer-prolog/blob/master/Cargo.toml#L13) under the `package.rust-version` key. -> The acurracy of this values is validated in CI +> The accuracy of this value is validated in CI ### From a local git checkout From 079a69396a85e568bba7fe563bd3c1d776145bdf Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 31 Jan 2025 15:12:54 +0100 Subject: [PATCH 08/67] Add debug asserts to UntypedArenaPtr::build_with, ::get_tag and raw_ptr_as_cell! These two functions are pretty unsafe, but having these assertions makes it easier to catch UB in testing. --- src/macros.rs | 1 + src/types.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/macros.rs b/src/macros.rs index 30a863cac..7778c89e6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -175,6 +175,7 @@ macro_rules! raw_ptr_as_cell { // TODO use <*{const,mut} _>::addr instead of as when the strict_provenance feature is stable rust-lang/rust#95228 // we might need <*{const,mut} _>::expose_provenance for strict provenance, dependening on how we recreate a pointer later let ptr : *const _ = $ptr; + debug_assert!(!$ptr.is_null()); HeapCellValue::from_ptr_addr(ptr as usize) }}; } diff --git a/src/types.rs b/src/types.rs index 49ff0a6e9..d68daac7c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -656,6 +656,7 @@ pub struct UntypedArenaPtr { impl UntypedArenaPtr { #[inline(always)] pub fn build_with(ptr: usize) -> Self { + debug_assert!(ptr != 0); UntypedArenaPtr::new().with_ptr(ptr as u64) } } @@ -698,6 +699,7 @@ impl UntypedArenaPtr { #[inline] pub fn get_tag(self) -> ArenaHeaderTag { unsafe { + debug_assert!(!self.get_ptr().is_null()); let header = *(self.get_ptr() as *const ArenaHeader); header.get_tag() } From f509d3d66f7522cf57f06ff67024663d8d37d54c Mon Sep 17 00:00:00 2001 From: David Farrell <1469333+dnmfarrell@users.noreply.github.com> Date: Sat, 1 Feb 2025 14:02:34 -0500 Subject: [PATCH 09/67] Line numbers start at 1 Bumps the line number for the singleton warning. When the singleton occurs on the same line at the term starts, the line number is correct: foo(X). However it stills mis-reports this line number as 1 instead of 5: foo(1) :- true, true, true, Y. See issue #1356. --- src/loader.pl | 3 ++- tests/scryer/cli/src_tests/directive_errors.md | 8 ++++---- tests/scryer/main.rs | 1 - 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/loader.pl b/src/loader.pl index cf3c14483..12341b42e 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -210,7 +210,8 @@ '$conclude_load'(Evacuable) ; var(Term) -> instantiation_error(load/1) - ; warn_about_singletons(Singletons, LinesRead), + ; LineNum is LinesRead + 1, + warn_about_singletons(Singletons, LineNum), compile_term(Term, Evacuable), load_loop(Stream, Evacuable) ). diff --git a/tests/scryer/cli/src_tests/directive_errors.md b/tests/scryer/cli/src_tests/directive_errors.md index 2beb7c73d..4b6c8b43b 100644 --- a/tests/scryer/cli/src_tests/directive_errors.md +++ b/tests/scryer/cli/src_tests/directive_errors.md @@ -42,14 +42,14 @@ $ scryer-prolog -f --no-add-history tests-pl/invalid_decl7.pl -g halt ```trycmd $ scryer-prolog -f --no-add-history tests-pl/invalid_decl8.pl -g halt -% Warning: singleton variables Var at line 0 of invalid_decl8.pl +% Warning: singleton variables Var at line 1 of invalid_decl8.pl error(domain_error(operator_specifier,todo_insert_invalid_term_here),load/1). ``` ```trycmd $ scryer-prolog -f --no-add-history tests-pl/invalid_decl9.pl -g halt -% Warning: singleton variables Var at line 0 of invalid_decl9.pl +% Warning: singleton variables Var at line 1 of invalid_decl9.pl error(type_error(integer,todo_insert_invalid_term_here),load/1). ``` @@ -63,7 +63,7 @@ FIXME I belive the following test should result in a `error(instantiation_error, ```trycmd $ scryer-prolog -f --no-add-history tests-pl/invalid_decl11.pl -g halt -% Warning: singleton variables Var at line 0 of invalid_decl11.pl +% Warning: singleton variables Var at line 1 of invalid_decl11.pl error(type_error(list,todo_insert_invalid_term_here),load/1). ``` @@ -101,7 +101,7 @@ $ scryer-prolog -f --no-add-history tests-pl/invalid_decl16.pl -g halt ```trycmd $ scryer-prolog -f --no-add-history tests-pl/invalid_decl_issue2467.pl -g halt -% Warning: singleton variables D at line 0 of invalid_decl_issue2467.pl +% Warning: singleton variables D at line 1 of invalid_decl_issue2467.pl error(instantiation_error,load/1). ``` diff --git a/tests/scryer/main.rs b/tests/scryer/main.rs index bb77f334b..a501bd423 100644 --- a/tests/scryer/main.rs +++ b/tests/scryer/main.rs @@ -22,7 +22,6 @@ fn cli_tests() { trycmd::TestCases::new() .default_bin_name("scryer-prolog") .case("tests/scryer/cli/issues/*.toml") - .skip("tests/scryer/cli/issues/singleton_warning.toml") // wrong line number .case("tests/scryer/cli/src_tests/*.toml") .case("tests/scryer/cli/src_tests/*.md"); } From 2fe7b55343ccf6396f4ad6a8088462592302ad06 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Mon, 3 Feb 2025 00:07:39 +0100 Subject: [PATCH 10/67] Fix close/1 messing up stream_aliases when user_input or user_output aren't set to Stdin and Stdout --- src/machine/streams.rs | 62 ++++++++++++++++++++++++++++++++++--- src/machine/system_calls.rs | 51 +++++++++--------------------- 2 files changed, 73 insertions(+), 40 deletions(-) diff --git a/src/machine/streams.rs b/src/machine/streams.rs index d434c569a..212ec4637 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -1288,11 +1288,10 @@ impl Stream { )) } + /// Drops the stream handle and marks the arena pointer as [`ArenaHeaderTag::Dropped`]. #[inline] pub(crate) fn close(&mut self) -> Result<(), std::io::Error> { - let mut stream = std::mem::replace(self, Stream::Null(StreamOptions::default())); - - match stream { + match self { Stream::NamedTcp(ref mut tcp_stream) => { tcp_stream.inner_mut().tcp_stream.shutdown(Shutdown::Both) } @@ -1322,7 +1321,20 @@ impl Stream { Ok(()) } - _ => Ok(()), + Stream::Byte(mut stream) => { + stream.drop_payload(); + Ok(()) + } + Stream::StaticString(mut stream) => { + stream.drop_payload(); + Ok(()) + } + + Stream::Null(_) => Ok(()), + + Stream::Readline(_) | Stream::StandardOutput(_) | Stream::StandardError(_) => { + unreachable!(); + } } } @@ -1893,3 +1905,45 @@ impl MachineState { } } } + +#[cfg(test)] +mod test { + use super::*; + use crate::machine::config::*; + + #[test] + #[cfg_attr(miri, ignore)] + fn close_memory_user_output_stream() { + let mut machine = MachineBuilder::new() + .with_streams(StreamConfig::in_memory()) + .build(); + + let results = machine + .run_query( + "\\+ \\+ (current_output(Stream), close(Stream)), write(user_output, hello).", + ) + .collect::>(); + + assert_eq!(results.len(), 1); + assert!(results[0].is_ok()); + + let mut actual = String::new(); + machine.user_output.read_to_string(&mut actual).unwrap(); + assert_eq!(actual, "hello"); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn close_memory_user_output_stream_twice() { + let mut machine = MachineBuilder::new() + .with_streams(StreamConfig::in_memory()) + .build(); + + let results = machine + .run_query("\\+ \\+ (current_output(Stream), close(Stream), close(Stream)).") + .collect::>(); + + assert_eq!(results.len(), 1); + assert!(results[0].is_ok()); + } +} diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 14f9094b8..7a7eeb57c 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3868,47 +3868,26 @@ impl Machine { stream.flush().unwrap(); // 8.11.6.1b) } - self.indices.streams.remove(&stream); - - if stream == self.user_input { - self.user_input = self - .indices - .stream_aliases - .get(&atom!("user_input")) - .cloned() - .unwrap(); - - self.indices.streams.insert(self.user_input); - } else if stream == self.user_output { - self.user_output = self - .indices - .stream_aliases - .get(&atom!("user_output")) - .cloned() - .unwrap(); - - self.indices.streams.insert(self.user_output); + if stream == self.user_input || stream == self.user_output || stream.is_stderr() { + // stdin, stdout and stderr shouldn't be removed from the store, so return now + return Ok(()); } - if !stream.is_stdin() && !stream.is_stdout() && !stream.is_stderr() { - if let Some(alias) = stream.options().get_alias() { - self.indices.stream_aliases.swap_remove(&alias); - } - - let close_result = stream.close(); - - if close_result.is_err() { - let stub = functor_stub(atom!("close"), 1); - let addr = stream_as_cell!(stream); - let err = self - .machine_st - .existence_error(ExistenceError::Stream(addr)); + self.indices.streams.remove(&stream); - return Err(self.machine_st.error_form(err, stub)); - } + if let Some(alias) = stream.options().get_alias() { + self.indices.stream_aliases.swap_remove(&alias); } - Ok(()) + stream.close().map_err(|_| { + let stub = functor_stub(atom!("close"), 1); + let addr = stream_as_cell!(stream); + let err = self + .machine_st + .existence_error(ExistenceError::Stream(addr)); + + self.machine_st.error_form(err, stub) + }) } #[inline(always)] From fdc35f8b40d2e43438d66c32716af5b846723cb2 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 31 Jan 2025 17:26:40 +0100 Subject: [PATCH 11/67] Fix UB caused by interactions with null streams --- src/machine/lib_machine/mod.rs | 2 +- src/machine/mod.rs | 2 +- src/machine/streams.rs | 96 +++++++++++++++++++++++++++++++++- src/machine/system_calls.rs | 83 ++++++++++++++--------------- src/macros.rs | 6 --- 5 files changed, 136 insertions(+), 53 deletions(-) diff --git a/src/machine/lib_machine/mod.rs b/src/machine/lib_machine/mod.rs index 24f22fe78..35668e64e 100644 --- a/src/machine/lib_machine/mod.rs +++ b/src/machine/lib_machine/mod.rs @@ -535,7 +535,7 @@ impl Machine { /// Consults a module into the [`Machine`] from a string. pub fn consult_module_string(&mut self, module_name: &str, program: impl Into) { let stream = Stream::from_owned_string(program.into(), &mut self.machine_st.arena); - self.machine_st.registers[1] = stream_as_cell!(stream); + self.machine_st.registers[1] = stream.into(); self.machine_st.registers[2] = atom_as_cell!(&atom_table::AtomTable::build_with( &self.machine_st.atom_tbl, module_name diff --git a/src/machine/mod.rs b/src/machine/mod.rs index a62c0caa8..0313a2d90 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -291,7 +291,7 @@ impl Machine { } fn load_file(&mut self, path: &str, stream: Stream) { - self.machine_st.registers[1] = stream_as_cell!(stream); + self.machine_st.registers[1] = stream.into(); self.machine_st.registers[2] = atom_as_cell!(AtomTable::build_with(&self.machine_st.atom_tbl, path)); diff --git a/src/machine/streams.rs b/src/machine/streams.rs index d434c569a..7b616b920 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -982,6 +982,20 @@ fn cursor_position( } } +impl From for HeapCellValue { + #[inline(always)] + fn from(stream: Stream) -> Self { + if stream.is_null_stream() { + let res = atom!("null_stream"); + atom_as_cell!(res) + } else { + let res = stream.as_ptr(); + debug_assert!(!res.is_null()); + raw_ptr_as_cell!(res) + } + } +} + impl Stream { #[inline] pub(crate) fn position(&mut self) -> Option<(u64, usize)> { @@ -1626,7 +1640,7 @@ impl MachineState { match_untyped_arena_ptr!(ptr, (ArenaHeaderTag::Stream, stream) => { return if stream.is_null_stream() { - Err(self.open_permission_error(stream_as_cell!(stream), caller, arity)) + Err(self.open_permission_error(HeapCellValue::from(stream), caller, arity)) } else { Ok(stream) }; @@ -1689,7 +1703,7 @@ impl MachineState { if let Some(alias) = stream.options().get_alias() { atom_as_cell!(alias) } else { - stream_as_cell!(stream) + stream.into() }, ); @@ -1893,3 +1907,81 @@ impl MachineState { } } } + +#[cfg(test)] +mod test { + use super::*; + use crate::machine::config::*; + + #[test] + #[cfg_attr(miri, ignore)] + fn current_input_null_stream() { + let mut machine = MachineBuilder::new() + .with_streams(StreamConfig::in_memory()) + .build(); + + let results = machine.run_query("current_input(S).").collect::>(); + + assert_eq!(results.len(), 1); + assert!(results[0].is_ok()); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn read_null_stream() { + let mut machine = MachineBuilder::new() + .with_streams(StreamConfig::in_memory()) + .build(); + + let results = machine.run_query("get_code(C).").collect::>(); + + assert_eq!(results.len(), 1); + assert!(results[0].is_err()); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn current_output_null_stream() { + // TODO: switch to a proper solution for configuring the machine with null streams + // once `StreamConfig` supports it. + let mut machine = MachineBuilder::new().build(); + machine.user_output = Stream::Null(StreamOptions::default()); + machine.configure_streams(); + + let results = machine.run_query("current_output(S).").collect::>(); + + assert_eq!(results.len(), 1); + assert!(results[0].is_ok()); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn write_null_stream() { + // TODO: switch to a proper solution for configuring the machine with null streams + // once `StreamConfig` supports it. + let mut machine = MachineBuilder::new().build(); + machine.user_output = Stream::Null(StreamOptions::default()); + machine.configure_streams(); + + let results = machine.run_query("write(hello).").collect::>(); + + assert_eq!(results.len(), 1); + assert!(results[0].is_err()); + } + + /// A variant of the [`write_null_stream`] that tries to write to a (null) input stream. + #[test] + #[cfg_attr(miri, ignore)] + fn write_null_input_stream() { + let mut machine = MachineBuilder::new() + .with_streams(StreamConfig::in_memory()) + .build(); + + let results = machine + .run_query("current_input(Stream), write(Stream, hello).") + .collect::>(); + + assert_eq!(results.len(), 1); + assert!(results[0].is_err()); + } +} diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 14f9094b8..11fe5c5f1 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1881,7 +1881,7 @@ impl Machine { let stream = self.user_input; if let Some(var) = addr.as_var() { - self.machine_st.bind(var, stream_as_cell!(stream)); + self.machine_st.bind(var, stream.into()); return Ok(()); } @@ -1916,7 +1916,7 @@ impl Machine { let stream = self.user_output; if let Some(var) = addr.as_var() { - self.machine_st.bind(var, stream_as_cell!(stream)); + self.machine_st.bind(var, stream.into()); return Ok(()); } @@ -3273,7 +3273,7 @@ impl Machine { match stream.write_all(&bytes) { Ok(_) => {} _ => { - let addr = stream_as_cell!(stream); + let addr = stream.into(); let err = self .machine_st .existence_error(ExistenceError::Stream(addr)); @@ -3323,7 +3323,7 @@ impl Machine { _ => { let err = self .machine_st - .existence_error(ExistenceError::Stream(stream_as_cell!(stream))); + .existence_error(ExistenceError::Stream(stream.into())); return Err(self.machine_st.error_form(err, stub_gen())); } @@ -3336,9 +3336,9 @@ impl Machine { return Ok(()); } _ => { - let err = self.machine_st.existence_error(ExistenceError::Stream( - stream_as_cell!(stream), - )); + let err = self + .machine_st + .existence_error(ExistenceError::Stream(stream.into())); return Err(self.machine_st.error_form(err, stub_gen())); } @@ -3730,7 +3730,7 @@ impl Machine { self.indices.streams = self.indices.streams.sub(&null_streams); if let Some(first_stream) = first_stream { - let stream = stream_as_cell!(first_stream); + let stream = first_stream.into(); let var = self.deref_register(1).as_var().unwrap(); @@ -3761,8 +3761,7 @@ impl Machine { if let Some(next_stream) = next_stream { let var = self.deref_register(2).as_var().unwrap(); - let next_stream = stream_as_cell!(next_stream); - self.machine_st.bind(var, next_stream); + self.machine_st.bind(var, next_stream.into()); } else { self.machine_st.fail = true; } @@ -3779,7 +3778,7 @@ impl Machine { if !stream.is_output_stream() { let stub = functor_stub(atom!("flush_output"), 1); - let addr = stream_as_cell!(stream); + let addr = HeapCellValue::from(stream); let err = self.machine_st @@ -3899,7 +3898,7 @@ impl Machine { if close_result.is_err() { let stub = functor_stub(atom!("close"), 1); - let addr = stream_as_cell!(stream); + let addr = stream.into(); let err = self .machine_st .existence_error(ExistenceError::Stream(addr)); @@ -4472,10 +4471,9 @@ impl Machine { self.indices.streams.insert(stream); - let stream = stream_as_cell!(stream); - let stream_addr = self.deref_register(2); - self.machine_st.bind(stream_addr.as_var().unwrap(), stream); + self.machine_st + .bind(stream_addr.as_var().unwrap(), stream.into()); } Err(_) => { self.machine_st.fail = true; @@ -4689,7 +4687,7 @@ impl Machine { *stream.options_mut() = StreamOptions::default(); stream.options_mut().set_stream_type(StreamType::Binary); self.indices.streams.insert(stream); - let stream = stream_as_cell!(stream); + let stream: HeapCellValue = stream.into(); let handle: TypedArenaPtr = arena_alloc!(request.response, &mut self.machine_st.arena); @@ -4792,27 +4790,26 @@ impl Machine { read_heap_cell!(culprit, (HeapCellValueTag::Cons, cons_ptr) => { - match_untyped_arena_ptr!(cons_ptr, - (ArenaHeaderTag::HttpResponse, http_response) => { - let mut stream = Stream::from_http_sender( - http_response, - status_code, - headers, - &mut self.machine_st.arena + match_untyped_arena_ptr!(cons_ptr, + (ArenaHeaderTag::HttpResponse, http_response) => { + let mut stream = Stream::from_http_sender( + http_response, + status_code, + headers, + &mut self.machine_st.arena + ); + *stream.options_mut() = StreamOptions::default(); + stream.options_mut().set_stream_type(StreamType::Binary); + self.indices.streams.insert(stream); + self.machine_st.bind(stream_addr.as_var().unwrap(), stream.into()); + } + _ => { + unreachable!(); + } ); - *stream.options_mut() = StreamOptions::default(); - stream.options_mut().set_stream_type(StreamType::Binary); - self.indices.streams.insert(stream); - let stream = stream_as_cell!(stream); - self.machine_st.bind(stream_addr.as_var().unwrap(), stream); - } - _ => { - unreachable!(); - } - ); } _ => { - unreachable!(); + unreachable!(); } ); @@ -5125,7 +5122,7 @@ impl Machine { let stream_var = self.deref_register(3); self.machine_st - .bind(stream_var.as_var().unwrap(), stream_as_cell!(stream)); + .bind(stream_var.as_var().unwrap(), stream.into()); } else { let err = self .machine_st @@ -6559,7 +6556,7 @@ impl Machine { self.indices.streams.insert(stream); - stream_as_cell!(stream) + HeapCellValue::from(stream) } Err(ErrorKind::PermissionDenied) => { return Err(self.machine_st.open_permission_error( @@ -6715,14 +6712,13 @@ impl Machine { self.indices.streams.insert(tcp_stream); - let tcp_stream = stream_as_cell!(tcp_stream); let client = atom_as_cell!(client); let client_addr = self.deref_register(2); let stream_addr = self.deref_register(3); self.machine_st.bind(client_addr.as_var().unwrap(), client); - self.machine_st.bind(stream_addr.as_var().unwrap(), tcp_stream); + self.machine_st.bind(stream_addr.as_var().unwrap(), tcp_stream.into()); } None => { self.machine_st.fail = true; @@ -6770,10 +6766,11 @@ impl Machine { let stream = Stream::from_tls_stream(addr, stream, &mut self.machine_st.arena); self.indices.streams.insert(stream); - self.machine_st.heap.push(stream_as_cell!(stream)); + // FIXME: why are we pushing a random, unreferenced cell on the heap? + self.machine_st.heap.push(stream.into()); let stream_addr = self.deref_register(3); self.machine_st - .bind(stream_addr.as_var().unwrap(), stream_as_cell!(stream)); + .bind(stream_addr.as_var().unwrap(), stream.into()); Ok(()) } else { @@ -6826,7 +6823,7 @@ impl Machine { let stream_addr = self.deref_register(4); self.machine_st - .bind(stream_addr.as_var().unwrap(), stream_as_cell!(stream)); + .bind(stream_addr.as_var().unwrap(), stream.into()); } else { unreachable!(); } @@ -6875,7 +6872,7 @@ impl Machine { let err = self.machine_st.permission_error( Permission::Reposition, atom!("stream"), - stream_as_cell!(stream), + HeapCellValue::from(stream), ); return Err(self.machine_st.error_form(err, stub)); @@ -8099,7 +8096,7 @@ impl Machine { let lib_stream = Stream::from_static_string(library, &mut self.machine_st.arena); unify!( self.machine_st, - stream_as_cell!(lib_stream), + HeapCellValue::from(lib_stream), self.machine_st.registers[2] ); diff --git a/src/macros.rs b/src/macros.rs index 7778c89e6..634959304 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -218,12 +218,6 @@ macro_rules! string_as_pstr_cell { }}; } -macro_rules! stream_as_cell { - ($ptr:expr) => { - raw_ptr_as_cell!($ptr.as_ptr()) - }; -} - macro_rules! cell_as_stream { ($cell:expr) => {{ let ptr = cell_as_untyped_arena_ptr!($cell); From 7108e87e92f1ab16dbb6c0ee4e6a954c943ab84e Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Sun, 2 Feb 2025 17:38:59 +0100 Subject: [PATCH 12/67] Make Stream::Null behave like /dev/null --- src/machine/mod.rs | 4 +++ src/machine/streams.rs | 74 +++++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 0313a2d90..107240300 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -509,6 +509,10 @@ impl Machine { .insert(atom!("user_error"), self.user_error); self.indices.streams.insert(self.user_error); + + self.indices + .stream_aliases + .insert(atom!("null_stream"), Stream::Null(StreamOptions::default())); } #[inline(always)] diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 7b616b920..443c01830 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -836,13 +836,13 @@ impl Read for Stream { ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, )), - Stream::OutputFile(_) - | Stream::StandardError(_) - | Stream::StandardOutput(_) - | Stream::Null(_) => Err(std::io::Error::new( - ErrorKind::PermissionDenied, - StreamError::ReadFromOutputStream, - )), + Stream::Null(_) => Ok(buf.len()), + Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) => { + Err(std::io::Error::new( + ErrorKind::PermissionDenied, + StreamError::ReadFromOutputStream, + )) + } } } } @@ -864,13 +864,10 @@ impl Write for Stream { ErrorKind::PermissionDenied, StreamError::WriteToInputStream, )), - Stream::StaticString(_) - | Stream::Readline(_) - | Stream::InputFile(..) - | Stream::Null(_) => Err(std::io::Error::new( - ErrorKind::PermissionDenied, - StreamError::WriteToInputStream, - )), + Stream::Null(_) => Ok(buf.len()), + Stream::StaticString(_) | Stream::Readline(_) | Stream::InputFile(..) => Err( + std::io::Error::new(ErrorKind::PermissionDenied, StreamError::WriteToInputStream), + ), } } @@ -890,13 +887,10 @@ impl Write for Stream { ErrorKind::PermissionDenied, StreamError::FlushToInputStream, )), - Stream::StaticString(_) - | Stream::Readline(_) - | Stream::InputFile(_) - | Stream::Null(_) => Err(std::io::Error::new( - ErrorKind::PermissionDenied, - StreamError::FlushToInputStream, - )), + Stream::Null(_) => Ok(()), + Stream::StaticString(_) | Stream::Readline(_) | Stream::InputFile(_) => Err( + std::io::Error::new(ErrorKind::PermissionDenied, StreamError::FlushToInputStream), + ), } } } @@ -1144,6 +1138,7 @@ impl Stream { } } } + Stream::Null(_) => AtEndOfStream::At, #[cfg(feature = "http")] Stream::HttpRead(stream_layout) => { if stream_layout @@ -1356,7 +1351,8 @@ impl Stream { | Stream::Byte(_) | Stream::Readline(_) | Stream::StaticString(_) - | Stream::InputFile(..) => true, + | Stream::InputFile(..) + | Stream::Null(_) => true, _ => false, } } @@ -1372,7 +1368,8 @@ impl Stream { | Stream::StandardOutput(_) | Stream::NamedTcp(..) | Stream::Byte(_) - | Stream::OutputFile(..) => true, + | Stream::OutputFile(..) + | Stream::Null(_) => true, _ => false, } } @@ -1607,7 +1604,7 @@ impl MachineState { debug_assert_eq!(arity, 0); return match stream_aliases.get(&name) { - Some(stream) if !stream.is_null_stream() => Ok(*stream), + Some(stream) => Ok(*stream), _ => { let stub = functor_stub(caller, arity); let addr = atom_as_cell!(name); @@ -1625,7 +1622,7 @@ impl MachineState { debug_assert_eq!(arity, 0); return match stream_aliases.get(&name) { - Some(stream) if !stream.is_null_stream() => Ok(*stream), + Some(stream) => Ok(*stream), _ => { let stub = functor_stub(caller, arity); let addr = atom_as_cell!(name); @@ -1639,11 +1636,10 @@ impl MachineState { (HeapCellValueTag::Cons, ptr) => { match_untyped_arena_ptr!(ptr, (ArenaHeaderTag::Stream, stream) => { - return if stream.is_null_stream() { - Err(self.open_permission_error(HeapCellValue::from(stream), caller, arity)) - } else { - Ok(stream) - }; + if stream.is_null_stream() { + unreachable!("Null streams have no Cons representation"); + } + return Ok(stream); } (ArenaHeaderTag::Dropped, _value) => { let stub = functor_stub(caller, arity); @@ -1936,7 +1932,11 @@ mod test { let results = machine.run_query("get_code(C).").collect::>(); assert_eq!(results.len(), 1); - assert!(results[0].is_err()); + assert!( + results[0].is_ok(), + "Expected read to succeed, got {:?}", + results[0] + ); } #[test] @@ -1966,7 +1966,11 @@ mod test { let results = machine.run_query("write(hello).").collect::>(); assert_eq!(results.len(), 1); - assert!(results[0].is_err()); + assert!( + results[0].is_ok(), + "Expected write to succeed, got {:?}", + results[0] + ); } /// A variant of the [`write_null_stream`] that tries to write to a (null) input stream. @@ -1982,6 +1986,10 @@ mod test { .collect::>(); assert_eq!(results.len(), 1); - assert!(results[0].is_err()); + assert!( + results[0].is_ok(), + "Expected write to succeed, got {:?}", + results[0] + ); } } From b8ccebbf24d9f70da6a0e2cfae11fb6164f01aaa Mon Sep 17 00:00:00 2001 From: bakaq Date: Sat, 1 Feb 2025 15:53:15 -0300 Subject: [PATCH 13/67] Fix bug in finding arity for verify attributes --- src/machine/dispatch.rs | 36 +++++++++++++++++-- .../scryer/cli/issues/compilation_bug.in/a.pl | 17 +++++++++ .../scryer/cli/issues/compilation_bug.in/b.pl | 30 ++++++++++++++++ .../scryer/cli/issues/compilation_bug.in/c.pl | 19 ++++++++++ tests/scryer/cli/issues/compilation_bug.stdin | 2 ++ .../scryer/cli/issues/compilation_bug.stdout | 2 ++ tests/scryer/cli/issues/compilation_bug.toml | 2 ++ 7 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 tests/scryer/cli/issues/compilation_bug.in/a.pl create mode 100644 tests/scryer/cli/issues/compilation_bug.in/b.pl create mode 100644 tests/scryer/cli/issues/compilation_bug.in/c.pl create mode 100644 tests/scryer/cli/issues/compilation_bug.stdin create mode 100644 tests/scryer/cli/issues/compilation_bug.stdout create mode 100644 tests/scryer/cli/issues/compilation_bug.toml diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 635de289c..a5ebe291d 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -566,7 +566,38 @@ impl Machine { let mut p = self.machine_st.p; let mut arity = 0; + self.indices.code_dir.sort_by(|_, a, _, b| a.cmp(b)); + + let predicate_idx = self + .indices + .code_dir + .binary_search_by_key(&p, |_, x| x.get().p() as usize) + .unwrap_or_else(|x| x - 1); + + let current_pred_limit = self + .indices + .code_dir + .get_index(predicate_idx + 1) + .map(|x| x.1.p() as usize); + while self.code[p].is_head_instr() { + //println!("{}: {:?}", p, &self.code[p]); + //println!("{} {:?}", arity, self.code[p]); + for r in self.code[p].registers() { + //println!("reg {:?}", r); + if let RegType::Temp(t) = r { + arity = std::cmp::max(arity, t); + } + } + + p += 1; + } + + let p_interrupt = p; + + while p < self.code.len() + && current_pred_limit.map(|x| p < x).unwrap_or(true) + { for r in self.code[p].registers() { if let RegType::Temp(t) = r { arity = std::cmp::max(arity, t); @@ -577,14 +608,15 @@ impl Machine { } let instr = std::mem::replace( - &mut self.code[p], + &mut self.code[p_interrupt], Instruction::VerifyAttrInterrupt(arity), ); self.code[VERIFY_ATTR_INTERRUPT_LOC] = instr; - self.machine_st.attr_var_init.cp = p; + self.machine_st.attr_var_init.cp = p_interrupt; } &Instruction::VerifyAttrInterrupt(arity) => { + //println!("VerifyAttr arity: {arity}"); // let (_, arity) = self.code[VERIFY_ATTR_INTERRUPT_LOC].to_name_and_arity(); // let arity = std::cmp::max(arity, self.machine_st.num_of_args); self.run_verify_attr_interrupt(arity); diff --git a/tests/scryer/cli/issues/compilation_bug.in/a.pl b/tests/scryer/cli/issues/compilation_bug.in/a.pl new file mode 100644 index 000000000..baf223ada --- /dev/null +++ b/tests/scryer/cli/issues/compilation_bug.in/a.pl @@ -0,0 +1,17 @@ +% Issue 2706 +:- use_module(library(atts)). +:- use_module(library(lists)). +:- use_module(library(iso_ext)). + +:- attribute a/1. + +verify_attributes(_,_, []). + +asdf([_|Xs], N) :- + true, + N1 is N - 1, + asdf(Xs, N1). + +test_a :- + put_atts(A, a(1)), + call_with_inference_limit(asdf(A, 1), 1000, _). diff --git a/tests/scryer/cli/issues/compilation_bug.in/b.pl b/tests/scryer/cli/issues/compilation_bug.in/b.pl new file mode 100644 index 000000000..b6f5539eb --- /dev/null +++ b/tests/scryer/cli/issues/compilation_bug.in/b.pl @@ -0,0 +1,30 @@ +% Issue 2632 + +% Repro for cycle detection crash +:- use_module(library(lists)). +:- use_module(library(clpz)). +:- use_module(library(error)). +:- use_module(library(lambda)). +:- use_module(library(debug)). + +clpz:monotonic. + +q_r(T/N, T:U) :- 0 #=< #T, 0 #=< #U, #N #= T + U. + +qs_Ts_Us(Qs, ΣTs, ΣUs) :- + maplist(\Q^T^U^(q_r(Q, T:U)), Qs, Ts, Us), + intlist_partsums(Ts, ΣTs), + intlist_partsums(Us, ΣUs). + +%% Utility predicates used above: + +intlist_partsums([X|Xs], [X|Ss]) :- + intlist_partsums_acc(Xs, Ss, X). + +intlist_partsums_acc([], [], _). +intlist_partsums_acc([X|Xs], [S|Ss], A) :- + #S #= #X + #A, + intlist_partsums_acc(Xs, Ss, S). + +test_b :- + once(qs_Ts_Us(_, [1,3], [5,9])). diff --git a/tests/scryer/cli/issues/compilation_bug.in/c.pl b/tests/scryer/cli/issues/compilation_bug.in/c.pl new file mode 100644 index 000000000..a93848bea --- /dev/null +++ b/tests/scryer/cli/issues/compilation_bug.in/c.pl @@ -0,0 +1,19 @@ +% Issue 2809 + +:- use_module(library(freeze)). + +main1 :- + freeze(Minor,true), + cbor_minor_value1(Minor, []). + +main2 :- + freeze(Minor,true), + cbor_minor_value2(Minor, []). + +cbor_minor_value1(24, S0) :- numbytes_number(1, S0). + +cbor_minor_value2(24, S0) :- S0=S1, numbytes_number(1, S1). + +numbytes_number(_, []). + +test_c :- main1. diff --git a/tests/scryer/cli/issues/compilation_bug.stdin b/tests/scryer/cli/issues/compilation_bug.stdin new file mode 100644 index 000000000..ab869cd29 --- /dev/null +++ b/tests/scryer/cli/issues/compilation_bug.stdin @@ -0,0 +1,2 @@ +use_module(a), use_module(b), use_module(c). +\+ \+ (test_a, test_b, test_c). diff --git a/tests/scryer/cli/issues/compilation_bug.stdout b/tests/scryer/cli/issues/compilation_bug.stdout new file mode 100644 index 000000000..99f1c7157 --- /dev/null +++ b/tests/scryer/cli/issues/compilation_bug.stdout @@ -0,0 +1,2 @@ + true. + true. diff --git a/tests/scryer/cli/issues/compilation_bug.toml b/tests/scryer/cli/issues/compilation_bug.toml new file mode 100644 index 000000000..a83959d8b --- /dev/null +++ b/tests/scryer/cli/issues/compilation_bug.toml @@ -0,0 +1,2 @@ +# issue 2706 +args = ["-f", "--no-add-history"] From 6d80c843e61774c821a5a605c6063795ab38ea5e Mon Sep 17 00:00:00 2001 From: bakaq Date: Wed, 5 Feb 2025 02:22:59 -0300 Subject: [PATCH 14/67] Scan entire predicate in InstallVerifyAttr --- src/machine/dispatch.rs | 62 ++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index a5ebe291d..57d3e5ae0 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -563,9 +563,9 @@ impl Machine { self.machine_st.cp = self.machine_st.attr_var_init.cp; } - let mut p = self.machine_st.p; - let mut arity = 0; + let p = self.machine_st.p; + // Find the boundaries of the current predicate self.indices.code_dir.sort_by(|_, a, _, b| a.cmp(b)); let predicate_idx = self @@ -574,38 +574,41 @@ impl Machine { .binary_search_by_key(&p, |_, x| x.get().p() as usize) .unwrap_or_else(|x| x - 1); - let current_pred_limit = self + let current_pred_start = self .indices .code_dir - .get_index(predicate_idx + 1) - .map(|x| x.1.p() as usize); - - while self.code[p].is_head_instr() { - //println!("{}: {:?}", p, &self.code[p]); - //println!("{} {:?}", arity, self.code[p]); - for r in self.code[p].registers() { - //println!("reg {:?}", r); - if let RegType::Temp(t) = r { - arity = std::cmp::max(arity, t); - } - } + .get_index(predicate_idx) + .map(|x| x.1.p() as usize) + .unwrap(); - p += 1; - } + debug_assert!(current_pred_start <= p); - let p_interrupt = p; + let current_pred_end = self + .indices + .code_dir + .get_index(predicate_idx + 1) + .map(|x| x.1.p() as usize) + .unwrap_or(self.code.len()); - while p < self.code.len() - && current_pred_limit.map(|x| p < x).unwrap_or(true) - { - for r in self.code[p].registers() { - if let RegType::Temp(t) = r { - arity = std::cmp::max(arity, t); - } - } + debug_assert!(current_pred_end >= p); + debug_assert!(current_pred_end <= self.code.len()); - p += 1; - } + // Find point to insert the interrupt + let p_interrupt = p + self.code[p..current_pred_end] + .iter() + .position(|x| !x.is_head_instr()) + .unwrap(); + + // Scan registers of all instructions to find out how many to save + let arity = self.code[current_pred_start..current_pred_end] + .iter() + .flat_map(Instruction::registers) + .flat_map(|r| match r { + RegType::Temp(t) => Some(t), + _ => None, + }) + .max() + .unwrap_or(0); let instr = std::mem::replace( &mut self.code[p_interrupt], @@ -616,9 +619,6 @@ impl Machine { self.machine_st.attr_var_init.cp = p_interrupt; } &Instruction::VerifyAttrInterrupt(arity) => { - //println!("VerifyAttr arity: {arity}"); - // let (_, arity) = self.code[VERIFY_ATTR_INTERRUPT_LOC].to_name_and_arity(); - // let arity = std::cmp::max(arity, self.machine_st.num_of_args); self.run_verify_attr_interrupt(arity); } &Instruction::Add(ref a1, ref a2, t) => { From 22538a05beff127903f2be6813b1facdf51baa07 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Thu, 6 Feb 2025 00:16:20 +0100 Subject: [PATCH 15/67] Fix backtracking on the topmost predicate triggering UB in run_module_predicate Fixes #2815, see that issue for my investigation. This is a one-line fix that I'm quite proud of :) If the topmost query for `run_module_predicate` needs to backtrack, then before this commit, one of the following two things may happen: - A dangling OrFrame is read at stack offset 0 - An AndFrame was at stack offset 0 would be read as an OrFrame This can be seen by either calling `run_module_predicate` with a throwing predicate (encountering the second scenario) or a failing predicate (encountering the first scenario), or by running the following in the REPL, which triggers a `throw/1` within the error handler, propagating it all the way up (and encountering the second scenario): ```prolog ?- current_output(S), open(stream(S), write, S0, [type(binary)]). ``` Currently, `Stack` is not equipped with tools to detect this incorrect behavior, so it would instead try to read an OrFrame at offset 0, which triggers UB, since transmuting between AndFramePrelude and OrFramePrelude isn't legal. In practice, since `AndFramePrelude` is smaller, the later fields of `OrFramePrelude` would read from the cells following the `AndFramePrelude`, and would contain nonsensical data, triggering the panic that led to my investigation in #2815 and that is fairly reliable to witness. Surprisingly, this wouldn't happen with `run_query`, which led me to look at how they operate differently. It turns out that `run_query` inserts an OrFrame at offset 0, which covers both problematic scenarios. The fix is thus to simply add a call to `Machine::allocate_stub_choice_point` in `run_module_predicate` :) --- src/machine/lib_machine/mod.rs | 2 +- src/machine/mod.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/machine/lib_machine/mod.rs b/src/machine/lib_machine/mod.rs index 24f22fe78..be5c6008e 100644 --- a/src/machine/lib_machine/mod.rs +++ b/src/machine/lib_machine/mod.rs @@ -544,7 +544,7 @@ impl Machine { self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2)); } - fn allocate_stub_choice_point(&mut self) { + pub(crate) fn allocate_stub_choice_point(&mut self) { // NOTE: create a choice point to terminate the dispatch_loop // if an exception is thrown. diff --git a/src/machine/mod.rs b/src/machine/mod.rs index a62c0caa8..cbd99c531 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -279,6 +279,7 @@ impl Machine { if let Some(module) = self.indices.modules.get(&module_name) { if let Some(code_index) = module.code_dir.get(&key) { let p = code_index.local().unwrap(); + self.allocate_stub_choice_point(); self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; self.machine_st.p = p; @@ -1264,3 +1265,25 @@ impl Machine { } } } + +#[cfg(test)] +mod tests { + use super::config::*; + use super::*; + + #[test] + fn test_run_module_predicate_throw() { + let mut machine = MachineBuilder::default() + .with_toplevel( + r#" + :- module('$toplevel', []). + repl :- throw(kaboom). + "#, + ) + .build(); + + let query = machine.run_module_predicate(atom!("$toplevel"), (atom!("repl"), 0)); + + assert_eq!(query, std::process::ExitCode::SUCCESS); + } +} From d4bf52e82c131882fbc2f7fd213ea39548ed78b6 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Thu, 6 Feb 2025 10:37:54 +0100 Subject: [PATCH 16/67] Disable test_run_module_predicate_throw under miri and support rustc < 1.83 --- src/machine/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index cbd99c531..bfa4f7b05 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -1272,18 +1272,17 @@ mod tests { use super::*; #[test] + #[cfg_attr(miri, ignore)] fn test_run_module_predicate_throw() { let mut machine = MachineBuilder::default() .with_toplevel( r#" - :- module('$toplevel', []). - repl :- throw(kaboom). - "#, + :- module('$toplevel', []). + repl :- throw(kaboom). + "#, ) .build(); - let query = machine.run_module_predicate(atom!("$toplevel"), (atom!("repl"), 0)); - - assert_eq!(query, std::process::ExitCode::SUCCESS); + machine.run_module_predicate(atom!("$toplevel"), (atom!("repl"), 0)); } } From e68ac8347f998827c8dca651954c5527d4936c77 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Thu, 6 Feb 2025 13:26:16 +0100 Subject: [PATCH 17/67] Document run_module_predicate and handle critical failure in toplevel.pl --- src/machine/mod.rs | 6 ++++++ src/toplevel.pl | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index bfa4f7b05..9a6c50d75 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -271,6 +271,11 @@ impl Machine { .unwrap() } + /// Runs the predicate `key` in `module_name` until completion. + /// Siltently ignores failure, thrown errors and choice points. + /// + /// Consider using [`Machine::run_query`] if you wish to handle + /// predicates that may fail, leave a choice point or throw. pub(crate) fn run_module_predicate( &mut self, module_name: Atom, @@ -279,6 +284,7 @@ impl Machine { if let Some(module) = self.indices.modules.get(&module_name) { if let Some(code_index) = module.code_dir.get(&key) { let p = code_index.local().unwrap(); + // Leave a halting choice point to backtrack to in case the predicate fails or throws. self.allocate_stub_choice_point(); self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; diff --git a/src/toplevel.pl b/src/toplevel.pl index b43ac3cb5..0727aee03 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -29,6 +29,19 @@ ). '$repl' :- + catch( + start_repl, + _, + % Something bad enough happened that the REPL itself threw an error. + % This can be caused by a broken user_output stream, so we cannot + % print an error. + % + % The best we can do now is halt with an error code, + % so that users can try to diagnose the issue: + halt(99) + ). + +start_repl :- asserta('$toplevel':started), raw_argv(Args0), ( append(Args1, ["--"|_], Args0) -> From 0cf46d3ec4f0fc1e9fd9c8222336a28ea3b4ad3b Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Mon, 3 Feb 2025 14:00:37 +0100 Subject: [PATCH 18/67] Encapsulate accesses to IndexStore::streams and ::stream_aliases These two fields are able to hold `Stream` instances, which predicates like `close/1` expect to be managed properly for their correctness. To ensure that this is the case, I have removed direct accesses to those two fields, so that they can be properly managed in one place. --- src/machine/loader.rs | 2 +- src/machine/machine_indices.rs | 105 ++++++++++++++++++++- src/machine/mod.rs | 28 ++---- src/machine/streams.rs | 12 +-- src/machine/system_calls.rs | 164 +++++++++++++++------------------ 5 files changed, 187 insertions(+), 124 deletions(-) diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 90080e9e5..b7e7c4b5c 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -1808,7 +1808,7 @@ impl Machine { pub(crate) fn push_load_context(&mut self) -> CallResult { let stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("$push_load_context"), 2, )?; diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index 2f2591252..4c8490683 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -7,8 +7,9 @@ use crate::atom_table::*; use crate::forms::*; use crate::machine::loader::*; use crate::machine::machine_state::*; -use crate::machine::streams::Stream; +use crate::machine::streams::{Stream, StreamOptions}; use crate::machine::ClauseType; +use crate::machine::MachineStubGen; use fxhash::FxBuildHasher; use indexmap::{IndexMap, IndexSet}; @@ -261,8 +262,8 @@ pub struct IndexStore { pub(super) meta_predicates: MetaPredicateDir, pub(super) modules: ModuleDir, pub(super) op_dir: OpDir, - pub(super) streams: StreamDir, - pub(super) stream_aliases: StreamAliasDir, + streams: StreamDir, + stream_aliases: StreamAliasDir, } impl IndexStore { @@ -459,6 +460,94 @@ impl IndexStore { } } + pub(crate) fn add_stream( + &mut self, + stream: Stream, + stub_name: Atom, + stub_arity: usize, + ) -> Result<(), MachineStubGen> { + if let Some(alias) = stream.options().get_alias() { + if self.stream_aliases.contains_key(&alias) { + return Err(Box::new(move |machine_st| { + machine_st.occupied_alias_permission_error(alias, stub_name, stub_arity) + })); + } + + self.stream_aliases.insert(alias, stream); + } + + self.streams.insert(stream); + + Ok(()) + } + + 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)); + self.stream_aliases.swap_remove(&alias); + } + self.streams.remove(&stream); + } + + pub(crate) fn update_stream_options( + &mut self, + mut stream: Stream, + callback: F, + ) { + if let Some(prev_alias) = stream.options().get_alias() { + debug_assert_eq!(self.stream_aliases.get(&prev_alias), Some(&stream)); + } + let options = stream.options_mut(); + let prev_alias = options.get_alias(); + + callback(options); + + if options.get_alias() != prev_alias { + if let Some(prev_alias) = prev_alias { + self.stream_aliases.swap_remove(&prev_alias); + } + if let Some(new_alias) = options.get_alias() { + self.stream_aliases.insert(new_alias, stream); + } + } + } + + pub(crate) fn has_stream(&self, alias: Atom) -> bool { + self.stream_aliases.contains_key(&alias) + } + + pub(crate) fn get_stream(&self, alias: Atom) -> Option { + self.stream_aliases.get(&alias).copied() + } + + pub(crate) fn iter_streams<'a, R: std::ops::RangeBounds>( + &'a self, + range: R, + ) -> impl Iterator + 'a { + self.streams.range(range).into_iter().copied() + } + + /// Forcibly sets `alias` to `stream`. + /// If there was a previous stream with that alias, it will lose that alias. + /// + /// Consider using [`add_stream`](Self::add_stream) if you wish to instead + /// return an error when stream aliases conflict. + pub(crate) fn set_stream(&mut self, alias: Atom, mut stream: Stream) { + if let Some(mut prev_stream) = self.get_stream(alias) { + if prev_stream == stream { + // Nothing to do, as the stream is already present + return; + } + + prev_stream.options_mut().set_alias_to_atom_opt(None); + } + + stream.options_mut().set_alias_to_atom_opt(Some(alias)); + + self.stream_aliases.insert(alias, stream); + self.streams.insert(stream); + } + #[inline] pub(super) fn new() -> Self { index_store!( @@ -468,3 +557,13 @@ impl IndexStore { ) } } + +/// A stream is said to have a "protected" alias if modifying its +/// alias would cause breakage in other parts of the code. +/// +/// A stream with a protected alias cannot be realiased through +/// [`IndexStore::update_stream_options`]. Instead, one has to use +/// [`IndexStore::set_stream`] to do so. +fn is_protected_alias(alias: Atom) -> bool { + alias == atom!("user_input") || alias == atom!("user_output") || alias == atom!("user_error") +} diff --git a/src/machine/mod.rs b/src/machine/mod.rs index a62c0caa8..57a6213b4 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -483,32 +483,16 @@ impl Machine { } } + /// Ensures that [`Machine::indices`] properly reflects + /// the streams stored in [`Machine::user_input`], [`Machine::user_output`] + /// and [`Machine::user_error`]. pub(crate) fn configure_streams(&mut self) { - self.user_input - .options_mut() - .set_alias_to_atom_opt(Some(atom!("user_input"))); - self.indices - .stream_aliases - .insert(atom!("user_input"), self.user_input); - - self.indices.streams.insert(self.user_input); - - self.user_output - .options_mut() - .set_alias_to_atom_opt(Some(atom!("user_output"))); - + .set_stream(atom!("user_input"), self.user_input); self.indices - .stream_aliases - .insert(atom!("user_output"), self.user_output); - - self.indices.streams.insert(self.user_output); - + .set_stream(atom!("user_output"), self.user_output); self.indices - .stream_aliases - .insert(atom!("user_error"), self.user_error); - - self.indices.streams.insert(self.user_error); + .set_stream(atom!("user_error"), self.user_error); } #[inline(always)] diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 212ec4637..3432fadcd 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -1594,7 +1594,7 @@ impl MachineState { pub(crate) fn get_stream_or_alias( &mut self, addr: HeapCellValue, - stream_aliases: &StreamAliasDir, + indices: &IndexStore, caller: Atom, arity: usize, ) -> Result { @@ -1604,8 +1604,8 @@ impl MachineState { (HeapCellValueTag::Atom, (name, arity)) => { debug_assert_eq!(arity, 0); - return match stream_aliases.get(&name) { - Some(stream) if !stream.is_null_stream() => Ok(*stream), + return match indices.get_stream(name) { + Some(stream) if !stream.is_null_stream() => Ok(stream), _ => { let stub = functor_stub(caller, arity); let addr = atom_as_cell!(name); @@ -1622,8 +1622,8 @@ impl MachineState { debug_assert_eq!(arity, 0); - return match stream_aliases.get(&name) { - Some(stream) if !stream.is_null_stream() => Ok(*stream), + return match indices.get_stream(name) { + Some(stream) if !stream.is_null_stream() => Ok(stream), _ => { let stub = functor_stub(caller, arity); let addr = atom_as_cell!(name); @@ -1813,7 +1813,7 @@ impl MachineState { // 8.11.5.3l) if let Some(alias) = options.get_alias() { - if indices.stream_aliases.contains_key(&alias) { + if indices.has_stream(alias) { return Err(self.occupied_alias_permission_error(alias, atom!("open"), 4)); } } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 7a7eeb57c..2b05b8f03 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -41,7 +41,6 @@ use indexmap::IndexSet; use std::cell::Cell; use std::cmp::Ordering; -use std::collections::BTreeSet; use std::convert::TryFrom; use std::env; #[cfg(feature = "ffi")] @@ -55,7 +54,6 @@ use std::mem; use std::net::{SocketAddr, ToSocketAddrs}; use std::net::{TcpListener, TcpStream}; use std::num::NonZeroU32; -use std::ops::Sub; use std::process; #[cfg(feature = "http")] use std::str::FromStr; @@ -2543,7 +2541,7 @@ impl Machine { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("peek_byte"), 2, )?; @@ -2634,7 +2632,7 @@ impl Machine { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("peek_char"), 2, )?; @@ -2726,7 +2724,7 @@ impl Machine { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("peek_code"), 2, )?; @@ -3147,7 +3145,7 @@ impl Machine { pub(crate) fn put_code(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("put_code"), 2, )?; @@ -3199,7 +3197,7 @@ impl Machine { pub(crate) fn put_char(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("put_char"), 2, )?; @@ -3243,7 +3241,7 @@ impl Machine { pub(crate) fn put_chars(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("$put_chars"), 2, )?; @@ -3292,7 +3290,7 @@ impl Machine { pub(crate) fn put_byte(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("put_byte"), 2, )?; @@ -3359,7 +3357,7 @@ impl Machine { pub(crate) fn get_byte(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("get_byte"), 2, )?; @@ -3444,7 +3442,7 @@ impl Machine { pub(crate) fn get_char(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("get_char"), 2, )?; @@ -3539,7 +3537,7 @@ impl Machine { pub(crate) fn get_n_chars(&mut self) -> CallResult { let stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("get_n_chars"), 3, )?; @@ -3608,7 +3606,7 @@ impl Machine { pub(crate) fn get_code(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("get_code"), 2, )?; @@ -3715,19 +3713,11 @@ impl Machine { #[inline(always)] pub(crate) fn first_stream(&mut self) { - let mut first_stream = None; - let mut null_streams = BTreeSet::new(); - - for stream in self.indices.streams.iter().cloned() { - if !stream.is_null_stream() { - first_stream = Some(stream); - break; - } else { - null_streams.insert(stream); - } - } - - self.indices.streams = self.indices.streams.sub(&null_streams); + let first_stream = self + .indices + .iter_streams(..) + .filter(|s| !s.is_null_stream()) + .next(); if let Some(first_stream) = first_stream { let stream = stream_as_cell!(first_stream); @@ -3743,20 +3733,12 @@ impl Machine { #[inline(always)] pub(crate) fn next_stream(&mut self) { let prev_stream = cell_as_stream!(self.deref_register(1)); - - let mut next_stream = None; - let mut null_streams = BTreeSet::new(); - - for stream in self.indices.streams.range(prev_stream..).skip(1).cloned() { - if !stream.is_null_stream() { - next_stream = Some(stream); - break; - } else { - null_streams.insert(stream); - } - } - - self.indices.streams = self.indices.streams.sub(&null_streams); + let next_stream = self + .indices + .iter_streams(prev_stream..) + .filter(|s| !s.is_null_stream()) + .skip(1) + .next(); if let Some(next_stream) = next_stream { let var = self.deref_register(2).as_var().unwrap(); @@ -3772,7 +3754,7 @@ impl Machine { pub(crate) fn flush_output(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("flush_output"), 1, )?; @@ -3859,7 +3841,7 @@ impl Machine { pub(crate) fn close(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("close"), 2, )?; @@ -3873,11 +3855,7 @@ impl Machine { return Ok(()); } - self.indices.streams.remove(&stream); - - if let Some(alias) = stream.options().get_alias() { - self.indices.stream_aliases.swap_remove(&alias); - } + self.indices.remove_stream(stream); stream.close().map_err(|_| { let stub = functor_stub(atom!("close"), 1); @@ -4445,11 +4423,10 @@ impl Machine { &mut self.machine_st.arena, ); *stream.options_mut() = StreamOptions::default(); - if let Some(alias) = stream.options().get_alias() { - self.indices.stream_aliases.insert(alias, stream); - } - self.indices.streams.insert(stream); + self.indices + .add_stream(stream, atom!("http_open"), 3) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; let stream = stream_as_cell!(stream); @@ -4667,7 +4644,10 @@ impl Machine { ); *stream.options_mut() = StreamOptions::default(); stream.options_mut().set_stream_type(StreamType::Binary); - self.indices.streams.insert(stream); + + self.indices.add_stream(stream, atom!("http_accept"), 7) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; + let stream = stream_as_cell!(stream); let handle: TypedArenaPtr = arena_alloc!(request.response, &mut self.machine_st.arena); @@ -4781,7 +4761,11 @@ impl Machine { ); *stream.options_mut() = StreamOptions::default(); stream.options_mut().set_stream_type(StreamType::Binary); - self.indices.streams.insert(stream); + + + self.indices.add_stream(stream, atom!("http_answer"), 4) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; + let stream = stream_as_cell!(stream); self.machine_st.bind(stream_addr.as_var().unwrap(), stream); } @@ -5096,11 +5080,10 @@ impl Machine { .stream_from_file_spec(file_spec, &mut self.indices, &options)?; *stream.options_mut() = options; - self.indices.streams.insert(stream); - if let Some(alias) = stream.options().get_alias() { - self.indices.stream_aliases.insert(alias, stream); - } + self.indices + .add_stream(stream, atom!("open"), 4) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; let stream_var = self.deref_register(3); self.machine_st @@ -5181,7 +5164,7 @@ impl Machine { pub(crate) fn set_stream_options(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("open"), 4, )?; @@ -5937,12 +5920,9 @@ impl Machine { pub(crate) fn set_input(&mut self) -> CallResult { let addr = self.deref_register(1); - let stream = self.machine_st.get_stream_or_alias( - addr, - &self.indices.stream_aliases, - atom!("set_input"), - 1, - )?; + let stream = + self.machine_st + .get_stream_or_alias(addr, &self.indices, atom!("set_input"), 1)?; if !stream.is_input_stream() { let stub = functor_stub(atom!("set_input"), 1); @@ -5964,12 +5944,9 @@ impl Machine { #[inline(always)] pub(crate) fn set_output(&mut self) -> CallResult { let addr = self.deref_register(1); - let stream = self.machine_st.get_stream_or_alias( - addr, - &self.indices.stream_aliases, - atom!("set_output"), - 1, - )?; + let stream = + self.machine_st + .get_stream_or_alias(addr, &self.indices, atom!("set_output"), 1)?; if !stream.is_output_stream() { let stub = functor_stub(atom!("set_output"), 1); @@ -6275,7 +6252,7 @@ impl Machine { let stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("read_term"), 3, )?; @@ -6516,7 +6493,7 @@ impl Machine { } if let Some(alias) = options.get_alias() { - if self.indices.stream_aliases.contains_key(&alias) { + if self.indices.has_stream(alias) { return Err(self.machine_st.occupied_alias_permission_error( alias, atom!("socket_client_open"), @@ -6532,11 +6509,9 @@ impl Machine { *stream.options_mut() = options; - if let Some(alias) = stream.options().get_alias() { - self.indices.stream_aliases.insert(alias, stream); - } - - self.indices.streams.insert(stream); + self.indices + .add_stream(stream, atom!("socket_client_open"), 7) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; stream_as_cell!(stream) } @@ -6544,7 +6519,7 @@ impl Machine { return Err(self.machine_st.open_permission_error( addr, atom!("socket_client_open"), - 3, + 7, )); } Err(ErrorKind::NotFound) => { @@ -6661,7 +6636,7 @@ impl Machine { } if let Some(alias) = options.get_alias() { - if self.indices.stream_aliases.contains_key(&alias) { + if self.indices.has_stream(alias) { return Err(self.machine_st.occupied_alias_permission_error( alias, atom!("socket_server_accept"), @@ -6688,11 +6663,10 @@ impl Machine { *tcp_stream.options_mut() = options; - if let Some(alias) = &tcp_stream.options().get_alias() { - self.indices.stream_aliases.insert(*alias, tcp_stream); - } - - self.indices.streams.insert(tcp_stream); + self.indices.add_stream(tcp_stream, atom!("socket_server_accept"), 4) + .map_err(|stub_gen| { + stub_gen(&mut self.machine_st) + })?; let tcp_stream = stream_as_cell!(tcp_stream); let client = atom_as_cell!(client); @@ -6728,7 +6702,7 @@ impl Machine { { let stream0 = self.machine_st.get_stream_or_alias( self.machine_st.registers[2], - &self.indices.stream_aliases, + &self.indices, atom!("tls_client_negotiate"), 3, )?; @@ -6747,7 +6721,10 @@ impl Machine { let addr = atom!("TLS"); let stream = Stream::from_tls_stream(addr, stream, &mut self.machine_st.arena); - self.indices.streams.insert(stream); + + self.indices + .add_stream(stream, atom!("tls_client_negotiate"), 3) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; self.machine_st.heap.push(stream_as_cell!(stream)); let stream_addr = self.deref_register(3); @@ -6782,7 +6759,7 @@ impl Machine { let stream0 = self.machine_st.get_stream_or_alias( self.machine_st.registers[3], - &self.indices.stream_aliases, + &self.indices, atom!("tls_server_negotiate"), 3, )?; @@ -6801,7 +6778,10 @@ impl Machine { }; let stream = Stream::from_tls_stream(atom!("TLS"), stream, &mut self.machine_st.arena); - self.indices.streams.insert(stream); + + self.indices + .add_stream(stream, atom!("tls_server_negotiate"), 3) + .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; let stream_addr = self.deref_register(4); self.machine_st @@ -6843,7 +6823,7 @@ impl Machine { pub(crate) fn set_stream_position(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("set_stream_position"), 2, )?; @@ -6886,7 +6866,7 @@ impl Machine { pub(crate) fn stream_property(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("stream_property"), 2, )?; @@ -7237,7 +7217,7 @@ impl Machine { pub(crate) fn write_term(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("write_term"), 3, )?; @@ -8113,7 +8093,7 @@ impl Machine { pub(crate) fn devour_whitespace(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], - &self.indices.stream_aliases, + &self.indices, atom!("$devour_whitespace"), 1, )?; From 949d316773906807c1d9a07891411f66d72c62e5 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Mon, 3 Feb 2025 14:26:33 +0100 Subject: [PATCH 19/67] Fix realiased streams causing close/1 to leave a dangling stream --- src/machine/streams.rs | 22 +++++++++++++++++++++- src/machine/system_calls.rs | 8 +++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 3432fadcd..ca348a0ef 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -640,7 +640,7 @@ impl Stream { } } - pub fn options_mut(&mut self) -> &mut StreamOptions { + pub(super) fn options_mut(&mut self) -> &mut StreamOptions { match self { Stream::Byte(ref mut ptr) => &mut ptr.options, Stream::InputFile(ref mut ptr) => &mut ptr.options, @@ -1946,4 +1946,24 @@ mod test { assert_eq!(results.len(), 1); assert!(results[0].is_ok()); } + + #[test] + #[cfg_attr(miri, ignore)] + fn close_realiased_stream() { + let mut machine = MachineBuilder::new().build(); + + let results = machine + .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::>(); + + assert_eq!(results.len(), 1); + assert!(results[0].is_ok()); + } } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 2b05b8f03..50c28549f 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5162,7 +5162,7 @@ impl Machine { #[inline(always)] pub(crate) fn set_stream_options(&mut self) -> CallResult { - let mut stream = self.machine_st.get_stream_or_alias( + let stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], &self.indices, atom!("open"), @@ -5174,10 +5174,12 @@ impl Machine { let reposition = self.machine_st.registers[4]; let stream_type = self.machine_st.registers[5]; - let options = + let new_options = self.machine_st .get_stream_options(alias, eof_action, reposition, stream_type); - *stream.options_mut() = options; + self.indices.update_stream_options(stream, |options| { + *options = new_options; + }); Ok(()) } From 7f2ce57ba77b68fe85fcc25bbf94f9450ad9cc9d Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Mon, 3 Feb 2025 14:58:04 +0100 Subject: [PATCH 20/67] Fix stream realiasing possibly shadowing other streams. --- src/machine/machine_indices.rs | 19 ++++++++++++++ src/machine/streams.rs | 47 ++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index 4c8490683..e2d7b2488 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -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 { self.stream_aliases.get(&alias).copied() } diff --git a/src/machine/streams.rs b/src/machine/streams.rs index ca348a0ef..b1ecba035 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -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::>(); + + 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::>(); assert_eq!(results.len(), 1); From d8213e29c5a4a2e628bd2e15400165bf3c3e1049 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Thu, 6 Feb 2025 22:55:40 +0100 Subject: [PATCH 21/67] Fix set_output/1 and set_input/1 not updating the alias Before this change, the following set of queries would behave incorrectly: ``` ?- open("/tmp/out.log", write, S), set_output(S). prints(""), write("/tmp/out.log", "S = stream(...)"). ?- write(user_output, hello). prints("hello"), unexpected. prints(""), write("/tmp/out.log", "hello"). % Expected, but not found. ``` Now, `set_output/1` and `set_input/1` properly bind the `user_output` and `user_input` aliases, making the queries above behave as expected. --- src/machine/system_calls.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 50c28549f..71e901822 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5940,6 +5940,7 @@ impl Machine { } self.user_input = stream; + self.indices.set_stream(atom!("user_input"), stream); Ok(()) } @@ -5964,6 +5965,7 @@ impl Machine { } self.user_output = stream; + self.indices.set_stream(atom!("user_output"), stream); Ok(()) } From 2a218f34b91d6d5ab7f3c45c3523b03b2812b693 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Thu, 6 Feb 2025 23:47:22 +0100 Subject: [PATCH 22/67] Test corner cases of stream aliasing --- src/tests/stream-aliasing.pl | 19 +++++++++++++++++++ .../cli/src_tests/alias_dropped_stream.stdin | 2 ++ .../cli/src_tests/alias_dropped_stream.stdout | 1 + .../cli/src_tests/alias_dropped_stream.toml | 2 ++ .../cli/src_tests/realias_user_output.stdin | 2 ++ .../cli/src_tests/realias_user_output.stdout | 1 + .../cli/src_tests/realias_user_output.toml | 2 ++ .../cli/src_tests/set_output_alias.stderr | 1 + .../cli/src_tests/set_output_alias.stdin | 2 ++ .../cli/src_tests/set_output_alias.toml | 2 ++ 10 files changed, 34 insertions(+) create mode 100644 src/tests/stream-aliasing.pl create mode 100644 tests/scryer/cli/src_tests/alias_dropped_stream.stdin create mode 100644 tests/scryer/cli/src_tests/alias_dropped_stream.stdout create mode 100644 tests/scryer/cli/src_tests/alias_dropped_stream.toml create mode 100644 tests/scryer/cli/src_tests/realias_user_output.stdin create mode 100644 tests/scryer/cli/src_tests/realias_user_output.stdout create mode 100644 tests/scryer/cli/src_tests/realias_user_output.toml create mode 100644 tests/scryer/cli/src_tests/set_output_alias.stderr create mode 100644 tests/scryer/cli/src_tests/set_output_alias.stdin create mode 100644 tests/scryer/cli/src_tests/set_output_alias.toml diff --git a/src/tests/stream-aliasing.pl b/src/tests/stream-aliasing.pl new file mode 100644 index 000000000..3b4eb1ace --- /dev/null +++ b/src/tests/stream-aliasing.pl @@ -0,0 +1,19 @@ +% NOTE: the tests in this file will need to be changed once +% `open(stream(S1), read, _, NewOptions)` creates a new stream handle `S2` instead of updating +% the options in `S1`. In that case, that specific query will need to be updated +% to instead change the options of `S1`. + +alias_dropped_stream :- + open("README.md", read, S, [alias(readme)]), + open(stream(S), read, _, [alias(not_readme)]), + close(S), + stream_property(readme, file_name(_)). % Should throw an existence_error + +realias_user_output :- + current_output(S), + open(stream(S), read, _, [alias(not_user_output)]), + stream_property(S, alias(user_output)). % Should succeed + +set_output_alias :- + set_output(user_error), + write(user_output, hello). % Should write into stderr, not stdout diff --git a/tests/scryer/cli/src_tests/alias_dropped_stream.stdin b/tests/scryer/cli/src_tests/alias_dropped_stream.stdin new file mode 100644 index 000000000..39dd91345 --- /dev/null +++ b/tests/scryer/cli/src_tests/alias_dropped_stream.stdin @@ -0,0 +1,2 @@ +alias_dropped_stream. +halt. diff --git a/tests/scryer/cli/src_tests/alias_dropped_stream.stdout b/tests/scryer/cli/src_tests/alias_dropped_stream.stdout new file mode 100644 index 000000000..1291cbcc6 --- /dev/null +++ b/tests/scryer/cli/src_tests/alias_dropped_stream.stdout @@ -0,0 +1 @@ + error(existence_error(stream,readme),stream_property/0). diff --git a/tests/scryer/cli/src_tests/alias_dropped_stream.toml b/tests/scryer/cli/src_tests/alias_dropped_stream.toml new file mode 100644 index 000000000..06611a9a3 --- /dev/null +++ b/tests/scryer/cli/src_tests/alias_dropped_stream.toml @@ -0,0 +1,2 @@ +# Part of issue 2806 +args = ["-f", "--no-add-history", "src/tests/stream-aliasing.pl"] diff --git a/tests/scryer/cli/src_tests/realias_user_output.stdin b/tests/scryer/cli/src_tests/realias_user_output.stdin new file mode 100644 index 000000000..e6b5d912e --- /dev/null +++ b/tests/scryer/cli/src_tests/realias_user_output.stdin @@ -0,0 +1,2 @@ +realias_user_output. +halt. diff --git a/tests/scryer/cli/src_tests/realias_user_output.stdout b/tests/scryer/cli/src_tests/realias_user_output.stdout new file mode 100644 index 000000000..d5c487011 --- /dev/null +++ b/tests/scryer/cli/src_tests/realias_user_output.stdout @@ -0,0 +1 @@ + true. diff --git a/tests/scryer/cli/src_tests/realias_user_output.toml b/tests/scryer/cli/src_tests/realias_user_output.toml new file mode 100644 index 000000000..06611a9a3 --- /dev/null +++ b/tests/scryer/cli/src_tests/realias_user_output.toml @@ -0,0 +1,2 @@ +# Part of issue 2806 +args = ["-f", "--no-add-history", "src/tests/stream-aliasing.pl"] diff --git a/tests/scryer/cli/src_tests/set_output_alias.stderr b/tests/scryer/cli/src_tests/set_output_alias.stderr new file mode 100644 index 000000000..2b8b2f183 --- /dev/null +++ b/tests/scryer/cli/src_tests/set_output_alias.stderr @@ -0,0 +1 @@ +hello true. diff --git a/tests/scryer/cli/src_tests/set_output_alias.stdin b/tests/scryer/cli/src_tests/set_output_alias.stdin new file mode 100644 index 000000000..43ae91e00 --- /dev/null +++ b/tests/scryer/cli/src_tests/set_output_alias.stdin @@ -0,0 +1,2 @@ +set_output_alias. +halt. diff --git a/tests/scryer/cli/src_tests/set_output_alias.toml b/tests/scryer/cli/src_tests/set_output_alias.toml new file mode 100644 index 000000000..06611a9a3 --- /dev/null +++ b/tests/scryer/cli/src_tests/set_output_alias.toml @@ -0,0 +1,2 @@ +# Part of issue 2806 +args = ["-f", "--no-add-history", "src/tests/stream-aliasing.pl"] From 8966e175f1a60b594bbc21752838f66920db75cf Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 7 Feb 2025 14:35:27 +0100 Subject: [PATCH 23/67] [fixup] return that reading from a null stream wrote 0 bytes to the buffer --- src/machine/streams.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 443c01830..253bd47ea 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -836,7 +836,7 @@ impl Read for Stream { ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, )), - Stream::Null(_) => Ok(buf.len()), + Stream::Null(_) => Ok(0), Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) => { Err(std::io::Error::new( ErrorKind::PermissionDenied, From f45b0bcfe8cb0bebcdcc7994f9b63661cfc7ad77 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 7 Feb 2025 14:59:32 +0100 Subject: [PATCH 24/67] Remove redundant alias resolution in at_end_of_stream/1, add corresponding tests for null streams Also fixed at_end_of_stream/0 leaving a choicepoint. --- src/lib/builtins.pl | 10 +++--- src/machine/mod.rs | 5 ++- src/machine/streams.rs | 75 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 745cbc487..f6df8da75 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -2188,12 +2188,10 @@ %% at_end_of_stream(+Stream). % % True iff the stream Stream has ended -at_end_of_stream(S_or_a) :- - ( var(S_or_a) -> +at_end_of_stream(S) :- + ( var(S) -> throw(error(instantiation_error, at_end_of_stream/1)) - ; atom(S_or_a) -> - stream_property(S, alias(S_or_a)) - ; S = S_or_a + ; true ), stream_property(S, end_of_stream(E)), ( E = at -> true ; E = past ). @@ -2205,7 +2203,7 @@ current_input(S), stream_property(S, end_of_stream(E)), !, - ( E = at ; E = past ). + ( E = at -> true ; E = past ). %% set_stream_position(+Stream, +Position). % diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 107240300..e2306283b 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -510,9 +510,12 @@ impl Machine { self.indices.streams.insert(self.user_error); + let mut null_options = StreamOptions::default(); + null_options.set_alias_to_atom_opt(Some(atom!("null_stream"))); + self.indices .stream_aliases - .insert(atom!("null_stream"), Stream::Null(StreamOptions::default())); + .insert(atom!("null_stream"), Stream::Null(null_options)); } #[inline(always)] diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 253bd47ea..54efbd39c 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -1908,6 +1908,14 @@ impl MachineState { mod test { use super::*; use crate::machine::config::*; + use crate::LeafAnswer; + + fn is_successful(answer: &Result) -> bool { + matches!( + answer, + Ok(LeafAnswer::True) | Ok(LeafAnswer::LeafAnswer { .. }) + ) + } #[test] #[cfg_attr(miri, ignore)] @@ -1919,7 +1927,7 @@ mod test { let results = machine.run_query("current_input(S).").collect::>(); assert_eq!(results.len(), 1); - assert!(results[0].is_ok()); + assert!(is_successful(&results[0])); } #[test] @@ -1933,7 +1941,7 @@ mod test { assert_eq!(results.len(), 1); assert!( - results[0].is_ok(), + is_successful(&results[0]), "Expected read to succeed, got {:?}", results[0] ); @@ -1951,7 +1959,7 @@ mod test { let results = machine.run_query("current_output(S).").collect::>(); assert_eq!(results.len(), 1); - assert!(results[0].is_ok()); + assert!(is_successful(&results[0])); } #[test] @@ -1967,7 +1975,28 @@ mod test { assert_eq!(results.len(), 1); assert!( - results[0].is_ok(), + is_successful(&results[0]), + "Expected write to succeed, got {:?}", + results[0] + ); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn put_code_null_stream() { + // TODO: switch to a proper solution for configuring the machine with null streams + // once `StreamConfig` supports it. + let mut machine = MachineBuilder::new().build(); + machine.user_output = Stream::Null(StreamOptions::default()); + machine.configure_streams(); + + let results = machine + .run_query("put_code(user_output, 65).") + .collect::>(); + + assert_eq!(results.len(), 1); + assert!( + is_successful(&results[0]), "Expected write to succeed, got {:?}", results[0] ); @@ -1987,9 +2016,45 @@ mod test { assert_eq!(results.len(), 1); assert!( - results[0].is_ok(), + is_successful(&results[0]), "Expected write to succeed, got {:?}", results[0] ); } + + #[test] + #[cfg_attr(miri, ignore)] + fn at_end_of_stream_0_null_stream() { + let mut machine = MachineBuilder::new() + .with_streams(StreamConfig::in_memory()) + .build(); + + let results = machine.run_query("at_end_of_stream.").collect::>(); + + assert_eq!(results.len(), 1); + assert!( + is_successful(&results[0]), + "Expected at_end_of_stream to succeed, got {:?}", + results[0] + ); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn at_end_of_stream_1_null_stream() { + let mut machine = MachineBuilder::new() + .with_streams(StreamConfig::in_memory()) + .build(); + + let results = machine + .run_query("current_input(Stream), at_end_of_stream(Stream).") + .collect::>(); + + assert_eq!(results.len(), 1); + assert!( + is_successful(&results[0]), + "Expected at_end_of_stream to succeed, got {:?}", + results[0] + ); + } } From dd6533e76c3e41f3ffb2fe6782dcb41210debefa Mon Sep 17 00:00:00 2001 From: bakaq Date: Tue, 28 Jan 2025 17:58:40 -0300 Subject: [PATCH 25/67] Add callback streams --- src/arena.rs | 4 ++ src/machine/config.rs | 25 ++++++++++++- src/machine/streams.rs | 84 +++++++++++++++++++++++++++++++++++++++--- src/macros.rs | 1 + 4 files changed, 107 insertions(+), 7 deletions(-) diff --git a/src/arena.rs b/src/arena.rs index e5aceb4df..e3e6cbbdf 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -181,6 +181,7 @@ pub enum ArenaHeaderTag { ReadlineStream = 0b110000, StaticStringStream = 0b110100, ByteStream = 0b111000, + CallbackStream = 0b111001, StandardOutputStream = 0b1100, StandardErrorStream = 0b11000, NullStream = 0b111100, @@ -841,6 +842,9 @@ unsafe fn drop_slab_in_place(value: NonNull, tag: ArenaHeaderTag) { ArenaHeaderTag::ByteStream => { drop_typed_slab_in_place!(ByteStream, value); } + ArenaHeaderTag::CallbackStream => { + drop_typed_slab_in_place!(CallbackStream, value); + } ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => { drop_typed_slab_in_place!(LiveLoadState, value); } diff --git a/src/machine/config.rs b/src/machine/config.rs index 2981899d0..34529434d 100644 --- a/src/machine/config.rs +++ b/src/machine/config.rs @@ -6,7 +6,8 @@ use crate::Machine; use super::{ bootstrapping_compile, current_dir, import_builtin_impls, libraries, load_module, Atom, - CompilationTarget, IndexStore, ListingSource, MachineArgs, MachineState, Stream, StreamOptions, + Callback, CompilationTarget, IndexStore, ListingSource, MachineArgs, MachineState, Stream, + StreamOptions, }; /// Describes how the streams of a [`Machine`](crate::Machine) will be handled. @@ -31,6 +32,13 @@ impl StreamConfig { inner: StreamConfigInner::Memory, } } + + /// Calls the given callbacks when the respective streams are written to. + pub fn with_callbacks(stdout: Option, stderr: Option) -> Self { + StreamConfig { + inner: StreamConfigInner::Callbacks { stdout, stderr }, + } + } } #[derive(Default)] @@ -38,6 +46,10 @@ enum StreamConfigInner { Stdio, #[default] Memory, + Callbacks { + stdout: Option, + stderr: Option, + }, } /// Describes how a [`Machine`](crate::Machine) will be configured. @@ -90,6 +102,17 @@ impl MachineBuilder { Stream::from_owned_string("".to_owned(), &mut machine_st.arena), Stream::stderr(&mut machine_st.arena), ), + StreamConfigInner::Callbacks { stdout, stderr } => ( + Stream::Null(StreamOptions::default()), + stdout.map_or_else( + || Stream::Null(StreamOptions::default()), + |x| Stream::from_callback(x, &mut machine_st.arena), + ), + stderr.map_or_else( + || Stream::Null(StreamOptions::default()), + |x| Stream::from_callback(x, &mut machine_st.arena), + ), + ), }; let mut wam = Machine { diff --git a/src/machine/streams.rs b/src/machine/streams.rs index b1ecba035..0c180d4a7 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -24,6 +24,7 @@ use std::fs::{File, OpenOptions}; use std::hash::Hash; use std::io; use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write}; +use std::mem::ManuallyDrop; use std::net::{Shutdown, TcpStream}; use std::ops::{Deref, DerefMut}; use std::path::PathBuf; @@ -375,6 +376,40 @@ impl Write for StandardErrorStream { } } +pub type Callback = Box>)>; + +pub struct CallbackStream { + pub(crate) inner: Cursor>, + callback: Callback, +} + +impl Debug for CallbackStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CallbackStream") + .field("inner", &self.inner) + .finish() + } +} + +impl Write for CallbackStream { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let pos = self.inner.position(); + + self.inner.seek(SeekFrom::End(0))?; + let result = self.inner.write(buf); + self.inner.seek(SeekFrom::Start(pos))?; + + result + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + (self.callback)(&mut self.inner); + self.inner.flush() + } +} + #[bitfield] #[repr(u64)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -500,6 +535,7 @@ arena_allocated_impl_for_stream!(ReadlineStream, ReadlineStream); arena_allocated_impl_for_stream!(StaticStringStream, StaticStringStream); arena_allocated_impl_for_stream!(StandardOutputStream, StandardOutputStream); arena_allocated_impl_for_stream!(StandardErrorStream, StandardErrorStream); +arena_allocated_impl_for_stream!(CharReader, CallbackStream); #[derive(Debug, Copy, Clone)] pub enum Stream { @@ -518,6 +554,7 @@ pub enum Stream { Readline(TypedArenaPtr), StandardOutput(TypedArenaPtr), StandardError(TypedArenaPtr), + Callback(TypedArenaPtr), } impl From> for Stream { @@ -581,6 +618,7 @@ impl Stream { ArenaHeaderTag::Dropped | ArenaHeaderTag::NullStream => { Stream::Null(StreamOptions::default()) } + ArenaHeaderTag::CallbackStream => Stream::Callback(unsafe { ptr.as_typed_ptr() }), _ => unreachable!(), } } @@ -617,6 +655,7 @@ impl Stream { Stream::Readline(ptr) => ptr.header_ptr(), Stream::StandardOutput(ptr) => ptr.header_ptr(), Stream::StandardError(ptr) => ptr.header_ptr(), + Stream::Callback(ptr) => ptr.header_ptr(), } } @@ -637,6 +676,7 @@ impl Stream { Stream::Readline(ref ptr) => &ptr.options, Stream::StandardOutput(ref ptr) => &ptr.options, Stream::StandardError(ref ptr) => &ptr.options, + Stream::Callback(ref ptr) => &ptr.options, } } @@ -657,6 +697,7 @@ impl Stream { Stream::Readline(ref mut ptr) => &mut ptr.options, Stream::StandardOutput(ref mut ptr) => &mut ptr.options, Stream::StandardError(ref mut ptr) => &mut ptr.options, + Stream::Callback(ref mut ptr) => &mut ptr.options, } } @@ -678,6 +719,7 @@ impl Stream { Stream::Readline(ptr) => ptr.lines_read += incr_num_lines_read, Stream::StandardOutput(ptr) => ptr.lines_read += incr_num_lines_read, Stream::StandardError(ptr) => ptr.lines_read += incr_num_lines_read, + Stream::Callback(ptr) => ptr.lines_read += incr_num_lines_read, } } @@ -699,6 +741,7 @@ impl Stream { Stream::Readline(ptr) => ptr.lines_read = value, Stream::StandardOutput(ptr) => ptr.lines_read = value, Stream::StandardError(ptr) => ptr.lines_read = value, + Stream::Callback(ptr) => ptr.lines_read = value, } } @@ -720,6 +763,7 @@ impl Stream { Stream::Readline(ptr) => ptr.lines_read, Stream::StandardOutput(ptr) => ptr.lines_read, Stream::StandardError(ptr) => ptr.lines_read, + Stream::Callback(ptr) => ptr.lines_read, } } } @@ -744,7 +788,8 @@ impl CharRead for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) - | Stream::Null(_) => Some(Err(std::io::Error::new( + | Stream::Null(_) + | Stream::Callback(_) => Some(Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, ))), @@ -770,7 +815,8 @@ impl CharRead for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) - | Stream::Null(_) => Some(Err(std::io::Error::new( + | Stream::Null(_) + | Stream::Callback(_) => Some(Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, ))), @@ -793,7 +839,8 @@ impl CharRead for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) - | Stream::Null(_) => {} + | Stream::Null(_) + | Stream::Callback(_) => {} } } @@ -813,7 +860,8 @@ impl CharRead for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) - | Stream::Null(_) => {} + | Stream::Null(_) + | Stream::Callback(_) => {} } } } @@ -839,7 +887,8 @@ impl Read for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) - | Stream::Null(_) => Err(std::io::Error::new( + | Stream::Null(_) + | Stream::Callback(_) => Err(std::io::Error::new( ErrorKind::PermissionDenied, StreamError::ReadFromOutputStream, )), @@ -855,6 +904,7 @@ impl Write for Stream { #[cfg(feature = "tls")] Stream::NamedTls(ref mut tls_stream) => tls_stream.get_mut().write(buf), Stream::Byte(ref mut cursor) => cursor.get_mut().write(buf), + Stream::Callback(ref mut callback_stream) => callback_stream.get_mut().write(buf), Stream::StandardOutput(stream) => stream.write(buf), Stream::StandardError(stream) => stream.write(buf), #[cfg(feature = "http")] @@ -881,6 +931,7 @@ impl Write for Stream { #[cfg(feature = "tls")] Stream::NamedTls(ref mut tls_stream) => tls_stream.stream.get_mut().flush(), Stream::Byte(ref mut cursor) => cursor.stream.get_mut().flush(), + Stream::Callback(ref mut callback_stream) => callback_stream.stream.get_mut().flush(), Stream::StandardError(stream) => stream.stream.flush(), Stream::StandardOutput(stream) => stream.stream.flush(), #[cfg(feature = "http")] @@ -1043,6 +1094,7 @@ impl Stream { Stream::Readline(stream) => stream.past_end_of_stream, Stream::StandardOutput(stream) => stream.past_end_of_stream, Stream::StandardError(stream) => stream.past_end_of_stream, + Stream::Callback(stream) => stream.past_end_of_stream, } } @@ -1069,6 +1121,7 @@ impl Stream { Stream::Readline(stream) => stream.past_end_of_stream = value, Stream::StandardOutput(stream) => stream.past_end_of_stream = value, Stream::StandardError(stream) => stream.past_end_of_stream = value, + Stream::Callback(stream) => stream.past_end_of_stream = value, } } @@ -1175,7 +1228,10 @@ impl Stream { Stream::OutputFile(file) if file.is_append => atom!("append"), #[cfg(feature = "http")] Stream::HttpWrite(_) => atom!("write"), - Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) => { + Stream::OutputFile(_) + | Stream::StandardError(_) + | Stream::StandardOutput(_) + | Stream::Callback(_) => { atom!("write") } Stream::Null(_) => atom!(""), @@ -1198,6 +1254,17 @@ impl Stream { )) } + #[inline] + pub fn from_callback(callback: Callback, arena: &mut Arena) -> Self { + Stream::Callback(arena_alloc!( + ManuallyDrop::new(StreamLayout::new(CharReader::new(CallbackStream { + inner: Cursor::new(Vec::new()), + callback, + }))), + arena + )) + } + #[inline] pub(crate) fn from_tcp_stream(address: Atom, tcp_stream: TcpStream, arena: &mut Arena) -> Self { tcp_stream.set_read_timeout(None).unwrap(); @@ -1325,6 +1392,10 @@ impl Stream { stream.drop_payload(); Ok(()) } + Stream::Callback(mut stream) => { + stream.drop_payload(); + Ok(()) + } Stream::StaticString(mut stream) => { stream.drop_payload(); Ok(()) @@ -1370,6 +1441,7 @@ impl Stream { | Stream::StandardOutput(_) | Stream::NamedTcp(..) | Stream::Byte(_) + | Stream::Callback(_) | Stream::OutputFile(..) => true, _ => false, } diff --git a/src/macros.rs b/src/macros.rs index 30a863cac..9b2cbabe4 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -305,6 +305,7 @@ macro_rules! match_untyped_arena_ptr_pat { | ArenaHeaderTag::ReadlineStream | ArenaHeaderTag::StaticStringStream | ArenaHeaderTag::ByteStream + | ArenaHeaderTag::CallbackStream | ArenaHeaderTag::StandardOutputStream | ArenaHeaderTag::StandardErrorStream }; From 7a6620b52df02f33690ecf3033b7920ae855c819 Mon Sep 17 00:00:00 2001 From: bakaq Date: Wed, 29 Jan 2025 11:35:04 -0300 Subject: [PATCH 26/67] Add input stream channel --- src/arena.rs | 4 +++ src/machine/config.rs | 57 ++++++++++++++++++++++++++++++++++++------ src/machine/streams.rs | 48 +++++++++++++++++++++++++++++++++++ src/macros.rs | 1 + 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/arena.rs b/src/arena.rs index e3e6cbbdf..113ff6f0e 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -182,6 +182,7 @@ pub enum ArenaHeaderTag { StaticStringStream = 0b110100, ByteStream = 0b111000, CallbackStream = 0b111001, + InputChannelStream = 0b111010, StandardOutputStream = 0b1100, StandardErrorStream = 0b11000, NullStream = 0b111100, @@ -845,6 +846,9 @@ unsafe fn drop_slab_in_place(value: NonNull, tag: ArenaHeaderTag) { ArenaHeaderTag::CallbackStream => { drop_typed_slab_in_place!(CallbackStream, value); } + ArenaHeaderTag::InputChannelStream => { + drop_typed_slab_in_place!(InputChannelStream, value); + } ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => { drop_typed_slab_in_place!(LiveLoadState, value); } diff --git a/src/machine/config.rs b/src/machine/config.rs index 34529434d..06dbb77ac 100644 --- a/src/machine/config.rs +++ b/src/machine/config.rs @@ -1,4 +1,7 @@ -use std::borrow::Cow; +use std::cell::RefCell; +use std::io::{Seek, SeekFrom, Write}; +use std::rc::Rc; +use std::{borrow::Cow, io::Cursor}; use rand::{rngs::StdRng, SeedableRng}; @@ -34,10 +37,45 @@ impl StreamConfig { } /// Calls the given callbacks when the respective streams are written to. - pub fn with_callbacks(stdout: Option, stderr: Option) -> Self { - StreamConfig { - inner: StreamConfigInner::Callbacks { stdout, stderr }, - } + /// + /// This also returns a handler to the stdin do the [`Machine`](crate::Machine). + pub fn with_callbacks(stdout: Option, stderr: Option) -> (UserInput, Self) { + let stdin = Rc::new(RefCell::new(Cursor::new(Vec::new()))); + ( + UserInput { + inner: stdin.clone(), + }, + StreamConfig { + inner: StreamConfigInner::Callbacks { + stdin, + stdout, + stderr, + }, + }, + ) + } +} + +/// A handler for the stdin of the [`Machine`](crate::Machine). +#[derive(Debug)] +pub struct UserInput { + inner: Rc>>>, +} + +impl Write for UserInput { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let mut inner = self.inner.borrow_mut(); + let pos = inner.position(); + + inner.seek(SeekFrom::End(0))?; + let result = inner.write(buf); + inner.seek(SeekFrom::Start(pos))?; + + result + } + + fn flush(&mut self) -> std::io::Result<()> { + self.inner.borrow_mut().flush() } } @@ -47,6 +85,7 @@ enum StreamConfigInner { #[default] Memory, Callbacks { + stdin: Rc>>>, stdout: Option, stderr: Option, }, @@ -102,8 +141,12 @@ impl MachineBuilder { Stream::from_owned_string("".to_owned(), &mut machine_st.arena), Stream::stderr(&mut machine_st.arena), ), - StreamConfigInner::Callbacks { stdout, stderr } => ( - Stream::Null(StreamOptions::default()), + StreamConfigInner::Callbacks { + stdin, + stdout, + stderr, + } => ( + Stream::input_channel(stdin, &mut machine_st.arena), stdout.map_or_else( || Stream::Null(StreamOptions::default()), |x| Stream::from_callback(x, &mut machine_st.arena), diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 0c180d4a7..ce0c8d054 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -16,6 +16,7 @@ pub use scryer_modular_bitfield::prelude::*; #[cfg(feature = "http")] use bytes::{buf::Reader as BufReader, Buf, Bytes}; +use std::cell::RefCell; use std::cmp::Ordering; use std::error::Error; use std::fmt; @@ -29,6 +30,7 @@ use std::net::{Shutdown, TcpStream}; use std::ops::{Deref, DerefMut}; use std::path::PathBuf; use std::ptr; +use std::rc::Rc; #[cfg(feature = "tls")] use native_tls::TlsStream; @@ -410,6 +412,18 @@ impl Write for CallbackStream { } } +#[derive(Debug)] +pub struct InputChannelStream { + pub(crate) inner: Rc>>>, +} + +impl Read for InputChannelStream { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.inner.borrow_mut().read(buf) + } +} + #[bitfield] #[repr(u64)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -536,6 +550,7 @@ arena_allocated_impl_for_stream!(StaticStringStream, StaticStringStream); arena_allocated_impl_for_stream!(StandardOutputStream, StandardOutputStream); arena_allocated_impl_for_stream!(StandardErrorStream, StandardErrorStream); arena_allocated_impl_for_stream!(CharReader, CallbackStream); +arena_allocated_impl_for_stream!(CharReader, InputChannelStream); #[derive(Debug, Copy, Clone)] pub enum Stream { @@ -555,6 +570,7 @@ pub enum Stream { StandardOutput(TypedArenaPtr), StandardError(TypedArenaPtr), Callback(TypedArenaPtr), + InputChannel(TypedArenaPtr), } impl From> for Stream { @@ -585,6 +601,14 @@ impl Stream { )) } + #[inline] + pub fn input_channel(cursor: Rc>>>, arena: &mut Arena) -> Stream { + Stream::InputChannel(arena_alloc!( + StreamLayout::new(CharReader::new(InputChannelStream { inner: cursor })), + arena + )) + } + #[inline] pub fn stdin(arena: &mut Arena, add_history: bool) -> Stream { Stream::Readline(arena_alloc!( @@ -619,6 +643,9 @@ impl Stream { Stream::Null(StreamOptions::default()) } ArenaHeaderTag::CallbackStream => Stream::Callback(unsafe { ptr.as_typed_ptr() }), + ArenaHeaderTag::InputChannelStream => { + Stream::InputChannel(unsafe { ptr.as_typed_ptr() }) + } _ => unreachable!(), } } @@ -656,6 +683,7 @@ impl Stream { Stream::StandardOutput(ptr) => ptr.header_ptr(), Stream::StandardError(ptr) => ptr.header_ptr(), Stream::Callback(ptr) => ptr.header_ptr(), + Stream::InputChannel(ptr) => ptr.header_ptr(), } } @@ -677,6 +705,7 @@ impl Stream { Stream::StandardOutput(ref ptr) => &ptr.options, Stream::StandardError(ref ptr) => &ptr.options, Stream::Callback(ref ptr) => &ptr.options, + Stream::InputChannel(ref ptr) => &ptr.options, } } @@ -698,6 +727,7 @@ impl Stream { Stream::StandardOutput(ref mut ptr) => &mut ptr.options, Stream::StandardError(ref mut ptr) => &mut ptr.options, Stream::Callback(ref mut ptr) => &mut ptr.options, + Stream::InputChannel(ref mut ptr) => &mut ptr.options, } } @@ -720,6 +750,7 @@ impl Stream { Stream::StandardOutput(ptr) => ptr.lines_read += incr_num_lines_read, Stream::StandardError(ptr) => ptr.lines_read += incr_num_lines_read, Stream::Callback(ptr) => ptr.lines_read += incr_num_lines_read, + Stream::InputChannel(ptr) => ptr.lines_read += incr_num_lines_read, } } @@ -742,6 +773,7 @@ impl Stream { Stream::StandardOutput(ptr) => ptr.lines_read = value, Stream::StandardError(ptr) => ptr.lines_read = value, Stream::Callback(ptr) => ptr.lines_read = value, + Stream::InputChannel(ptr) => ptr.lines_read = value, } } @@ -764,6 +796,7 @@ impl Stream { Stream::StandardOutput(ptr) => ptr.lines_read, Stream::StandardError(ptr) => ptr.lines_read, Stream::Callback(ptr) => ptr.lines_read, + Stream::InputChannel(ptr) => ptr.lines_read, } } } @@ -780,6 +813,7 @@ impl CharRead for Stream { Stream::Readline(rl_stream) => (*rl_stream).peek_char(), Stream::StaticString(src) => (*src).peek_char(), Stream::Byte(cursor) => (*cursor).peek_char(), + Stream::InputChannel(cursor) => (*cursor).peek_char(), #[cfg(feature = "http")] Stream::HttpWrite(_) => Some(Err(std::io::Error::new( ErrorKind::PermissionDenied, @@ -807,6 +841,7 @@ impl CharRead for Stream { Stream::Readline(rl_stream) => (*rl_stream).read_char(), Stream::StaticString(src) => (*src).read_char(), Stream::Byte(cursor) => (*cursor).read_char(), + Stream::InputChannel(cursor) => (*cursor).read_char(), #[cfg(feature = "http")] Stream::HttpWrite(_) => Some(Err(std::io::Error::new( ErrorKind::PermissionDenied, @@ -841,6 +876,7 @@ impl CharRead for Stream { | Stream::StandardOutput(_) | Stream::Null(_) | Stream::Callback(_) => {} + Stream::InputChannel(_) => {} } } @@ -855,6 +891,7 @@ impl CharRead for Stream { Stream::Readline(ref mut rl_stream) => rl_stream.consume(nread), Stream::StaticString(ref mut src) => src.consume(nread), Stream::Byte(ref mut cursor) => cursor.consume(nread), + Stream::InputChannel(ref mut cursor) => cursor.consume(nread), #[cfg(feature = "http")] Stream::HttpWrite(_) => {} Stream::OutputFile(_) @@ -879,6 +916,7 @@ impl Read for Stream { Stream::Readline(rl_stream) => (*rl_stream).read(buf), Stream::StaticString(src) => (*src).read(buf), Stream::Byte(cursor) => (*cursor).read(buf), + Stream::InputChannel(cursor) => (*cursor).read(buf), #[cfg(feature = "http")] Stream::HttpWrite(_) => Err(std::io::Error::new( ErrorKind::PermissionDenied, @@ -915,6 +953,7 @@ impl Write for Stream { StreamError::WriteToInputStream, )), Stream::StaticString(_) + | Stream::InputChannel(_) | Stream::Readline(_) | Stream::InputFile(..) | Stream::Null(_) => Err(std::io::Error::new( @@ -942,6 +981,7 @@ impl Write for Stream { StreamError::FlushToInputStream, )), Stream::StaticString(_) + | Stream::InputChannel(_) | Stream::Readline(_) | Stream::InputFile(_) | Stream::Null(_) => Err(std::io::Error::new( @@ -1095,6 +1135,7 @@ impl Stream { Stream::StandardOutput(stream) => stream.past_end_of_stream, Stream::StandardError(stream) => stream.past_end_of_stream, Stream::Callback(stream) => stream.past_end_of_stream, + Stream::InputChannel(stream) => stream.past_end_of_stream, } } @@ -1122,6 +1163,7 @@ impl Stream { Stream::StandardOutput(stream) => stream.past_end_of_stream = value, Stream::StandardError(stream) => stream.past_end_of_stream = value, Stream::Callback(stream) => stream.past_end_of_stream = value, + Stream::InputChannel(stream) => stream.past_end_of_stream = value, } } @@ -1221,6 +1263,7 @@ impl Stream { #[cfg(feature = "tls")] Stream::NamedTls(..) => atom!("read_append"), Stream::Byte(_) + | Stream::InputChannel(_) | Stream::Readline(_) | Stream::StaticString(_) | Stream::InputFile(..) => atom!("read"), @@ -1396,6 +1439,10 @@ impl Stream { stream.drop_payload(); Ok(()) } + Stream::InputChannel(mut stream) => { + stream.drop_payload(); + Ok(()) + } Stream::StaticString(mut stream) => { stream.drop_payload(); Ok(()) @@ -1423,6 +1470,7 @@ impl Stream { Stream::HttpRead(..) => true, Stream::NamedTcp(..) | Stream::Byte(_) + | Stream::InputChannel(_) | Stream::Readline(_) | Stream::StaticString(_) | Stream::InputFile(..) => true, diff --git a/src/macros.rs b/src/macros.rs index 9b2cbabe4..547ccc007 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -306,6 +306,7 @@ macro_rules! match_untyped_arena_ptr_pat { | ArenaHeaderTag::StaticStringStream | ArenaHeaderTag::ByteStream | ArenaHeaderTag::CallbackStream + | ArenaHeaderTag::InputChannelStream | ArenaHeaderTag::StandardOutputStream | ArenaHeaderTag::StandardErrorStream }; From 4e032c8a285ee7a15bf2d9833bc332d64916674f Mon Sep 17 00:00:00 2001 From: bakaq Date: Wed, 29 Jan 2025 17:09:48 -0300 Subject: [PATCH 27/67] Test for callback streams --- src/machine/lib_machine/tests.rs | 36 +++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/machine/lib_machine/tests.rs b/src/machine/lib_machine/tests.rs index 5b89d67da..40c33225e 100644 --- a/src/machine/lib_machine/tests.rs +++ b/src/machine/lib_machine/tests.rs @@ -1,5 +1,8 @@ +use std::io::Write; +use std::{cell::RefCell, io::Read, rc::Rc}; + use super::*; -use crate::MachineBuilder; +use crate::{MachineBuilder, StreamConfig}; #[test] #[cfg_attr(miri, ignore = "it takes too long to run")] @@ -608,3 +611,34 @@ fn errors_and_exceptions() { [Ok(LeafAnswer::Exception(Term::atom("a")))] ); } + +#[test] +#[cfg_attr(miri, ignore)] +fn callback_streams() { + let test_string = Rc::new(RefCell::new(String::new())); + let test_string2 = test_string.clone(); + + let (mut user_input, streams) = StreamConfig::with_callbacks( + Some(Box::new(move |x| { + x.read_to_string(&mut *test_string2.borrow_mut()).unwrap(); + })), + None, + ); + let mut machine = MachineBuilder::default().with_streams(streams).build(); + + write!(&mut user_input, "a(1,2,3).").unwrap(); + + let complete_answer: Vec<_> = machine + .run_query("read(A), write('asdf'), nl, flush_output.") + .collect(); + + assert_eq!( + complete_answer, + [Ok(LeafAnswer::from_bindings([( + "A", + Term::compound("a", [Term::integer(1), Term::integer(2), Term::integer(3)]) + ),]))] + ); + + assert_eq!(*test_string.borrow(), "asdf\n"); +} From baae1dca15080735f7b0a77093b86390f14f4e38 Mon Sep 17 00:00:00 2001 From: bakaq Date: Thu, 30 Jan 2025 06:14:38 -0300 Subject: [PATCH 28/67] Refactor UserInput to use channels --- src/machine/config.rs | 33 +++++++--------- src/machine/lib_machine/tests.rs | 2 +- src/machine/streams.rs | 65 +++++++++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 27 deletions(-) diff --git a/src/machine/config.rs b/src/machine/config.rs index 06dbb77ac..12e5a5381 100644 --- a/src/machine/config.rs +++ b/src/machine/config.rs @@ -1,7 +1,6 @@ -use std::cell::RefCell; -use std::io::{Seek, SeekFrom, Write}; -use std::rc::Rc; -use std::{borrow::Cow, io::Cursor}; +use std::borrow::Cow; +use std::io::Write; +use std::sync::mpsc::{channel, Receiver, Sender}; use rand::{rngs::StdRng, SeedableRng}; @@ -40,14 +39,12 @@ impl StreamConfig { /// /// This also returns a handler to the stdin do the [`Machine`](crate::Machine). pub fn with_callbacks(stdout: Option, stderr: Option) -> (UserInput, Self) { - let stdin = Rc::new(RefCell::new(Cursor::new(Vec::new()))); + let (sender, receiver) = channel(); ( - UserInput { - inner: stdin.clone(), - }, + UserInput { inner: sender }, StreamConfig { inner: StreamConfigInner::Callbacks { - stdin, + stdin: receiver, stdout, stderr, }, @@ -59,23 +56,19 @@ impl StreamConfig { /// A handler for the stdin of the [`Machine`](crate::Machine). #[derive(Debug)] pub struct UserInput { - inner: Rc>>>, + inner: Sender>, } impl Write for UserInput { fn write(&mut self, buf: &[u8]) -> std::io::Result { - let mut inner = self.inner.borrow_mut(); - let pos = inner.position(); - - inner.seek(SeekFrom::End(0))?; - let result = inner.write(buf); - inner.seek(SeekFrom::Start(pos))?; - - result + self.inner + .send(buf.into()) + .map(|_| buf.len()) + .map_err(|_| std::io::ErrorKind::BrokenPipe.into()) } fn flush(&mut self) -> std::io::Result<()> { - self.inner.borrow_mut().flush() + Ok(()) } } @@ -85,7 +78,7 @@ enum StreamConfigInner { #[default] Memory, Callbacks { - stdin: Rc>>>, + stdin: Receiver>, stdout: Option, stderr: Option, }, diff --git a/src/machine/lib_machine/tests.rs b/src/machine/lib_machine/tests.rs index 40c33225e..502f4d89b 100644 --- a/src/machine/lib_machine/tests.rs +++ b/src/machine/lib_machine/tests.rs @@ -620,7 +620,7 @@ fn callback_streams() { let (mut user_input, streams) = StreamConfig::with_callbacks( Some(Box::new(move |x| { - x.read_to_string(&mut *test_string2.borrow_mut()).unwrap(); + x.read_to_string(&mut test_string2.borrow_mut()).unwrap(); })), None, ); diff --git a/src/machine/streams.rs b/src/machine/streams.rs index ce0c8d054..edb6756b8 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -16,7 +16,6 @@ pub use scryer_modular_bitfield::prelude::*; #[cfg(feature = "http")] use bytes::{buf::Reader as BufReader, Buf, Bytes}; -use std::cell::RefCell; use std::cmp::Ordering; use std::error::Error; use std::fmt; @@ -30,7 +29,8 @@ use std::net::{Shutdown, TcpStream}; use std::ops::{Deref, DerefMut}; use std::path::PathBuf; use std::ptr; -use std::rc::Rc; +use std::sync::mpsc::Receiver; +use std::sync::mpsc::TryRecvError; #[cfg(feature = "tls")] use native_tls::TlsStream; @@ -414,13 +414,50 @@ impl Write for CallbackStream { #[derive(Debug)] pub struct InputChannelStream { - pub(crate) inner: Rc>>>, + pub(crate) inner: Cursor>, + pub eof: bool, + channel: Receiver>, } impl Read for InputChannelStream { #[inline] fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - self.inner.borrow_mut().read(buf) + if self.eof { + return Ok(0); + } + + let to_read = buf.len(); + let mut total_read = 0; + + loop { + total_read += self.inner.read(&mut buf[total_read..])?; + + if total_read < to_read { + // We need to get more data to read + match self.channel.try_recv() { + Ok(data) => { + // Append into self.inner + let pos = self.inner.position(); + assert_eq!(pos as usize, self.inner.get_ref().len()); + self.inner.write_all(&data)?; + self.inner.seek(SeekFrom::Start(pos))?; + } + Err(TryRecvError::Empty) => { + // Data is pending + break; + } + Err(TryRecvError::Disconnected) => { + // The other end of the channel was closed so we are EOF + self.eof = true; + break; + } + } + } else { + assert_eq!(total_read, to_read); + break; + } + } + Ok(total_read) } } @@ -602,9 +639,14 @@ impl Stream { } #[inline] - pub fn input_channel(cursor: Rc>>>, arena: &mut Arena) -> Stream { + pub fn input_channel(channel: Receiver>, arena: &mut Arena) -> Stream { + let inner = Cursor::new(Vec::new()); Stream::InputChannel(arena_alloc!( - StreamLayout::new(CharReader::new(InputChannelStream { inner: cursor })), + StreamLayout::new(CharReader::new(InputChannelStream { + inner, + eof: false, + channel + })), arena )) } @@ -1239,6 +1281,13 @@ impl Stream { AtEndOfStream::Past } } + Stream::InputChannel(stream_layout) => { + if stream_layout.stream.get_ref().eof { + AtEndOfStream::At + } else { + AtEndOfStream::Not + } + } _ => AtEndOfStream::Not, } } @@ -1519,6 +1568,10 @@ impl Stream { readline_stream.reset(); true } + Stream::InputChannel(ref mut input_channel_stream) => { + input_channel_stream.stream.get_mut().inner.set_position(0); + true + } _ => false, } } From 0a2457943e428f5f3d3f5f34145fdf4630c4520f Mon Sep 17 00:00:00 2001 From: bakaq Date: Thu, 30 Jan 2025 09:20:28 -0300 Subject: [PATCH 29/67] Configure streams separately --- src/machine/config.rs | 135 +++++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 49 deletions(-) diff --git a/src/machine/config.rs b/src/machine/config.rs index 12e5a5381..44d579452 100644 --- a/src/machine/config.rs +++ b/src/machine/config.rs @@ -7,22 +7,83 @@ use rand::{rngs::StdRng, SeedableRng}; use crate::Machine; use super::{ - bootstrapping_compile, current_dir, import_builtin_impls, libraries, load_module, Atom, + bootstrapping_compile, current_dir, import_builtin_impls, libraries, load_module, Arena, Atom, Callback, CompilationTarget, IndexStore, ListingSource, MachineArgs, MachineState, Stream, StreamOptions, }; -/// Describes how the streams of a [`Machine`](crate::Machine) will be handled. #[derive(Default)] +enum OutputStreamConfig { + #[default] + Null, + Memory, + Stdout, + Stderr, + Callback(Callback), +} + +impl std::fmt::Debug for OutputStreamConfig { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Null => write!(f, "Null"), + Self::Memory => write!(f, "Memory"), + Self::Stdout => write!(f, "Stdout"), + Self::Stderr => write!(f, "Stderr"), + Self::Callback(_) => f.debug_tuple("Callback").field(&"").finish(), + } + } +} + +impl OutputStreamConfig { + fn into_stream(self, arena: &mut Arena) -> Stream { + match self { + OutputStreamConfig::Null => Stream::Null(StreamOptions::default()), + OutputStreamConfig::Memory => Stream::from_owned_string("".to_owned(), arena), + OutputStreamConfig::Stdout => Stream::stdout(arena), + OutputStreamConfig::Stderr => Stream::stderr(arena), + OutputStreamConfig::Callback(callback) => Stream::from_callback(callback, arena), + } + } +} + +#[derive(Debug, Default)] +enum InputStreamConfig { + #[default] + Null, + Stdin, + Channel(Receiver>), +} + +impl InputStreamConfig { + fn into_stream(self, arena: &mut Arena, add_history: bool) -> Stream { + match self { + InputStreamConfig::Null => Stream::Null(StreamOptions::default()), + InputStreamConfig::Stdin => Stream::stdin(arena, add_history), + InputStreamConfig::Channel(channel) => Stream::input_channel(channel, arena), + } + } +} + +/// Describes how the streams of a [`Machine`](crate::Machine) will be handled. pub struct StreamConfig { - inner: StreamConfigInner, + stdin: InputStreamConfig, + stdout: OutputStreamConfig, + stderr: OutputStreamConfig, +} + +impl Default for StreamConfig { + fn default() -> Self { + Self::in_memory() + } } impl StreamConfig { /// Binds the input, output and error streams to stdin, stdout and stderr. pub fn stdio() -> Self { StreamConfig { - inner: StreamConfigInner::Stdio, + stdin: InputStreamConfig::Stdin, + stdout: OutputStreamConfig::Stdout, + stderr: OutputStreamConfig::Stderr, } } @@ -31,7 +92,9 @@ impl StreamConfig { /// The input stream is ignored. pub fn in_memory() -> Self { StreamConfig { - inner: StreamConfigInner::Memory, + stdin: InputStreamConfig::Null, + stdout: OutputStreamConfig::Memory, + stderr: OutputStreamConfig::Stderr, } } @@ -43,14 +106,24 @@ impl StreamConfig { ( UserInput { inner: sender }, StreamConfig { - inner: StreamConfigInner::Callbacks { - stdin: receiver, - stdout, - stderr, - }, + stdin: InputStreamConfig::Channel(receiver), + stdout: stdout.map_or(OutputStreamConfig::Null, |x| { + OutputStreamConfig::Callback(x) + }), + stderr: stderr.map_or(OutputStreamConfig::Null, |x| { + OutputStreamConfig::Callback(x) + }), }, ) } + + fn into_streams(self, arena: &mut Arena, add_history: bool) -> (Stream, Stream, Stream) { + ( + self.stdin.into_stream(arena, add_history), + self.stdout.into_stream(arena), + self.stderr.into_stream(arena), + ) + } } /// A handler for the stdin of the [`Machine`](crate::Machine). @@ -72,18 +145,6 @@ impl Write for UserInput { } } -#[derive(Default)] -enum StreamConfigInner { - Stdio, - #[default] - Memory, - Callbacks { - stdin: Receiver>, - stdout: Option, - stderr: Option, - }, -} - /// Describes how a [`Machine`](crate::Machine) will be configured. pub struct MachineBuilder { pub(crate) streams: StreamConfig, @@ -123,33 +184,9 @@ impl MachineBuilder { let args = MachineArgs::new(); let mut machine_st = MachineState::new(); - let (user_input, user_output, user_error) = match self.streams.inner { - StreamConfigInner::Stdio => ( - Stream::stdin(&mut machine_st.arena, args.add_history), - Stream::stdout(&mut machine_st.arena), - Stream::stderr(&mut machine_st.arena), - ), - StreamConfigInner::Memory => ( - Stream::Null(StreamOptions::default()), - Stream::from_owned_string("".to_owned(), &mut machine_st.arena), - Stream::stderr(&mut machine_st.arena), - ), - StreamConfigInner::Callbacks { - stdin, - stdout, - stderr, - } => ( - Stream::input_channel(stdin, &mut machine_st.arena), - stdout.map_or_else( - || Stream::Null(StreamOptions::default()), - |x| Stream::from_callback(x, &mut machine_st.arena), - ), - stderr.map_or_else( - || Stream::Null(StreamOptions::default()), - |x| Stream::from_callback(x, &mut machine_st.arena), - ), - ), - }; + let (user_input, user_output, user_error) = self + .streams + .into_streams(&mut machine_st.arena, args.add_history); let mut wam = Machine { machine_st, From 5386c183d2946ed24862076f3f2f7811f8e4d5a1 Mon Sep 17 00:00:00 2001 From: bakaq Date: Fri, 31 Jan 2025 07:23:39 -0300 Subject: [PATCH 30/67] Make input and output stream configuration public --- src/machine/config.rs | 127 +++++++++++++++++++++++++++++++----------- 1 file changed, 96 insertions(+), 31 deletions(-) diff --git a/src/machine/config.rs b/src/machine/config.rs index 44d579452..395dbb2fd 100644 --- a/src/machine/config.rs +++ b/src/machine/config.rs @@ -13,7 +13,7 @@ use super::{ }; #[derive(Default)] -enum OutputStreamConfig { +enum OutputStreamConfigInner { #[default] Null, Memory, @@ -22,7 +22,7 @@ enum OutputStreamConfig { Callback(Callback), } -impl std::fmt::Debug for OutputStreamConfig { +impl std::fmt::Debug for OutputStreamConfigInner { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Null => write!(f, "Null"), @@ -34,41 +34,110 @@ impl std::fmt::Debug for OutputStreamConfig { } } +/// Configuration for an output stream. +#[derive(Debug, Default)] +pub struct OutputStreamConfig { + inner: OutputStreamConfigInner, +} + impl OutputStreamConfig { + /// Ignores all output. + pub fn null() -> Self { + Self { + inner: OutputStreamConfigInner::Null, + } + } + /// Sends output to stdout. + pub fn stdout() -> Self { + Self { + inner: OutputStreamConfigInner::Stdout, + } + } + /// Sends output to stderr. + pub fn stderr() -> Self { + Self { + inner: OutputStreamConfigInner::Stderr, + } + } + /// Keeps output in a memory buffer. + pub fn memory() -> Self { + Self { + inner: OutputStreamConfigInner::Memory, + } + } + /// Calls a callback with the output whenever the stream is written to. + pub fn callback(callback: Callback) -> Self { + Self { + inner: OutputStreamConfigInner::Callback(callback), + } + } + fn into_stream(self, arena: &mut Arena) -> Stream { - match self { - OutputStreamConfig::Null => Stream::Null(StreamOptions::default()), - OutputStreamConfig::Memory => Stream::from_owned_string("".to_owned(), arena), - OutputStreamConfig::Stdout => Stream::stdout(arena), - OutputStreamConfig::Stderr => Stream::stderr(arena), - OutputStreamConfig::Callback(callback) => Stream::from_callback(callback, arena), + match self.inner { + OutputStreamConfigInner::Null => Stream::Null(StreamOptions::default()), + OutputStreamConfigInner::Memory => Stream::from_owned_string("".to_owned(), arena), + OutputStreamConfigInner::Stdout => Stream::stdout(arena), + OutputStreamConfigInner::Stderr => Stream::stderr(arena), + OutputStreamConfigInner::Callback(callback) => Stream::from_callback(callback, arena), } } } #[derive(Debug, Default)] -enum InputStreamConfig { +enum InputStreamConfigInner { #[default] Null, Stdin, Channel(Receiver>), } +/// Configuration for an input stream; +#[derive(Debug, Default)] +pub struct InputStreamConfig { + inner: InputStreamConfigInner, +} + impl InputStreamConfig { + /// Ignores all input. + pub fn null() -> Self { + Self { + inner: InputStreamConfigInner::Null, + } + } + /// Gets input from stdin. + pub fn stdin() -> Self { + Self { + inner: InputStreamConfigInner::Stdin, + } + } + /// Connects the input to the receiving end of a channel. + pub fn channel() -> (UserInput, Self) { + let (sender, receiver) = channel(); + ( + UserInput { inner: sender }, + Self { + inner: InputStreamConfigInner::Channel(receiver), + }, + ) + } + fn into_stream(self, arena: &mut Arena, add_history: bool) -> Stream { - match self { - InputStreamConfig::Null => Stream::Null(StreamOptions::default()), - InputStreamConfig::Stdin => Stream::stdin(arena, add_history), - InputStreamConfig::Channel(channel) => Stream::input_channel(channel, arena), + match self.inner { + InputStreamConfigInner::Null => Stream::Null(StreamOptions::default()), + InputStreamConfigInner::Stdin => Stream::stdin(arena, add_history), + InputStreamConfigInner::Channel(channel) => Stream::input_channel(channel, arena), } } } /// Describes how the streams of a [`Machine`](crate::Machine) will be handled. pub struct StreamConfig { - stdin: InputStreamConfig, - stdout: OutputStreamConfig, - stderr: OutputStreamConfig, + /// The configuration for the stdin of the [`Machine`](crate::Machine). + pub stdin: InputStreamConfig, + /// The configuration for the stdout of the [`Machine`](crate::Machine). + pub stdout: OutputStreamConfig, + /// The configuration for the stderr of the [`Machine`](crate::Machine). + pub stderr: OutputStreamConfig, } impl Default for StreamConfig { @@ -81,9 +150,9 @@ impl StreamConfig { /// Binds the input, output and error streams to stdin, stdout and stderr. pub fn stdio() -> Self { StreamConfig { - stdin: InputStreamConfig::Stdin, - stdout: OutputStreamConfig::Stdout, - stderr: OutputStreamConfig::Stderr, + stdin: InputStreamConfig::stdin(), + stdout: OutputStreamConfig::stdout(), + stderr: OutputStreamConfig::stderr(), } } @@ -92,9 +161,9 @@ impl StreamConfig { /// The input stream is ignored. pub fn in_memory() -> Self { StreamConfig { - stdin: InputStreamConfig::Null, - stdout: OutputStreamConfig::Memory, - stderr: OutputStreamConfig::Stderr, + stdin: InputStreamConfig::null(), + stdout: OutputStreamConfig::memory(), + stderr: OutputStreamConfig::stderr(), } } @@ -102,17 +171,13 @@ impl StreamConfig { /// /// This also returns a handler to the stdin do the [`Machine`](crate::Machine). pub fn with_callbacks(stdout: Option, stderr: Option) -> (UserInput, Self) { - let (sender, receiver) = channel(); + let (user_input, channel_stream) = InputStreamConfig::channel(); ( - UserInput { inner: sender }, + user_input, StreamConfig { - stdin: InputStreamConfig::Channel(receiver), - stdout: stdout.map_or(OutputStreamConfig::Null, |x| { - OutputStreamConfig::Callback(x) - }), - stderr: stderr.map_or(OutputStreamConfig::Null, |x| { - OutputStreamConfig::Callback(x) - }), + stdin: channel_stream, + stdout: stdout.map_or_else(OutputStreamConfig::null, OutputStreamConfig::callback), + stderr: stderr.map_or_else(OutputStreamConfig::null, OutputStreamConfig::callback), }, ) } From ad211d5e0557efeaf1a9784213377b43fcc017a7 Mon Sep 17 00:00:00 2001 From: bakaq Date: Fri, 31 Jan 2025 07:45:13 -0300 Subject: [PATCH 31/67] Disallow null streams in output --- src/machine/config.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/machine/config.rs b/src/machine/config.rs index 395dbb2fd..2e71ae263 100644 --- a/src/machine/config.rs +++ b/src/machine/config.rs @@ -15,7 +15,6 @@ use super::{ #[derive(Default)] enum OutputStreamConfigInner { #[default] - Null, Memory, Stdout, Stderr, @@ -25,7 +24,6 @@ enum OutputStreamConfigInner { impl std::fmt::Debug for OutputStreamConfigInner { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Null => write!(f, "Null"), Self::Memory => write!(f, "Memory"), Self::Stdout => write!(f, "Stdout"), Self::Stderr => write!(f, "Stderr"), @@ -41,30 +39,27 @@ pub struct OutputStreamConfig { } impl OutputStreamConfig { - /// Ignores all output. - pub fn null() -> Self { - Self { - inner: OutputStreamConfigInner::Null, - } - } /// Sends output to stdout. pub fn stdout() -> Self { Self { inner: OutputStreamConfigInner::Stdout, } } + /// Sends output to stderr. pub fn stderr() -> Self { Self { inner: OutputStreamConfigInner::Stderr, } } + /// Keeps output in a memory buffer. pub fn memory() -> Self { Self { inner: OutputStreamConfigInner::Memory, } } + /// Calls a callback with the output whenever the stream is written to. pub fn callback(callback: Callback) -> Self { Self { @@ -74,7 +69,6 @@ impl OutputStreamConfig { fn into_stream(self, arena: &mut Arena) -> Stream { match self.inner { - OutputStreamConfigInner::Null => Stream::Null(StreamOptions::default()), OutputStreamConfigInner::Memory => Stream::from_owned_string("".to_owned(), arena), OutputStreamConfigInner::Stdout => Stream::stdout(arena), OutputStreamConfigInner::Stderr => Stream::stderr(arena), @@ -104,12 +98,14 @@ impl InputStreamConfig { inner: InputStreamConfigInner::Null, } } + /// Gets input from stdin. pub fn stdin() -> Self { Self { inner: InputStreamConfigInner::Stdin, } } + /// Connects the input to the receiving end of a channel. pub fn channel() -> (UserInput, Self) { let (sender, receiver) = channel(); @@ -176,8 +172,10 @@ impl StreamConfig { user_input, StreamConfig { stdin: channel_stream, - stdout: stdout.map_or_else(OutputStreamConfig::null, OutputStreamConfig::callback), - stderr: stderr.map_or_else(OutputStreamConfig::null, OutputStreamConfig::callback), + stdout: stdout + .map_or_else(OutputStreamConfig::memory, OutputStreamConfig::callback), + stderr: stderr + .map_or_else(OutputStreamConfig::memory, OutputStreamConfig::callback), }, ) } From 28926486e01e5227017e82f4e0dbeb4d5b778021 Mon Sep 17 00:00:00 2001 From: bakaq Date: Fri, 31 Jan 2025 13:14:45 -0300 Subject: [PATCH 32/67] More stream tests --- src/machine/config.rs | 28 ++++--- src/machine/lib_machine/mod.rs | 36 ++++++-- src/machine/lib_machine/tests.rs | 36 +------- src/machine/streams.rs | 140 ++++++++++++++++++++++++++++++- 4 files changed, 183 insertions(+), 57 deletions(-) diff --git a/src/machine/config.rs b/src/machine/config.rs index 2e71ae263..02480602c 100644 --- a/src/machine/config.rs +++ b/src/machine/config.rs @@ -9,7 +9,6 @@ use crate::Machine; use super::{ bootstrapping_compile, current_dir, import_builtin_impls, libraries, load_module, Arena, Atom, Callback, CompilationTarget, IndexStore, ListingSource, MachineArgs, MachineState, Stream, - StreamOptions, }; #[derive(Default)] @@ -77,14 +76,19 @@ impl OutputStreamConfig { } } -#[derive(Debug, Default)] +#[derive(Debug)] enum InputStreamConfigInner { - #[default] - Null, + String(String), Stdin, Channel(Receiver>), } +impl Default for InputStreamConfigInner { + fn default() -> Self { + Self::String("".into()) + } +} + /// Configuration for an input stream; #[derive(Debug, Default)] pub struct InputStreamConfig { @@ -92,10 +96,10 @@ pub struct InputStreamConfig { } impl InputStreamConfig { - /// Ignores all input. - pub fn null() -> Self { + /// Gets input from string. + pub fn string(s: impl Into) -> Self { Self { - inner: InputStreamConfigInner::Null, + inner: InputStreamConfigInner::String(s.into()), } } @@ -119,7 +123,7 @@ impl InputStreamConfig { fn into_stream(self, arena: &mut Arena, add_history: bool) -> Stream { match self.inner { - InputStreamConfigInner::Null => Stream::Null(StreamOptions::default()), + InputStreamConfigInner::String(s) => Stream::from_owned_string(s, arena), InputStreamConfigInner::Stdin => Stream::stdin(arena, add_history), InputStreamConfigInner::Channel(channel) => Stream::input_channel(channel, arena), } @@ -152,14 +156,12 @@ impl StreamConfig { } } - /// Binds the output stream to a memory buffer, and the error stream to stderr. - /// - /// The input stream is ignored. + /// Binds the output and error streams to memory buffers and has an empty input. pub fn in_memory() -> Self { StreamConfig { - stdin: InputStreamConfig::null(), + stdin: InputStreamConfig::string(""), stdout: OutputStreamConfig::memory(), - stderr: OutputStreamConfig::stderr(), + stderr: OutputStreamConfig::memory(), } } diff --git a/src/machine/lib_machine/mod.rs b/src/machine/lib_machine/mod.rs index 24f22fe78..87b640745 100644 --- a/src/machine/lib_machine/mod.rs +++ b/src/machine/lib_machine/mod.rs @@ -6,11 +6,13 @@ use crate::heap_iter::{stackful_post_order_iter, NonListElider}; use crate::machine::machine_indices::VarKey; use crate::machine::mock_wam::CompositeOpDir; use crate::machine::{ - F64Offset, F64Ptr, Fixnum, Number, BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS, + ArenaHeaderTag, F64Offset, F64Ptr, Fixnum, Number, BREAK_FROM_DISPATCH_LOOP_LOC, + LIB_QUERY_SUCCESS, }; use crate::parser::ast::{Var, VarPtr}; use crate::parser::parser::{Parser, Tokens}; use crate::read::{write_term_to_heap, TermWriteResult}; +use crate::types::UntypedArenaPtr; use dashu::{Integer, Rational}; use indexmap::IndexMap; @@ -280,11 +282,32 @@ impl Term { (HeapCellValueTag::Fixnum, n) => { term_stack.push(Term::Integer(n.into())); } - (HeapCellValueTag::Cons) => { - match Number::try_from(addr) { - Ok(Number::Integer(i)) => term_stack.push(Term::Integer((*i).clone())), - Ok(Number::Rational(r)) => term_stack.push(Term::Rational((*r).clone())), - _ => {} + (HeapCellValueTag::Cons, ptr) => { + if let Ok(n) = Number::try_from(addr) { + match n { + Number::Integer(i) => term_stack.push(Term::Integer((*i).clone())), + Number::Rational(r) => term_stack.push(Term::Rational((*r).clone())), + _ => { unreachable!() }, + } + } else { + match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::Stream, stream) => { + let stream_term = if let Some(alias) = stream.options().get_alias() { + Term::atom(alias.as_str().to_string()) + } else { + Term::compound("$stream", [ + Term::integer(stream.as_ptr() as usize) + ]) + }; + term_stack.push(stream_term); + } + (ArenaHeaderTag::Dropped, _stream) => { + term_stack.push(Term::atom("$dropped_value")); + } + _ => { + unreachable!(); + } + ); } } (HeapCellValueTag::CStr, s) => { @@ -394,6 +417,7 @@ impl Term { } */ _ => { + unreachable!(); } ); } diff --git a/src/machine/lib_machine/tests.rs b/src/machine/lib_machine/tests.rs index 502f4d89b..5b89d67da 100644 --- a/src/machine/lib_machine/tests.rs +++ b/src/machine/lib_machine/tests.rs @@ -1,8 +1,5 @@ -use std::io::Write; -use std::{cell::RefCell, io::Read, rc::Rc}; - use super::*; -use crate::{MachineBuilder, StreamConfig}; +use crate::MachineBuilder; #[test] #[cfg_attr(miri, ignore = "it takes too long to run")] @@ -611,34 +608,3 @@ fn errors_and_exceptions() { [Ok(LeafAnswer::Exception(Term::atom("a")))] ); } - -#[test] -#[cfg_attr(miri, ignore)] -fn callback_streams() { - let test_string = Rc::new(RefCell::new(String::new())); - let test_string2 = test_string.clone(); - - let (mut user_input, streams) = StreamConfig::with_callbacks( - Some(Box::new(move |x| { - x.read_to_string(&mut test_string2.borrow_mut()).unwrap(); - })), - None, - ); - let mut machine = MachineBuilder::default().with_streams(streams).build(); - - write!(&mut user_input, "a(1,2,3).").unwrap(); - - let complete_answer: Vec<_> = machine - .run_query("read(A), write('asdf'), nl, flush_output.") - .collect(); - - assert_eq!( - complete_answer, - [Ok(LeafAnswer::from_bindings([( - "A", - Term::compound("a", [Term::integer(1), Term::integer(2), Term::integer(3)]) - ),]))] - ); - - assert_eq!(*test_string.borrow(), "asdf\n"); -} diff --git a/src/machine/streams.rs b/src/machine/streams.rs index edb6756b8..ea082c917 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -2097,9 +2097,143 @@ impl MachineState { } #[cfg(test)] -mod test { - use super::*; - use crate::machine::config::*; +mod tests { + use crate::*; + use std::{cell::RefCell, io::Read, io::Write, rc::Rc}; + + fn succeeded(answer: Vec>) -> bool { + // Ideally this should be a method in QueryState or LeafAnswer. + matches!( + answer[0].as_ref(), + Ok(LeafAnswer::True) | Ok(LeafAnswer::LeafAnswer { .. }) + ) + } + + #[test] + #[cfg_attr(miri, ignore)] + fn user_input_string_stream() { + let streams = StreamConfig { + stdin: InputStreamConfig::string("a(1,2,3)."), + ..Default::default() + }; + + let mut machine = MachineBuilder::default().with_streams(streams).build(); + + let complete_answer: Vec<_> = machine + .run_query(r#"current_input(_), \+ at_end_of_stream."#) + .collect(); + + assert!(succeeded(complete_answer)); + + let complete_answer: Vec<_> = machine.run_query("read(A).").collect(); + + assert_eq!( + complete_answer, + [Ok(LeafAnswer::from_bindings([( + "A", + Term::compound("a", [Term::integer(1), Term::integer(2), Term::integer(3),]) + )]))] + ); + + let complete_answer: Vec<_> = machine.run_query(r#"at_end_of_stream."#).collect(); + + assert!(succeeded(complete_answer)); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn user_input_channel_stream() { + let (mut user_input, channel_stream) = InputStreamConfig::channel(); + let streams = StreamConfig { + stdin: channel_stream, + ..Default::default() + }; + + let mut machine = MachineBuilder::default().with_streams(streams).build(); + + let complete_answer: Vec<_> = machine + .run_query(r#"current_input(_), \+ at_end_of_stream."#) + .collect(); + + assert!(succeeded(complete_answer)); + + write!(user_input, "a(1,2,3).").unwrap(); + + let complete_answer: Vec<_> = machine + .run_query(r#"\+ at_end_of_stream, read(A)."#) + .collect(); + + assert_eq!( + complete_answer, + [Ok(LeafAnswer::from_bindings([( + "A", + Term::compound("a", [Term::integer(1), Term::integer(2), Term::integer(3),]) + )]))] + ); + + // End-of-data but not end-of-stream; + let complete_answer: Vec<_> = machine + .run_query( + r#" + use_module(library(charsio)), + current_input(In), get_n_chars(In, N, C), + N == 0, \+ at_end_of_stream. + "#, + ) + .collect(); + + assert!(succeeded(complete_answer)); + + // Dropping the sender closes the input + drop(user_input); + + let complete_answer: Vec<_> = machine + .run_query( + r#" + current_input(In), get_n_chars(In, N, _), + N == 0, at_end_of_stream. + "#, + ) + .collect(); + + assert!(succeeded(complete_answer)); + } + + #[test] + #[cfg_attr(miri, ignore)] + fn user_output_callback_stream() { + let test_string = Rc::new(RefCell::new(String::new())); + + let streams = StreamConfig { + stdout: OutputStreamConfig::callback(Box::new({ + let test_string = test_string.clone(); + move |x| { + x.read_to_string(&mut test_string.borrow_mut()).unwrap(); + } + })), + ..Default::default() + }; + + let mut machine = MachineBuilder::default().with_streams(streams).build(); + + let complete_answer: Vec<_> = machine + .run_query(r#"current_output(Out), \+ at_end_of_stream(Out)."#) + .collect(); + + assert!(succeeded(complete_answer)); + + let complete_answer: Vec<_> = machine + .run_query(r#"write(asdf), nl, flush_output."#) + .collect(); + + assert!(succeeded(complete_answer)); + assert_eq!(test_string.borrow().as_str(), "asdf\n"); + + let complete_answer: Vec<_> = machine.run_query(r#"write(abcd), flush_output."#).collect(); + + assert!(succeeded(complete_answer)); + assert_eq!(test_string.borrow().as_str(), "asdf\nabcd"); + } #[test] #[cfg_attr(miri, ignore)] From 3fc5709c505c89de41444d8b692393b197ad3675 Mon Sep 17 00:00:00 2001 From: bakaq Date: Wed, 5 Feb 2025 12:07:28 -0300 Subject: [PATCH 33/67] Add builder style configuration of user input, output and error --- src/machine/config.rs | 55 +++++++++++++++++++++++++++--------------- src/machine/streams.rs | 20 +++++---------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/machine/config.rs b/src/machine/config.rs index 02480602c..b9d3f7039 100644 --- a/src/machine/config.rs +++ b/src/machine/config.rs @@ -132,12 +132,9 @@ impl InputStreamConfig { /// Describes how the streams of a [`Machine`](crate::Machine) will be handled. pub struct StreamConfig { - /// The configuration for the stdin of the [`Machine`](crate::Machine). - pub stdin: InputStreamConfig, - /// The configuration for the stdout of the [`Machine`](crate::Machine). - pub stdout: OutputStreamConfig, - /// The configuration for the stderr of the [`Machine`](crate::Machine). - pub stderr: OutputStreamConfig, + user_input: InputStreamConfig, + user_output: OutputStreamConfig, + user_error: OutputStreamConfig, } impl Default for StreamConfig { @@ -150,43 +147,61 @@ impl StreamConfig { /// Binds the input, output and error streams to stdin, stdout and stderr. pub fn stdio() -> Self { StreamConfig { - stdin: InputStreamConfig::stdin(), - stdout: OutputStreamConfig::stdout(), - stderr: OutputStreamConfig::stderr(), + user_input: InputStreamConfig::stdin(), + user_output: OutputStreamConfig::stdout(), + user_error: OutputStreamConfig::stderr(), } } /// Binds the output and error streams to memory buffers and has an empty input. pub fn in_memory() -> Self { StreamConfig { - stdin: InputStreamConfig::string(""), - stdout: OutputStreamConfig::memory(), - stderr: OutputStreamConfig::memory(), + user_input: InputStreamConfig::string(""), + user_output: OutputStreamConfig::memory(), + user_error: OutputStreamConfig::memory(), } } /// Calls the given callbacks when the respective streams are written to. /// - /// This also returns a handler to the stdin do the [`Machine`](crate::Machine). - pub fn with_callbacks(stdout: Option, stderr: Option) -> (UserInput, Self) { + /// This also returns a handler to the stdin of the [`Machine`](crate::Machine). + pub fn from_callbacks(stdout: Option, stderr: Option) -> (UserInput, Self) { let (user_input, channel_stream) = InputStreamConfig::channel(); ( user_input, StreamConfig { - stdin: channel_stream, - stdout: stdout + user_input: channel_stream, + user_output: stdout .map_or_else(OutputStreamConfig::memory, OutputStreamConfig::callback), - stderr: stderr + user_error: stderr .map_or_else(OutputStreamConfig::memory, OutputStreamConfig::callback), }, ) } + /// Configures the `user_input` stream. + pub fn with_user_input(self, user_input: InputStreamConfig) -> Self { + Self { user_input, ..self } + } + + /// Configures the `user_output` stream. + pub fn with_user_output(self, user_output: OutputStreamConfig) -> Self { + Self { + user_output, + ..self + } + } + + /// Configures the `user_error` stream. + pub fn with_user_error(self, user_error: OutputStreamConfig) -> Self { + Self { user_error, ..self } + } + fn into_streams(self, arena: &mut Arena, add_history: bool) -> (Stream, Stream, Stream) { ( - self.stdin.into_stream(arena, add_history), - self.stdout.into_stream(arena), - self.stderr.into_stream(arena), + self.user_input.into_stream(arena, add_history), + self.user_output.into_stream(arena), + self.user_error.into_stream(arena), ) } } diff --git a/src/machine/streams.rs b/src/machine/streams.rs index ea082c917..500ed6793 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -2112,10 +2112,8 @@ mod tests { #[test] #[cfg_attr(miri, ignore)] fn user_input_string_stream() { - let streams = StreamConfig { - stdin: InputStreamConfig::string("a(1,2,3)."), - ..Default::default() - }; + let streams = + StreamConfig::default().with_user_input(InputStreamConfig::string("a(1,2,3).")); let mut machine = MachineBuilder::default().with_streams(streams).build(); @@ -2144,11 +2142,7 @@ mod tests { #[cfg_attr(miri, ignore)] fn user_input_channel_stream() { let (mut user_input, channel_stream) = InputStreamConfig::channel(); - let streams = StreamConfig { - stdin: channel_stream, - ..Default::default() - }; - + let streams = StreamConfig::default().with_user_input(channel_stream); let mut machine = MachineBuilder::default().with_streams(streams).build(); let complete_answer: Vec<_> = machine @@ -2204,15 +2198,13 @@ mod tests { fn user_output_callback_stream() { let test_string = Rc::new(RefCell::new(String::new())); - let streams = StreamConfig { - stdout: OutputStreamConfig::callback(Box::new({ + let streams = + StreamConfig::default().with_user_output(OutputStreamConfig::callback(Box::new({ let test_string = test_string.clone(); move |x| { x.read_to_string(&mut test_string.borrow_mut()).unwrap(); } - })), - ..Default::default() - }; + }))); let mut machine = MachineBuilder::default().with_streams(streams).build(); From b32273fc790a62168982ec288ca412605f704be2 Mon Sep 17 00:00:00 2001 From: bakaq Date: Fri, 31 Jan 2025 09:24:39 -0300 Subject: [PATCH 34/67] Make Wasm compilable --- Cargo.toml | 11 +++++++++++ flake.nix | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a299e6900..0eb6b7a1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,6 +139,17 @@ opt-level = 3 lto = true opt-level = 3 +[profile.wasm-dev] +inherits = "dev" +opt-level = 1 +lto = "off" + +[profile.wasm-release] +inherits = "release" +lto = "off" +panic = "abort" +codegen-units = 256 + [[bench]] name = "run_criterion" harness = false diff --git a/flake.nix b/flake.nix index 852aa4afb..14e389430 100644 --- a/flake.nix +++ b/flake.nix @@ -20,6 +20,10 @@ rustToolchainDev = super.rust-bin.stable.latest.default.override { extensions = [ "rust-src" "rust-analyzer" ]; }; + rustToolchainDevWasm = super.rust-bin.stable.latest.default.override { + extensions = [ "rust-src" "rust-analyzer" ]; + targets = [ "wasm32-unknown-unknown" ]; + }; rustToolchainNightly = super.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default.override { extensions = [ "rust-src" "rust-analyzer" "miri" ]; @@ -38,12 +42,21 @@ in { devShells = { - default = pkgs.mkShell { + default = pkgs.mkShell.override { stdenv = pkgs.clangMultiStdenv; } { nativeBuildInputs = nativeBuildInputs; buildInputs = buildInputs ++ (with pkgs; [ rustToolchainDev ]); }; + wasm-js = pkgs.mkShell.override { stdenv = pkgs.clangMultiStdenv; } { + nativeBuildInputs = nativeBuildInputs; + buildInputs = buildInputs ++ (with pkgs; [ + wasm-pack + rustToolchainDevWasm + ]); + TARGET_CC = "${pkgs.clangMultiStdenv.cc}/bin/clang"; + hardeningDisable = [ "all" ]; + }; # For use with Miri and stuff like it nightly = pkgs.mkShell { nativeBuildInputs = nativeBuildInputs; From 93e804eeaa880865fcb2e97b959004309c07d054 Mon Sep 17 00:00:00 2001 From: bakaq Date: Fri, 31 Jan 2025 09:24:39 -0300 Subject: [PATCH 35/67] Rework Wasm interface --- Cargo.lock | 58 ++++++- Cargo.toml | 1 + src/bin/scryer-prolog.rs | 6 +- src/lib.rs | 16 +- src/wasm.rs | 320 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 385 insertions(+), 16 deletions(-) create mode 100644 src/wasm.rs diff --git a/Cargo.lock b/Cargo.lock index 623d5cf78..8be9620b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -1129,6 +1135,12 @@ dependencies = [ "http 0.2.12", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -1844,6 +1856,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ouroboros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.72", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -2132,6 +2168,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", + "version_check", + "yansi", +] + [[package]] name = "quick-xml" version = "0.26.0" @@ -2580,6 +2629,7 @@ dependencies = [ "native-tls", "num-order", "ordered-float", + "ouroboros", "phf 0.11.2", "pprof", "predicates-core", @@ -2966,7 +3016,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", @@ -3780,6 +3830,12 @@ dependencies = [ "tap", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 0eb6b7a1f..4d89a0161 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -116,6 +116,7 @@ web-sys = { version = "0.3", features = [ "Performance", ] } js-sys = "0.3" +ouroboros = "0.18" [dev-dependencies] maplit = "1.0.2" diff --git a/src/bin/scryer-prolog.rs b/src/bin/scryer-prolog.rs index 763314ec9..4a9dd2cd7 100644 --- a/src/bin/scryer-prolog.rs +++ b/src/bin/scryer-prolog.rs @@ -1,3 +1,7 @@ fn main() -> std::process::ExitCode { - scryer_prolog::run_binary() + #[cfg(target_arch = "wasm32")] + return std::process::ExitCode::SUCCESS; + + #[cfg(not(target_arch = "wasm32"))] + return scryer_prolog::run_binary(); } diff --git a/src/lib.rs b/src/lib.rs index 85ee726ac..fcb65ff0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,27 +39,15 @@ mod repl_helper; mod targets; pub(crate) mod types; -#[cfg(target_arch = "wasm32")] -use wasm_bindgen::prelude::*; - // Re-exports pub use machine::config::*; pub use machine::lib_machine::*; pub use machine::Machine; -/// Eval a source file in Wasm. #[cfg(target_arch = "wasm32")] -#[wasm_bindgen] -pub fn eval_code(s: &str) -> String { - use machine::mock_wam::*; - - console_error_panic_hook::set_once(); - - let mut wam = MachineBuilder::default().build(); - let bytes = wam.test_load_string(s); - String::from_utf8_lossy(&bytes).to_string() -} +pub mod wasm; +#[cfg(not(target_arch = "wasm32"))] /// The entry point for the Scryer Prolog CLI. pub fn run_binary() -> std::process::ExitCode { use crate::atom_table::Atom; diff --git a/src/wasm.rs b/src/wasm.rs new file mode 100644 index 000000000..d08e18603 --- /dev/null +++ b/src/wasm.rs @@ -0,0 +1,320 @@ +//! Wasm interface + +#![allow(missing_docs)] + +use std::mem; +use std::sync::mpsc; +use std::sync::mpsc::{Receiver, Sender}; + +use ouroboros::self_referencing; +use wasm_bindgen::prelude::*; + +use crate::*; + +#[wasm_bindgen(js_name = MachineBuilder)] +#[derive(Default)] +pub struct WasmMachineBuilder { + inner: MachineBuilder, +} + +#[wasm_bindgen(js_class = MachineBuilder)] +impl WasmMachineBuilder { + pub fn new() -> Self { + Default::default() + } + + pub fn build(&mut self) -> WasmMachine { + WasmMachine { + inner: Ok(std::mem::take(&mut self.inner).build()), + } + } +} + +#[wasm_bindgen(inline_js = " + export function self_iterable(obj) { + obj[Symbol.iterator] = function () { + return this + }; + } +")] +extern "C" { + fn self_iterable(obj: &JsValue); +} + +#[wasm_bindgen(js_name = Machine)] +pub struct WasmMachine { + inner: Result>, +} + +#[wasm_bindgen(js_class = Machine)] +impl WasmMachine { + #[wasm_bindgen(js_name = runQuery)] + pub fn run_query(&mut self, query: String) -> Result { + if self.inner.is_err() { + // We have a receiver, try to get the Machine + let machine = self + .inner + .as_mut() + .unwrap_err() + .try_recv() + .map_err(|_| js_sys::Error::new("Another query is still active"))?; + let _ = mem::replace(&mut self.inner, Ok(machine)); + } + + assert!(self.inner.is_ok()); + + // Installs a receiver and gets the machine + let (sender, receiver) = mpsc::channel(); + let machine = mem::replace(&mut self.inner, Err(receiver)).unwrap(); + + let query_state: JsValue = WasmQueryState { + inner: Some( + WasmQueryStateInnerBuilder { + machine, + drop_channel: sender, + query_state_builder: move |m: &mut Machine| m.run_query(query), + } + .build(), + ), + } + .into(); + + self_iterable(&query_state); + Ok(query_state) + } +} + +#[self_referencing] +struct WasmQueryStateInner { + machine: Machine, + drop_channel: Sender, + #[covariant] + #[borrows(mut machine)] + query_state: QueryState<'this>, +} + +#[wasm_bindgen(js_name = QueryState)] +pub struct WasmQueryState { + inner: Option, +} + +#[wasm_bindgen(js_class = QueryState)] +impl WasmQueryState { + #[wasm_bindgen(js_name = next)] + pub fn next_answer(&mut self) -> Result { + let ret = js_sys::Object::new(); + let mut error = None; + let mut to_drop = false; + match &mut self.inner { + Some(ref mut inner) => { + inner.with_query_state_mut(|query_state| match query_state.next() { + Some(Ok(leaf_answer)) => { + js_sys::Reflect::set(&ret, &"value".into(), &leaf_answer.into()).unwrap(); + js_sys::Reflect::set(&ret, &"done".into(), &false.into()).unwrap(); + } + Some(Err(error_term)) => { + let js_error = js_sys::Error::new("Prolog error"); + js_error.set_cause(&error_term.into()); + error = Some(js_error); + } + None => { + js_sys::Reflect::set(&ret, &"done".into(), &true.into()).unwrap(); + to_drop = true; + } + }) + } + None => return Err(js_sys::Error::new("This query was already dropped").into()), + } + + if let Some(e) = error { + self.drop_inner(); + return Err(JsValue::from(e)); + } + + if to_drop { + self.drop_inner(); + } + + Ok(ret.into()) + } + + #[wasm_bindgen(js_name = drop)] + pub fn drop_inner(&mut self) { + let ouroboros_impl_wasm_query_state_inner::Heads { + machine, + drop_channel, + } = self.inner.take().unwrap().into_heads(); + drop_channel.send(machine).unwrap(); + } +} + +#[wasm_bindgen(inline_js = r#" + export class LeafAnswer { + constructor(bindings) { + this.bindings = bindings; + } + } + + export class PrologInteger { + constructor(integerStr) { + this.type = "integer"; + this.integer = BigInt(integerStr); + } + } + + export class PrologRational { + constructor(numeratorStr, denominatorStr) { + this.type = "rational"; + this.numerator = BigInt(numeratorStr); + this.denominator = BigInt(denominatorStr); + } + } + + export class PrologFloat { + constructor(float) { + this.type = "float"; + this.float = float; + } + } + + export class PrologAtom { + constructor(atom) { + this.type = "atom"; + this.atom = atom; + } + } + + export class PrologString { + constructor(string) { + this.type = "string"; + this.string = string; + } + } + + export class PrologList { + constructor(list) { + this.type = "list"; + this.list = list; + } + } + + export class PrologCompound { + constructor(functor, args) { + this.type = "compound"; + this.functor = functor; + this.args = args; + } + } + + export class PrologVariable { + constructor(variable) { + this.type = "variable"; + this.variable = variable; + } + } + + export class PrologException { + constructor(exception) { + this.exception = exception; + } + } +"#)] +extern "C" { + #[wasm_bindgen(js_name = LeafAnswer)] + pub type JsLeafAnswer; + + #[wasm_bindgen(constructor, js_class = LeafAnswer)] + pub fn new(bindings: js_sys::Object) -> JsLeafAnswer; + + #[wasm_bindgen(js_name = PrologInteger)] + pub type JsPrologInteger; + + #[wasm_bindgen(constructor, js_class = PrologInteger)] + pub fn new(int_str: &str) -> JsPrologInteger; + + #[wasm_bindgen(js_name = PrologRational)] + pub type JsPrologRational; + + #[wasm_bindgen(constructor, js_class = PrologRational)] + pub fn new(numer_str: &str, denom_str: &str) -> JsPrologRational; + + #[wasm_bindgen(js_name = PrologFloat)] + pub type JsPrologFloat; + + #[wasm_bindgen(constructor, js_class = PrologFloat)] + pub fn new(float: f64) -> JsPrologFloat; + + #[wasm_bindgen(js_name = PrologAtom)] + pub type JsPrologAtom; + + #[wasm_bindgen(constructor, js_class = PrologAtom)] + pub fn new(atom: &str) -> JsPrologAtom; + + #[wasm_bindgen(js_name = PrologString)] + pub type JsPrologString; + + #[wasm_bindgen(constructor, js_class = PrologString)] + pub fn new(string: &str) -> JsPrologString; + + #[wasm_bindgen(js_name = PrologList)] + pub type JsPrologList; + + #[wasm_bindgen(constructor, js_class = PrologList)] + pub fn new(list: js_sys::Array) -> JsPrologList; + + #[wasm_bindgen(js_name = PrologCompound)] + pub type JsPrologCompound; + + #[wasm_bindgen(constructor, js_class = PrologCompound)] + pub fn new(functor: &str, args: js_sys::Array) -> JsPrologCompound; + + #[wasm_bindgen(js_name = PrologVariable)] + pub type JsPrologVariable; + + #[wasm_bindgen(constructor, js_class = PrologVariable)] + pub fn new(variable: &str) -> JsPrologVariable; + + #[wasm_bindgen(js_name = PrologException)] + pub type JsPrologException; + + #[wasm_bindgen(constructor, js_class = PrologException)] + pub fn new(exception: JsValue) -> JsPrologException; +} + +impl From for JsValue { + fn from(leaf_answer: LeafAnswer) -> JsValue { + match leaf_answer { + LeafAnswer::True => true.into(), + LeafAnswer::False => false.into(), + LeafAnswer::Exception(e) => JsPrologException::new(e.into()).into(), + LeafAnswer::LeafAnswer { bindings } => { + let bindings_obj = js_sys::Object::new(); + for (var, term) in bindings.into_iter() { + js_sys::Reflect::set(&bindings_obj, &var.into(), &term.into()).unwrap(); + } + JsLeafAnswer::new(bindings_obj).into() + } + } + } +} + +impl From for JsValue { + fn from(term: Term) -> JsValue { + match term { + Term::Integer(i) => JsPrologInteger::new(&i.to_string()).into(), + Term::Rational(r) => { + JsPrologRational::new(&r.numerator().to_string(), &r.denominator().to_string()) + .into() + } + Term::Float(f) => JsPrologFloat::new(f).into(), + Term::Atom(a) => JsPrologAtom::new(&a).into(), + Term::String(s) => JsPrologString::new(&s).into(), + Term::List(l) => JsPrologList::new(l.into_iter().map(JsValue::from).collect()).into(), + Term::Compound(functor, args) => { + JsPrologCompound::new(&functor, args.into_iter().map(JsValue::from).collect()) + .into() + } + Term::Var(v) => JsPrologVariable::new(&v).into(), + } + } +} From cea8e6b1c97699a349073ca580df2c7836441caf Mon Sep 17 00:00:00 2001 From: bakaq Date: Wed, 19 Feb 2025 22:50:41 -0300 Subject: [PATCH 36/67] MachineBuilder constructor --- src/wasm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wasm.rs b/src/wasm.rs index d08e18603..758d7a6ab 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -19,6 +19,7 @@ pub struct WasmMachineBuilder { #[wasm_bindgen(js_class = MachineBuilder)] impl WasmMachineBuilder { + #[wasm_bindgen(constructor)] pub fn new() -> Self { Default::default() } From 236ed93c6cb99b80a085f8dd0248eae9a6194fbd Mon Sep 17 00:00:00 2001 From: bakaq Date: Thu, 20 Feb 2025 00:12:30 -0300 Subject: [PATCH 37/67] Send pure objects instead of classes --- src/wasm.rs | 245 +++++++++++++++++++--------------------------------- 1 file changed, 88 insertions(+), 157 deletions(-) diff --git a/src/wasm.rs b/src/wasm.rs index 758d7a6ab..cdfa6e915 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -31,17 +31,6 @@ impl WasmMachineBuilder { } } -#[wasm_bindgen(inline_js = " - export function self_iterable(obj) { - obj[Symbol.iterator] = function () { - return this - }; - } -")] -extern "C" { - fn self_iterable(obj: &JsValue); -} - #[wasm_bindgen(js_name = Machine)] pub struct WasmMachine { inner: Result>, @@ -80,7 +69,6 @@ impl WasmMachine { } .into(); - self_iterable(&query_state); Ok(query_state) } } @@ -149,151 +137,29 @@ impl WasmQueryState { } } -#[wasm_bindgen(inline_js = r#" - export class LeafAnswer { - constructor(bindings) { - this.bindings = bindings; - } - } - - export class PrologInteger { - constructor(integerStr) { - this.type = "integer"; - this.integer = BigInt(integerStr); - } - } - - export class PrologRational { - constructor(numeratorStr, denominatorStr) { - this.type = "rational"; - this.numerator = BigInt(numeratorStr); - this.denominator = BigInt(denominatorStr); - } - } - - export class PrologFloat { - constructor(float) { - this.type = "float"; - this.float = float; - } - } - - export class PrologAtom { - constructor(atom) { - this.type = "atom"; - this.atom = atom; - } - } - - export class PrologString { - constructor(string) { - this.type = "string"; - this.string = string; - } - } - - export class PrologList { - constructor(list) { - this.type = "list"; - this.list = list; - } - } - - export class PrologCompound { - constructor(functor, args) { - this.type = "compound"; - this.functor = functor; - this.args = args; - } - } - - export class PrologVariable { - constructor(variable) { - this.type = "variable"; - this.variable = variable; - } - } - - export class PrologException { - constructor(exception) { - this.exception = exception; - } - } -"#)] -extern "C" { - #[wasm_bindgen(js_name = LeafAnswer)] - pub type JsLeafAnswer; - - #[wasm_bindgen(constructor, js_class = LeafAnswer)] - pub fn new(bindings: js_sys::Object) -> JsLeafAnswer; - - #[wasm_bindgen(js_name = PrologInteger)] - pub type JsPrologInteger; - - #[wasm_bindgen(constructor, js_class = PrologInteger)] - pub fn new(int_str: &str) -> JsPrologInteger; - - #[wasm_bindgen(js_name = PrologRational)] - pub type JsPrologRational; - - #[wasm_bindgen(constructor, js_class = PrologRational)] - pub fn new(numer_str: &str, denom_str: &str) -> JsPrologRational; - - #[wasm_bindgen(js_name = PrologFloat)] - pub type JsPrologFloat; - - #[wasm_bindgen(constructor, js_class = PrologFloat)] - pub fn new(float: f64) -> JsPrologFloat; - - #[wasm_bindgen(js_name = PrologAtom)] - pub type JsPrologAtom; - - #[wasm_bindgen(constructor, js_class = PrologAtom)] - pub fn new(atom: &str) -> JsPrologAtom; - - #[wasm_bindgen(js_name = PrologString)] - pub type JsPrologString; - - #[wasm_bindgen(constructor, js_class = PrologString)] - pub fn new(string: &str) -> JsPrologString; - - #[wasm_bindgen(js_name = PrologList)] - pub type JsPrologList; - - #[wasm_bindgen(constructor, js_class = PrologList)] - pub fn new(list: js_sys::Array) -> JsPrologList; - - #[wasm_bindgen(js_name = PrologCompound)] - pub type JsPrologCompound; - - #[wasm_bindgen(constructor, js_class = PrologCompound)] - pub fn new(functor: &str, args: js_sys::Array) -> JsPrologCompound; - - #[wasm_bindgen(js_name = PrologVariable)] - pub type JsPrologVariable; - - #[wasm_bindgen(constructor, js_class = PrologVariable)] - pub fn new(variable: &str) -> JsPrologVariable; - - #[wasm_bindgen(js_name = PrologException)] - pub type JsPrologException; - - #[wasm_bindgen(constructor, js_class = PrologException)] - pub fn new(exception: JsValue) -> JsPrologException; -} - impl From for JsValue { fn from(leaf_answer: LeafAnswer) -> JsValue { match leaf_answer { LeafAnswer::True => true.into(), LeafAnswer::False => false.into(), - LeafAnswer::Exception(e) => JsPrologException::new(e.into()).into(), + LeafAnswer::Exception(e) => { + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"type".into(), &"exception".into()).unwrap(); + js_sys::Reflect::set(&obj, &"exception".into(), &e.into()).unwrap(); + obj.into() + } LeafAnswer::LeafAnswer { bindings } => { let bindings_obj = js_sys::Object::new(); for (var, term) in bindings.into_iter() { js_sys::Reflect::set(&bindings_obj, &var.into(), &term.into()).unwrap(); } - JsLeafAnswer::new(bindings_obj).into() + + let leaf_answer_obj = js_sys::Object::new(); + js_sys::Reflect::set(&leaf_answer_obj, &"type".into(), &"leafAnswer".into()) + .unwrap(); + js_sys::Reflect::set(&leaf_answer_obj, &"bindings".into(), &bindings_obj.into()) + .unwrap(); + leaf_answer_obj.into() } } } @@ -302,20 +168,85 @@ impl From for JsValue { impl From for JsValue { fn from(term: Term) -> JsValue { match term { - Term::Integer(i) => JsPrologInteger::new(&i.to_string()).into(), + Term::Integer(i) => { + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"type".into(), &"integer".into()).unwrap(); + js_sys::Reflect::set( + &obj, + &"integer".into(), + &js_sys::BigInt::new(&i.to_string().into()).unwrap().into(), + ) + .unwrap(); + obj.into() + } Term::Rational(r) => { - JsPrologRational::new(&r.numerator().to_string(), &r.denominator().to_string()) - .into() + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"type".into(), &"rational".into()).unwrap(); + js_sys::Reflect::set( + &obj, + &"numerator".into(), + &js_sys::BigInt::new(&r.numerator().to_string().into()) + .unwrap() + .into(), + ) + .unwrap(); + js_sys::Reflect::set( + &obj, + &"denominator".into(), + &js_sys::BigInt::new(&r.denominator().to_string().into()) + .unwrap() + .into(), + ) + .unwrap(); + obj.into() + } + Term::Float(f) => { + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"type".into(), &"float".into()).unwrap(); + js_sys::Reflect::set(&obj, &"float".into(), &f.into()).unwrap(); + obj.into() + } + Term::Atom(a) => { + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"type".into(), &"atom".into()).unwrap(); + js_sys::Reflect::set(&obj, &"atom".into(), &a.into()).unwrap(); + obj.into() + } + Term::String(s) => { + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"type".into(), &"string".into()).unwrap(); + js_sys::Reflect::set(&obj, &"string".into(), &s.into()).unwrap(); + obj.into() + } + Term::List(l) => { + let list = js_sys::Array::new(); + for term in l { + list.push(&term.into()); + } + + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"type".into(), &"list".into()).unwrap(); + js_sys::Reflect::set(&obj, &"list".into(), &list.into()).unwrap(); + obj.into() } - Term::Float(f) => JsPrologFloat::new(f).into(), - Term::Atom(a) => JsPrologAtom::new(&a).into(), - Term::String(s) => JsPrologString::new(&s).into(), - Term::List(l) => JsPrologList::new(l.into_iter().map(JsValue::from).collect()).into(), Term::Compound(functor, args) => { - JsPrologCompound::new(&functor, args.into_iter().map(JsValue::from).collect()) - .into() + let args_list = js_sys::Array::new(); + for term in args { + args_list.push(&term.into()); + } + + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"type".into(), &"compound".into()).unwrap(); + js_sys::Reflect::set(&obj, &"functor".into(), &functor.into()).unwrap(); + js_sys::Reflect::set(&obj, &"args".into(), &args_list.into()).unwrap(); + obj.into() + } + Term::Var(v) => { + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"type".into(), &"variable".into()).unwrap(); + js_sys::Reflect::set(&obj, &"variable".into(), &v.into()).unwrap(); + obj.into() } - Term::Var(v) => JsPrologVariable::new(&v).into(), } } } From 3b89c1124d454dba73623f15517058d2e30de6dd Mon Sep 17 00:00:00 2001 From: bakaq Date: Thu, 20 Feb 2025 00:33:11 -0300 Subject: [PATCH 38/67] Consult modules --- src/wasm.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/wasm.rs b/src/wasm.rs index cdfa6e915..08383318a 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -38,8 +38,7 @@ pub struct WasmMachine { #[wasm_bindgen(js_class = Machine)] impl WasmMachine { - #[wasm_bindgen(js_name = runQuery)] - pub fn run_query(&mut self, query: String) -> Result { + fn ensure_machine_ownership(&mut self) -> Result<(), JsValue> { if self.inner.is_err() { // We have a receiver, try to get the Machine let machine = self @@ -50,7 +49,12 @@ impl WasmMachine { .map_err(|_| js_sys::Error::new("Another query is still active"))?; let _ = mem::replace(&mut self.inner, Ok(machine)); } + Ok(()) + } + #[wasm_bindgen(js_name = runQuery)] + pub fn run_query(&mut self, query: String) -> Result { + self.ensure_machine_ownership()?; assert!(self.inner.is_ok()); // Installs a receiver and gets the machine @@ -71,6 +75,21 @@ impl WasmMachine { Ok(query_state) } + + #[wasm_bindgen(js_name = consultModuleString)] + pub fn consult_module_string( + &mut self, + module: String, + program: String, + ) -> Result<(), JsValue> { + self.ensure_machine_ownership()?; + assert!(self.inner.is_ok()); + + let inner = self.inner.as_mut().unwrap(); + inner.consult_module_string(&module, program); + + Ok(()) + } } #[self_referencing] From 080a4fed038478980e05f87b8944896a40ed67a2 Mon Sep 17 00:00:00 2001 From: bakaq Date: Thu, 20 Feb 2025 00:48:51 -0300 Subject: [PATCH 39/67] Documentation --- src/wasm.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/wasm.rs b/src/wasm.rs index 08383318a..10430677e 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -1,7 +1,5 @@ //! Wasm interface -#![allow(missing_docs)] - use std::mem; use std::sync::mpsc; use std::sync::mpsc::{Receiver, Sender}; @@ -11,6 +9,7 @@ use wasm_bindgen::prelude::*; use crate::*; +/// A builder for a `Machine`. #[wasm_bindgen(js_name = MachineBuilder)] #[derive(Default)] pub struct WasmMachineBuilder { @@ -19,11 +18,13 @@ pub struct WasmMachineBuilder { #[wasm_bindgen(js_class = MachineBuilder)] impl WasmMachineBuilder { + /// Creates a new `MachineBuilder` with the default configuration. #[wasm_bindgen(constructor)] pub fn new() -> Self { Default::default() } + /// Creates a new `Machine`. pub fn build(&mut self) -> WasmMachine { WasmMachine { inner: Ok(std::mem::take(&mut self.inner).build()), @@ -31,6 +32,7 @@ impl WasmMachineBuilder { } } +/// The Scryer Prolog `Machine`. #[wasm_bindgen(js_name = Machine)] pub struct WasmMachine { inner: Result>, @@ -52,6 +54,10 @@ impl WasmMachine { Ok(()) } + /// Runs a query. + /// + /// You can only have one query at a time. If you try to do anything with this machine while + /// doing a query an error will be thrown. #[wasm_bindgen(js_name = runQuery)] pub fn run_query(&mut self, query: String) -> Result { self.ensure_machine_ownership()?; @@ -76,6 +82,7 @@ impl WasmMachine { Ok(query_state) } + /// Consults a module. #[wasm_bindgen(js_name = consultModuleString)] pub fn consult_module_string( &mut self, @@ -101,6 +108,7 @@ struct WasmQueryStateInner { query_state: QueryState<'this>, } +/// The state of a running query. #[wasm_bindgen(js_name = QueryState)] pub struct WasmQueryState { inner: Option, @@ -108,6 +116,12 @@ pub struct WasmQueryState { #[wasm_bindgen(js_class = QueryState)] impl WasmQueryState { + /// Gets the next leaf answer. + /// + /// This follows the Javascript iterator protocol, so it returns an object that + /// contains a `done` field and a `value` field. If `done` is `false`, then the query ended + /// and control of the `Machine` will be given back to the `Machine` that created this query. + /// Any call after that will result in an error. #[wasm_bindgen(js_name = next)] pub fn next_answer(&mut self) -> Result { let ret = js_sys::Object::new(); @@ -146,6 +160,10 @@ impl WasmQueryState { Ok(ret.into()) } + /// Drops the query. + /// + /// This is useful to end a query early. Like finishing a query, control will be given back + /// to the `Machine` and any call to `next` after that will result in an error. #[wasm_bindgen(js_name = drop)] pub fn drop_inner(&mut self) { let ouroboros_impl_wasm_query_state_inner::Heads { From 5d468d3e1989a11e12d6aa00a50ab7df4ba38fb7 Mon Sep 17 00:00:00 2001 From: revue_2_presse Date: Tue, 25 Feb 2025 20:19:14 +0100 Subject: [PATCH 40/67] Replace futures::executor::block_on with tokio::block_in_place --- src/machine/system_calls.rs | 111 +++++++++++++++------------- tests-pl/issue-http_open-hanging.pl | 23 ++++++ tests/scryer/helper.rs | 16 ++++ tests/scryer/issues.rs | 13 ++++ 4 files changed, 111 insertions(+), 52 deletions(-) create mode 100644 tests-pl/issue-http_open-hanging.pl diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 71e901822..abc8f0b0f 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -91,6 +91,8 @@ use roxmltree; use futures::future; #[cfg(feature = "http")] use reqwest::Url; +use tokio::runtime::Handle; +use tokio::task; #[cfg(feature = "http")] use warp::hyper::header::{HeaderName, HeaderValue}; #[cfg(feature = "http")] @@ -4378,65 +4380,70 @@ impl Machine { } // do it! - match futures::executor::block_on(req.send()) { - Ok(resp) => { - // status code - let status = resp.status().as_u16(); - self.machine_st - .unify_fixnum(Fixnum::build_with(status as i64), address_status); - // headers - let headers: Vec = resp - .headers() - .iter() - .map(|(header_name, header_value)| { - let h = self.machine_st.heap.len(); - - let header_term = functor!( - AtomTable::build_with( - &self.machine_st.atom_tbl, - header_name.as_str() - ), - [cell(string_as_cstr_cell!(AtomTable::build_with( - &self.machine_st.atom_tbl, - header_value.to_str().unwrap() - )))] - ); + task::block_in_place(move || { + match Handle::current().block_on(req.send()) { + Ok(resp) => { + // status code + let status = resp.status().as_u16(); + self.machine_st + .unify_fixnum(Fixnum::build_with(status as i64), address_status); + // headers + let headers: Vec = resp + .headers() + .iter() + .map(|(header_name, header_value)| { + let h = self.machine_st.heap.len(); + + let header_term = functor!( + AtomTable::build_with( + &self.machine_st.atom_tbl, + header_name.as_str() + ), + [cell(string_as_cstr_cell!(AtomTable::build_with( + &self.machine_st.atom_tbl, + header_value.to_str().unwrap() + )))] + ); - self.machine_st.heap.extend(header_term); - str_loc_as_cell!(h) - }) - .collect(); + self.machine_st.heap.extend(header_term); + str_loc_as_cell!(h) + }) + .collect(); - let headers_list = - iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter()); - unify!( - self.machine_st, - heap_loc_as_cell!(headers_list), - self.machine_st.registers[6] - ); - // body - let reader = futures::executor::block_on(resp.bytes()).unwrap().reader(); + let headers_list = + iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter()); - let mut stream = Stream::from_http_stream( - AtomTable::build_with(&self.machine_st.atom_tbl, &address_string), - reader, - &mut self.machine_st.arena, - ); - *stream.options_mut() = StreamOptions::default(); + unify!( + self.machine_st, + heap_loc_as_cell!(headers_list), + self.machine_st.registers[6] + ); - self.indices - .add_stream(stream, atom!("http_open"), 3) - .map_err(|stub_gen| stub_gen(&mut self.machine_st))?; + // body + let reader = futures::executor::block_on(resp.bytes()).unwrap().reader(); - let stream = stream_as_cell!(stream); + let mut stream = Stream::from_http_stream( + AtomTable::build_with(&self.machine_st.atom_tbl, &address_string), + reader, + &mut self.machine_st.arena, + ); + *stream.options_mut() = StreamOptions::default(); - let stream_addr = self.deref_register(2); - self.machine_st.bind(stream_addr.as_var().unwrap(), stream); - } - Err(_) => { - self.machine_st.fail = true; + self.indices + .add_stream(stream, atom!("http_open"), 3) + .map_err(|stub_gen| stub_gen(&mut self.machine_st)) + .unwrap(); + + let stream = stream_as_cell!(stream); + + let stream_addr = self.deref_register(2); + self.machine_st.bind(stream_addr.as_var().unwrap(), stream); + } + Err(_) => { + self.machine_st.fail = true; + } } - } + }); } else { let err = self .machine_st diff --git a/tests-pl/issue-http_open-hanging.pl b/tests-pl/issue-http_open-hanging.pl new file mode 100644 index 000000000..b26b5d06f --- /dev/null +++ b/tests-pl/issue-http_open-hanging.pl @@ -0,0 +1,23 @@ +:- module(http_open_hanging, [submit_request/0]). + +:- use_module(library(charsio)). +:- use_module(library(http/http_open)). + +send_request :- + Options = [ + method('get'), + status_code(StatusCode), + request_headers([]), + headers(_) + ], + http_open("https://scryer.pl", _Stream, Options), + write_term('received response with status code':StatusCode, []), nl. + +main :- + send_request, + send_request, + send_request, + send_request, + send_request. + +:- initialization(main). \ No newline at end of file diff --git a/tests/scryer/helper.rs b/tests/scryer/helper.rs index bb6402a1a..b7c2dd776 100644 --- a/tests/scryer/helper.rs +++ b/tests/scryer/helper.rs @@ -1,3 +1,5 @@ +use scryer_prolog::MachineBuilder; + pub(crate) trait Expectable { #[track_caller] fn assert_eq(self, other: &[u8]); @@ -31,3 +33,17 @@ pub(crate) fn load_module_test(file: &str, expected: T) { let mut wam = MachineBuilder::default().build(); expected.assert_eq(wam.test_load_file(file).as_slice()); } + +/// Same as `load_module_test` with tokio runtime +#[cfg(not(target_arch = "wasm32"))] +pub(crate) fn load_module_test_with_tokio_runtime(file: &str, expected: T) { + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + + runtime.block_on(async move { + let mut wam = MachineBuilder::default().build(); + expected.assert_eq(wam.test_load_file(file).as_slice()) + }); +} diff --git a/tests/scryer/issues.rs b/tests/scryer/issues.rs index 784c20d0d..974183a4f 100644 --- a/tests/scryer/issues.rs +++ b/tests/scryer/issues.rs @@ -1,4 +1,6 @@ use crate::helper::load_module_test; +#[cfg(not(target_arch = "wasm32"))] +use crate::helper::load_module_test_with_tokio_runtime; use serial_test::serial; // issue #831 @@ -43,3 +45,14 @@ fn load_context_unreachable() { fn issue2725_dcg_without_module() { load_module_test("tests-pl/issue2725.pl", ""); } + +#[test] +#[cfg(feature = "http")] +#[cfg(not(target_arch = "wasm32"))] +#[cfg_attr(miri, ignore = "it takes too long to run")] +fn http_open_hanging() { + load_module_test_with_tokio_runtime( + "tests-pl/issue-http_open-hanging.pl", + "received response with status code:200\nreceived response with status code:200\nreceived response with status code:200\nreceived response with status code:200\nreceived response with status code:200\n" + ); +} From 4f3843742c2f500e768497cad74c0b4a5d627f3d Mon Sep 17 00:00:00 2001 From: notoria Date: Fri, 28 Feb 2025 08:00:00 +0100 Subject: [PATCH 41/67] Fix conversion from float to rational --- src/machine/arithmetic_ops.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index eab7e7080..43ea34c24 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -534,7 +534,7 @@ pub fn rational_from_number( match n { Number::Fixnum(n) => Ok(arena_alloc!(Rational::from(n.get_num()), arena)), Number::Rational(r) => Ok(r), - Number::Float(OrderedFloat(f)) => match Rational::simplest_from_f64(f) { + Number::Float(OrderedFloat(f)) => match Rational::try_from(f).ok() { Some(r) => Ok(arena_alloc!(r, arena)), None => Err(Box::new(move |machine_st| { let instantiation_error = machine_st.instantiation_error(); From 2d4fcf9714ac0b754446761a4c60278776c36657 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 28 Feb 2025 08:14:34 +0100 Subject: [PATCH 42/67] correct handling of floats with N digits (~f and ~Nf) This addresses #2771. Many thanks to @tmerr for reporting the issue! The code was posted by @UWN in: https://github.com/mthom/scryer-prolog/discussions/2805 Many thanks! With additional inputs by @adri326 and @notoria, who also posted interesting approaches which could help to increase precision in cases that are currently not ideally handled. Thank you all! Please see the issue and discussion for more information. --- src/lib/format.pl | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/src/lib/format.pl b/src/lib/format.pl index 96266c5df..63104519e 100644 --- a/src/lib/format.pl +++ b/src/lib/format.pl @@ -285,36 +285,12 @@ cells(Fs, Args, 0, [], VNs). cells([~,s|Fs], [Arg|Args], Tab, Es, VNs) --> !, cells(Fs, Args, Tab, [chars(Arg)|Es], VNs). -cells([~,f|Fs], [Arg|Args], Tab, Es, VNs) --> !, - { G = format_number_chars(Arg, Chars) }, - cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs). +cells([~,f|Fs], Args, Tab, Es, VNs) --> !, + cells([~,'6',f|Fs], Args, Tab, Es, VNs). cells([~|Fs0], Args0, Tab, Es, VNs) --> { numeric_argument(Fs0, Num, [f|Fs], Args0, [Arg|Args]) }, !, - { G = (format_number_chars(Arg, Cs0), - phrase(upto_what(Bs, .), Cs0, Cs), - ( Num =:= 0 -> Chars = Bs - ; ( Cs = ['.'|Rest] -> - length(Rest, L), - ( Num < L -> - length(Ds, Num), - append(Ds, _, Rest) - ; Num =:= L -> - Ds = Rest - ; Num > L, - Delta is Num - L, - % we should look into the float with - % greater accuracy here, and use the - % actual digits instead of 0. - length(Zs, Delta), - maplist(=('0'), Zs), - append(Rest, Zs, Ds) - ) - ; length(Ds, Num), - maplist(=('0'), Ds) - ), - append(Bs, ['.'|Ds], Chars) - )) }, + { G = phrase(float_with_n_decimal_digits(Arg, Num), Chars) }, cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs). cells([~,r|Fs], Args, Tab, Es, VNs) --> !, cells([~,'8',r|Fs], Args, Tab, Es, VNs). @@ -369,9 +345,18 @@ Fs1 = [_|_] }, cells(Fs, Args, Tab, [chars(Fs1)|Es], VNs). -format_number_chars(N0, Chars) :- - N is N0, % evaluate compound expression - number_chars(N, Chars). +float_with_n_decimal_digits(F, N) --> + { Fr is abs(float_fractional_part(F)), + FrR0 is round(Fr*10^N), + I0 is truncate(F), + ( FrR0 >= 10^N + -> I is I0+truncate(sign(F)), FrR = FrR0 + ; I = I0, FrR is FrR0+10^N + ) + }, + ( { I=0, F<0, FrR>10^N } -> "-0" ; { number_chars(I, Is) }, seq(Is) ), + ".", + ( { FrR = 1 } -> "0" ; { number_chars(FrR, ['1'|FrRs]) }, seq(FrRs) ). n_newlines(N0) --> { N0 > 0, N is N0 - 1 }, [newline], n_newlines(N). n_newlines(0) --> []. From b8526e05108d7b127cb06115bcc45848695c559d Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 28 Feb 2025 08:22:54 +0100 Subject: [PATCH 43/67] FIXED: portray_clause/1 for numbers --- src/lib/format.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/format.pl b/src/lib/format.pl index 63104519e..62a9e93e7 100644 --- a/src/lib/format.pl +++ b/src/lib/format.pl @@ -580,6 +580,7 @@ literal(Lit, VNs) --> { write_term_to_chars(Lit, [quoted(true),variable_names(VNs),double_quotes(true)], Ls) }, ( { nonvar(Lit), + \+ number(Lit), functor(Lit, F, A), current_op(Pri, _, F), ( A =:= 0 From 32d91449b57d0c266e3c68092288ce3279fadc3f Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 8 Mar 2025 09:12:17 +0100 Subject: [PATCH 44/67] ADDED: call_with_error_context/2 See https://github.com/mthom/scryer-prolog/discussions/2839 for more information. --- src/lib/error.pl | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/lib/error.pl b/src/lib/error.pl index fc3b9712d..81c676df0 100644 --- a/src/lib/error.pl +++ b/src/lib/error.pl @@ -1,5 +1,5 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2018-2023 by Markus Triska (triska@metalevel.at) + Written 2018-2025 by Markus Triska (triska@metalevel.at) I place this code in the public domain. Use it in any way you want. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -7,7 +7,8 @@ can_be/2, instantiation_error/1, domain_error/3, - type_error/3 + type_error/3, + call_with_error_context/2 ]). @@ -217,3 +218,20 @@ type_error(Type, Term, Context) :- throw(error(type_error(Type, Term), Context)). + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + call_with_error_context/2 + + See https://github.com/mthom/scryer-prolog/discussions/2839 . +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +%% call_with_error_context(+Goal, +Pair) +% +% Call _Goal_ with error context _Pair_. +% +% Examples of error contexts: `predicate-PI`, `file-Filename` etc. + +:- meta_predicate(call_with_error_context(0,+)). +call_with_error_context(G_0, Pair) :- + must_be(pair, Pair), + catch(G_0, error(E,Pairs), throw(error(E,[Pair|Pairs]))). From 57bf1ec39b9dc121594e54fb048f6be405aecfae Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 8 Mar 2025 14:42:48 +0100 Subject: [PATCH 45/67] ADDED: type pair --- src/lib/error.pl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib/error.pl b/src/lib/error.pl index 81c676df0..67df8a32f 100644 --- a/src/lib/error.pl +++ b/src/lib/error.pl @@ -37,6 +37,7 @@ % - list % - octet_character % - octet_chars +% - pair % - term must_be(Type, Term) :- @@ -83,6 +84,7 @@ must_be_(list, Term) :- check_(error:ilist, list, Term). must_be_(type, Term) :- check_(error:type, type, Term). must_be_(boolean, Term) :- check_(error:boolean, boolean, Term). +must_be_(pair, Term) :- check_(error:pair, pair, Term). must_be_(term, Term) :- ( acyclic_term(Term) -> ( ground(Term) -> true @@ -105,6 +107,9 @@ ; type_error(Type, Term, must_be/2) ). + +pair(_-_). + boolean(B) :- ( B == true ; B == false ). character(C) :- @@ -140,6 +145,7 @@ type(boolean). type(term). type(not_less_than_zero). +type(pair). %% can_be(Type, Term) % @@ -188,6 +194,7 @@ ). can_(list, Term) :- list_or_partial_list(Term). can_(boolean, Term) :- boolean(Term). +can_(pair, Term) :- pair(Term). can_(term, Term) :- ( acyclic_term(Term) -> true From a45a1f61976cdd22e8aa2126746b2c00e1ac3f1f Mon Sep 17 00:00:00 2001 From: Aman Verma Date: Thu, 27 Mar 2025 18:49:03 -0500 Subject: [PATCH 46/67] Use backticks when referencing predicate in docs. Trivial change but this prevents the HTML output from being erroneously italicized. --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 6834a3796..b14546bc1 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -458,7 +458,7 @@ more general programs. See [`n_factorial/2`](#clpz-factorial) for an example. -This library uses goal_expansion/2 to automatically rewrite +This library uses `goal_expansion/2` to automatically rewrite constraints at compilation time so that low-level arithmetic predicates are _automatically_ used whenever possible. For example, the predicate: From 165d47c7b5b32776b0eee01aa51a07d0b3864c6f Mon Sep 17 00:00:00 2001 From: Aman Verma Date: Thu, 27 Mar 2025 20:20:46 -0500 Subject: [PATCH 47/67] Bump dependencies except for arcu v0.1.1 and half 2.4.1. arcu is not updated for reasons laid out in issue #2749. half is not updated because going to 2.5.0 would require an MSRV bump 1.81. Previous versions of idna, openssl, and ring were subject to the following security advisories: - https://rustsec.org/advisories/RUSTSEC-2025-0009 - https://rustsec.org/advisories/RUSTSEC-2025-0004 - https://rustsec.org/advisories/RUSTSEC-2024-0421 --- Cargo.lock | 1264 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 798 insertions(+), 466 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8be9620b9..e36827fb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "ahash" @@ -24,10 +24,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -68,9 +68,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -83,36 +83,37 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] @@ -123,19 +124,20 @@ checksum = "f8727c0fb4c436605c8f11c579ec86edcb729134aec4ee66e454efd99a91859f" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "assert_cmd" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc65048dd435533bb1baf2ed9956b9a278fbfdcf90301b39ee117f06c0199d37" +checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" dependencies = [ "anstyle", "bstr", "doc-comment", + "libc", "predicates", "predicates-core", "predicates-tree", @@ -144,34 +146,34 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "automod" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edf3ee19dbc0a46d740f6f0926bde8c50f02bdbc7b536842da28f6ac56513a8b" +checksum = "ebb4bd301db2e2ca1f5be131c24eb8ebf2d9559bc3744419e93baf8ddea7e670" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -218,9 +220,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bitvec" @@ -254,9 +256,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "regex-automata", @@ -265,15 +267,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.16.3" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" [[package]] name = "byteorder" @@ -283,9 +285,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cast" @@ -295,9 +297,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.7" +version = "1.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -311,18 +316,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -354,18 +365,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.13" +version = "4.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.13" +version = "4.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" dependencies = [ "anstyle", "clap_lex", @@ -373,9 +384,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clipboard-win" @@ -388,9 +399,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "console_error_panic_hook" @@ -429,15 +440,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpp_demangle" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" dependencies = [ "cfg-if", ] @@ -454,9 +465,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -499,9 +510,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -518,9 +529,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" @@ -528,11 +539,11 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "crossterm_winapi", "mio", "parking_lot", - "rustix", + "rustix 0.38.44", "signal-hook", "signal-hook-mio", "winapi", @@ -560,9 +571,9 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-common" @@ -583,7 +594,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.11.2", + "phf 0.11.3", "smallvec", ] @@ -594,17 +605,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] name = "ctrlc" -version = "3.4.4" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" dependencies = [ - "nix 0.28.0", - "windows-sys 0.52.0", + "nix 0.29.0", + "windows-sys 0.59.0", ] [[package]] @@ -690,9 +701,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" [[package]] name = "debugid" @@ -705,15 +716,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] @@ -754,6 +765,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "divrem" version = "1.0.0" @@ -768,9 +790,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dtoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" +checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" [[package]] name = "dtoa-short" @@ -783,27 +805,27 @@ dependencies = [ [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "ego-tree" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" +checksum = "12a0bb14ac04a9fcf170d0bbbef949b44cc492f4452bd20c095636956f653642" [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -816,53 +838,53 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "error-code" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" [[package]] name = "fastrand" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fd-lock" -version = "4.0.2" +version = "4.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix", - "windows-sys 0.52.0", + "rustix 1.0.3", + "windows-sys 0.59.0", ] [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] @@ -925,9 +947,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -940,9 +962,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -950,15 +972,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -967,38 +989,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1040,15 +1062,27 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "git-version" @@ -1067,14 +1101,14 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "h2" @@ -1107,9 +1141,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "headers" @@ -1149,17 +1183,17 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1184,7 +1218,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] @@ -1200,9 +1234,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -1222,9 +1256,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1234,9 +1268,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" [[package]] name = "humantime-serde" @@ -1250,9 +1284,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -1287,9 +1321,9 @@ dependencies = [ [[package]] name = "iai-callgrind" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cea0f8111720335c28761833ed6db5cf0878014b667064260999c7f97993569" +checksum = "283f598a969822c70af13aae1272ba09c97014c7344d3b24652e5b1d7b771c36" dependencies = [ "bincode", "iai-callgrind-macros", @@ -1298,35 +1332,38 @@ dependencies = [ [[package]] name = "iai-callgrind-macros" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065a2a6ff103151860ce6f416eac6667e87dd7d866e7f5ebfd10f70c0e98c090" +checksum = "04e2ff2e86bdba764b66d94b65f2caa03da60d971a6930fdc2e67f12582c5bb8" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.72", + "serde", + "serde_json", + "syn 2.0.100", ] [[package]] name = "iai-callgrind-runner" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5cf673059de32d68347df93d045bf2ab215dd10b4befd7c01b3d585b2d3693d" +checksum = "fb92a65def0d3a0ef41029c411dc2ecdd3518708c062f8bd576fd4143be1c56b" dependencies = [ "serde", ] [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "b2fd658b06e56721792c5df4475705b6cda790e9298d19d2f8af083457bcd127" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -1340,21 +1377,150 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] name = "indexmap" -version = "2.3.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", @@ -1380,19 +1546,19 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1412,16 +1578,17 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1515,9 +1682,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libffi" @@ -1540,9 +1707,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -1554,15 +1721,28 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "libc", + "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "lock_api" @@ -1576,9 +1756,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "mac" @@ -1599,8 +1779,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" dependencies = [ "log", - "phf 0.11.2", - "phf_codegen 0.11.2", + "phf 0.11.3", + "phf_codegen 0.11.3", "string_cache", "string_cache_codegen", "tendril", @@ -1614,9 +1794,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] @@ -1639,23 +1819,22 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi", "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1679,9 +1858,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -1726,9 +1905,21 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", + "cfg-if", + "cfg_aliases 0.1.1", + "libc", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.9.0", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.2.1", "libc", ] @@ -1774,32 +1965,32 @@ dependencies = [ [[package]] name = "object" -version = "0.36.2" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "c2806eaa3524762875e21c3dcd057bc4b7bfa01ce4da8d46be1cd43649e1cc6b" [[package]] name = "oorandom" -version = "11.1.4" +version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", "foreign-types", "libc", @@ -1816,20 +2007,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -1839,9 +2030,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "4.2.2" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a91171844676f8c7990ce64959210cd2eaef32c2612c50f9fae9f8aaa6065a6" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" dependencies = [ "num-traits", ] @@ -1877,7 +2068,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] @@ -1898,7 +2089,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.3", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -1926,12 +2117,12 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", - "phf_shared 0.11.2", + "phf_shared 0.11.3", ] [[package]] @@ -1946,12 +2137,12 @@ dependencies = [ [[package]] name = "phf_codegen" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -1966,25 +2157,25 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", "rand", ] [[package]] name = "phf_macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] @@ -1993,43 +2184,43 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -2039,15 +2230,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -2058,15 +2249,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] @@ -2095,11 +2286,11 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.24", ] [[package]] @@ -2110,9 +2301,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "3.1.2" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "difflib", @@ -2121,15 +2312,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", @@ -2161,9 +2352,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -2176,7 +2367,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", "version_check", "yansi", ] @@ -2192,13 +2383,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.7.0" @@ -2242,7 +2439,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -2267,38 +2464,29 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" -dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -2308,9 +2496,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2319,9 +2507,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -2365,24 +2553,23 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.45" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade4539f42266ded9e755c605bdddf546242b2c961b03b06a7375260788a0523" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" dependencies = [ "bytemuck", ] [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -2410,24 +2597,37 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.9.3", + "windows-sys 0.59.0", ] [[package]] @@ -2455,25 +2655,24 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -2482,9 +2681,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "rustyline" @@ -2492,7 +2691,7 @@ version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", "clipboard-win", "fd-lock", @@ -2510,9 +2709,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -2525,20 +2724,20 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.7" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a870e34715d5d59c8536040d4d4e7a41af44d527dc50237036ba4090db7996fc" +checksum = "ea091f6cac2595aa38993f04f4ee692ed43757035c36e67c180b6828356385b1" dependencies = [ "sdd", ] [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2614,7 +2813,7 @@ dependencies = [ "ego-tree", "futures", "fxhash", - "getrandom", + "getrandom 0.2.15", "git-version", "hostname", "iai-callgrind", @@ -2630,7 +2829,7 @@ dependencies = [ "num-order", "ordered-float", "ouroboros", - "phf 0.11.2", + "phf 0.11.3", "pprof", "predicates-core", "proc-macro2", @@ -2654,7 +2853,7 @@ dependencies = [ "static_assertions", "strum", "strum_macros", - "syn 2.0.72", + "syn 2.0.100", "to-syn-value", "to-syn-value_derive", "tokio", @@ -2668,9 +2867,9 @@ dependencies = [ [[package]] name = "sdd" -version = "2.1.0" +version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177258b64c0faaa9ffd3c65cd3262c2bc7e2588dbbd9c1641d0346145c1bbda8" +checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" [[package]] name = "security-framework" @@ -2678,7 +2877,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "core-foundation", "core-foundation-sys", "libc", @@ -2687,9 +2886,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -2701,7 +2900,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cssparser", "derive_more", "fxhash", @@ -2716,15 +2915,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -2742,20 +2941,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] name = "serde_json" -version = "1.0.122" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -2765,9 +2964,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -2786,9 +2985,9 @@ dependencies = [ [[package]] name = "serial_test" -version = "3.1.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d" +checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9" dependencies = [ "futures", "log", @@ -2800,13 +2999,13 @@ dependencies = [ [[package]] name = "serial_test_derive" -version = "3.1.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" +checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] @@ -2888,9 +3087,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "siphasher" @@ -2898,6 +3097,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -2909,15 +3114,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "snapbox" -version = "0.6.16" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027c936207f85d10d015e21faf5c676c7e08c453ed371adf55c0874c443ca77a" +checksum = "96dcfc4581e3355d70ac2ee14cfdf81dce3d85c85f1ed9e2c1d3013f53b3436b" dependencies = [ "anstream", "anstyle", @@ -2932,7 +3137,7 @@ dependencies = [ "tempfile", "wait-timeout", "walkdir", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2946,9 +3151,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2980,26 +3185,25 @@ checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" [[package]] name = "string_cache" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe" dependencies = [ "new_debug_unreachable", - "once_cell", "parking_lot", - "phf_shared 0.10.0", + "phf_shared 0.11.3", "precomputed-hash", "serde", ] [[package]] name = "string_cache_codegen" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", ] @@ -3020,7 +3224,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] @@ -3031,9 +3235,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "symbolic-common" -version = "12.10.0" +version = "12.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16629323a4ec5268ad23a575110a724ad4544aae623451de600c747bf87b36cf" +checksum = "66135c8273581acaab470356f808a1c74a707fe7ec24728af019d7247e089e71" dependencies = [ "debugid", "memmap2", @@ -3043,9 +3247,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.10.0" +version = "12.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c043a45f08f41187414592b3ceb53fb0687da57209cc77401767fb69d5b596" +checksum = "42bcacd080282a72e795864660b148392af7babd75691d5ae9a3b77e29c98c77" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -3065,9 +3269,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -3080,6 +3284,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -3109,15 +3324,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.11.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", + "getrandom 0.3.2", "once_cell", - "rustix", - "windows-sys 0.52.0", + "rustix 1.0.3", + "windows-sys 0.59.0", ] [[package]] @@ -3133,62 +3348,57 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] -name = "tinytemplate" -version = "1.2.1" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "serde", - "serde_json", + "displaydoc", + "zerovec", ] [[package]] -name = "tinyvec" -version = "1.8.0" +name = "tinytemplate" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ - "tinyvec_macros", + "serde", + "serde_json", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "to-syn-value" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfcc684f2ceaec3b4e8689657c9e0944b07bf5e34563e0bd758c4d42c05c82ed" dependencies = [ - "syn 2.0.72", + "syn 2.0.100", "to-syn-value_derive", ] @@ -3200,14 +3410,14 @@ checksum = "3dfffda778de8443144ff3b042ddf14e8bc5445f0fd9fe937c3d252535dc9212" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] name = "tokio" -version = "1.39.2" +version = "1.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" dependencies = [ "backtrace", "bytes", @@ -3223,13 +3433,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", ] [[package]] @@ -3267,9 +3477,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ "bytes", "futures-core", @@ -3289,9 +3499,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "serde", @@ -3302,15 +3512,15 @@ dependencies = [ [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -3319,9 +3529,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -3334,10 +3544,11 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trycmd" -version = "0.15.6" +version = "0.15.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e8673f1dc45acdff8e25a06cc62f8e529563e8acd84237ce83d5a28e2befa12" +checksum = "a8b5cf29388862aac065d6597ac9c8e842d1cc827cb50f7c32f11d29442eaae4" dependencies = [ + "anstream", "automod", "glob", "humantime", @@ -3358,7 +3569,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http 1.3.1", "httparse", "log", "rand", @@ -3370,51 +3581,33 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "untrusted" @@ -3424,9 +3617,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -3439,6 +3632,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -3447,9 +3652,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" [[package]] name = "vcpkg" @@ -3465,9 +3670,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] @@ -3509,7 +3714,7 @@ dependencies = [ "multer", "percent-encoding", "pin-project", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "scoped-tls", "serde", "serde_json", @@ -3528,48 +3733,59 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3577,28 +3793,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -3654,6 +3873,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + [[package]] name = "windows-sys" version = "0.48.0" @@ -3804,9 +4029,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] @@ -3821,6 +4046,27 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -3836,14 +4082,46 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +dependencies = [ + "zerocopy-derive 0.8.24", ] [[package]] @@ -3854,7 +4132,39 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.100", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", + "synstructure", ] [[package]] @@ -3862,3 +4172,25 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] From 6df4eaf8096fa4e5309b104c89f799ae81f90a38 Mon Sep 17 00:00:00 2001 From: Aman Verma Date: Thu, 27 Mar 2025 20:35:01 -0500 Subject: [PATCH 48/67] Update scraper and ego-tree. This gets rid of 4 dependencies, as verified by the output of yj -tj < Cargo.lock | jq -c '.package | length' Previously it was 432, now it is 428. --- Cargo.lock | 122 +++++++++++++++++++---------------------------------- Cargo.toml | 4 +- 2 files changed, 45 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e36827fb5..e08d8551f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -587,14 +587,14 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.31.2" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be" +checksum = "b7c66d1cd8ed61bf80b38432613a7a2f09401ab8d0501110655f8b341484a3e3" dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.11.3", + "phf", "smallvec", ] @@ -811,9 +811,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "ego-tree" -version = "0.6.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12a0bb14ac04a9fcf170d0bbbef949b44cc492f4452bd20c095636956f653642" +checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8" [[package]] name = "either" @@ -1209,16 +1209,14 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.27.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2", - "quote", - "syn 2.0.100", + "match_token", ] [[package]] @@ -1774,18 +1772,29 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "markup5ever" -version = "0.12.1" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" dependencies = [ "log", - "phf 0.11.3", - "phf_codegen 0.11.3", + "phf", + "phf_codegen", "string_cache", "string_cache_codegen", "tendril", ] +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "memchr" version = "2.7.4" @@ -2106,15 +2115,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - [[package]] name = "phf" version = "0.11.3" @@ -2122,17 +2122,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", - "phf_shared 0.11.3", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_shared", ] [[package]] @@ -2141,18 +2131,8 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand", + "phf_generator", + "phf_shared", ] [[package]] @@ -2161,7 +2141,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.3", + "phf_shared", "rand", ] @@ -2171,29 +2151,20 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", "proc-macro2", "quote", "syn 2.0.100", ] -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher 0.3.11", -] - [[package]] name = "phf_shared" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 1.0.1", + "siphasher", ] [[package]] @@ -2754,15 +2725,14 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scraper" -version = "0.19.1" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761fb705fdf625482d2ed91d3f0559dcfeab2798fe2771c69560a774865d0802" +checksum = "527e65d9d888567588db4c12da1087598d0f6f8b346cc2c5abc91f05fc2dffe2" dependencies = [ - "ahash", "cssparser", "ego-tree", "html5ever", - "once_cell", + "precomputed-hash", "selectors", "tendril", ] @@ -2829,7 +2799,7 @@ dependencies = [ "num-order", "ordered-float", "ouroboros", - "phf 0.11.3", + "phf", "pprof", "predicates-core", "proc-macro2", @@ -2896,9 +2866,9 @@ dependencies = [ [[package]] name = "selectors" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" +checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8" dependencies = [ "bitflags 2.9.0", "cssparser", @@ -2906,8 +2876,8 @@ dependencies = [ "fxhash", "log", "new_debug_unreachable", - "phf 0.10.1", - "phf_codegen 0.10.0", + "phf", + "phf_codegen", "precomputed-hash", "servo_arc", "smallvec", @@ -3010,9 +2980,9 @@ dependencies = [ [[package]] name = "servo_arc" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44" +checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a" dependencies = [ "stable_deref_trait", ] @@ -3091,12 +3061,6 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - [[package]] name = "siphasher" version = "1.0.1" @@ -3191,7 +3155,7 @@ checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe" dependencies = [ "new_debug_unreachable", "parking_lot", - "phf_shared 0.11.3", + "phf_shared", "precomputed-hash", "serde", ] @@ -3202,8 +3166,8 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", "proc-macro2", "quote", ] diff --git a/Cargo.toml b/Cargo.toml index 4d89a0161..7ad54ba14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,10 +72,10 @@ sha3 = "0.10.8" smallvec = "1.13.2" static_assertions = "1.1.0" -scraper = { version = "0.19.1", default-features = false, features = [ +scraper = { version = "0.23.1", default-features = false, features = [ "errors", ] } -ego-tree = "0.6.2" +ego-tree = "0.10.0" serde_json = "1.0.122" From e273b007a2ec79f3dd77625684fcb5b3ed6b3319 Mon Sep 17 00:00:00 2001 From: Aman Verma Date: Thu, 27 Mar 2025 21:02:14 -0500 Subject: [PATCH 49/67] Update ordered-float to 5.0.0. --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e08d8551f..5a0941ce3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2039,9 +2039,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "4.6.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01" dependencies = [ "num-traits", ] diff --git a/Cargo.toml b/Cargo.toml index 7ad54ba14..397566a6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ libc = "0.2.155" libloading = "0.8" scryer-modular-bitfield = "0.11.4" num-order = { version = "1.2.0" } -ordered-float = "4.2.2" +ordered-float = "5.0.0" phf = { version = "0.11", features = ["macros"] } rand = "0.8.5" regex = "1.10.6" From 9dbf592a58b8699d29ea56479575bbc67a0b91ce Mon Sep 17 00:00:00 2001 From: Aman Verma Date: Thu, 27 Mar 2025 21:11:21 -0500 Subject: [PATCH 50/67] Remove unused deps: regex, assert_cmd, predicates-core. regex was introduced in commit 9e85be11fecd00b3d0be8b828eb0dd5cfb57e60a but isn't used anymore. assert_cmd and predicates-core were introduced in e1c681fffead4d63caa0d0b902196224b651f465 for tests but aren't used anymore. Dependency count in Cargo.lock drops from 428 to 421. --- Cargo.lock | 75 ------------------------------------------------------ Cargo.toml | 3 --- 2 files changed, 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a0941ce3..36a9e50e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,22 +128,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "assert_cmd" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" -dependencies = [ - "anstyle", - "bstr", - "doc-comment", - "libc", - "predicates", - "predicates-core", - "predicates-tree", - "wait-timeout", -] - [[package]] name = "autocfg" version = "1.4.0" @@ -254,17 +238,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bstr" -version = "1.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" -dependencies = [ - "memchr", - "regex-automata", - "serde", -] - [[package]] name = "bumpalo" version = "3.17.0" @@ -727,12 +700,6 @@ dependencies = [ "syn 2.0.100", ] -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - [[package]] name = "digest" version = "0.10.7" @@ -782,12 +749,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69dde51e8fef5e12c1d65e0929b03d66e4c0c18282bc30ed2ca050ad6f44dd82" -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "dtoa" version = "1.0.10" @@ -2270,33 +2231,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "predicates" -version = "3.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" -dependencies = [ - "anstyle", - "difflib", - "predicates-core", -] - -[[package]] -name = "predicates-core" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" - -[[package]] -name = "predicates-tree" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" -dependencies = [ - "predicates-core", - "termtree", -] - [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2763,7 +2697,6 @@ name = "scryer-prolog" version = "0.9.4" dependencies = [ "arcu", - "assert_cmd", "base64 0.22.1", "bit-set", "bitvec", @@ -2801,11 +2734,9 @@ dependencies = [ "ouroboros", "phf", "pprof", - "predicates-core", "proc-macro2", "quote", "rand", - "regex", "reqwest", "ring", "ripemd", @@ -3310,12 +3241,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "termtree" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" - [[package]] name = "thiserror" version = "1.0.69" diff --git a/Cargo.toml b/Cargo.toml index 397566a6b..3d8b3714b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,6 @@ num-order = { version = "1.2.0" } ordered-float = "5.0.0" phf = { version = "0.11", features = ["macros"] } rand = "0.8.5" -regex = "1.10.6" ring = { version = "0.17.8", features = ["wasm32_unknown_unknown_js"] } ripemd = "0.1.3" roxmltree = "0.20.0" @@ -120,11 +119,9 @@ ouroboros = "0.18" [dev-dependencies] maplit = "1.0.2" -predicates-core = "1.0.8" serial_test = "3.1.1" [target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies] -assert_cmd = "2.0.15" criterion = "0.5.1" iai-callgrind = "0.12.1" trycmd = "0.15.6" From 464cfd9213229c2b7be9125ef7f86b4e1120a02b Mon Sep 17 00:00:00 2001 From: Aman Verma Date: Fri, 28 Mar 2025 14:05:45 -0500 Subject: [PATCH 51/67] Update lexical. The previous versions of lexical and lexical-core we were using were subject to the following security advisories: - https://rustsec.org/advisories/RUSTSEC-2023-0086 - https://rustsec.org/advisories/RUSTSEC-2023-0055 --- Cargo.lock | 28 ++++++++++++++-------------- Cargo.toml | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 36a9e50e8..519de31bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1568,18 +1568,18 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lexical" -version = "6.1.1" +version = "7.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7aefb36fd43fef7003334742cbf77b243fcd36418a1d1bdd480d613a67968f6" +checksum = "70ed980ff02623721dc334b9105150b66d0e1f246a92ab5a2eca0335d54c48f6" dependencies = [ "lexical-core", ] [[package]] name = "lexical-core" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" +checksum = "b765c31809609075565a70b4b71402281283aeda7ecaf4818ac14a7b2ade8958" dependencies = [ "lexical-parse-float", "lexical-parse-integer", @@ -1590,9 +1590,9 @@ dependencies = [ [[package]] name = "lexical-parse-float" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" +checksum = "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2" dependencies = [ "lexical-parse-integer", "lexical-util", @@ -1601,9 +1601,9 @@ dependencies = [ [[package]] name = "lexical-parse-integer" -version = "0.8.6" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +checksum = "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e" dependencies = [ "lexical-util", "static_assertions", @@ -1611,18 +1611,18 @@ dependencies = [ [[package]] name = "lexical-util" -version = "0.8.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +checksum = "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3" dependencies = [ "static_assertions", ] [[package]] name = "lexical-write-float" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" +checksum = "c5afc668a27f460fb45a81a757b6bf2f43c2d7e30cb5a2dcd3abf294c78d62bd" dependencies = [ "lexical-util", "lexical-write-integer", @@ -1631,9 +1631,9 @@ dependencies = [ [[package]] name = "lexical-write-integer" -version = "0.8.5" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" +checksum = "629ddff1a914a836fb245616a7888b62903aae58fa771e1d83943035efa0f978" dependencies = [ "lexical-util", "static_assertions", diff --git a/Cargo.toml b/Cargo.toml index 3d8b3714b..d2401f8b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ fxhash = "0.2.1" git-version = "0.3.9" indexmap = "2.3.0" lazy_static = "1.5.0" -lexical = "6.1.1" +lexical = "7.0.4" libc = "0.2.155" libloading = "0.8" scryer-modular-bitfield = "0.11.4" From 7b6f4439bb6c421873fab9941cb7eb6a069d581c Mon Sep 17 00:00:00 2001 From: Aman Verma Date: Mon, 31 Mar 2025 04:03:31 -0500 Subject: [PATCH 52/67] Install correct locked version of iai-callgrind-runner in CI. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e4f447d5..ab63d3af4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -153,7 +153,7 @@ jobs: - name: Install CLI tools run: | cargo install cargo2junit --force - version=`yq -ptoml -oy -r '.target.*.dev-dependencies.iai-callgrind|select(.)' Cargo.toml` + version=`yq -ptoml -oj -r '.package[] | select(.name == "iai-callgrind") | .version' Cargo.lock` echo installing iai-callgrind "$version" cargo install iai-callgrind-runner --force --version "$version" sudo apt install valgrind -y From 3d600943309f314f3514c2718279c129f283c787 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 5 Apr 2025 09:47:29 +0200 Subject: [PATCH 53/67] ENHANCED: format specifier ~w more faithfully emulates write/1 In particular, variables now start with "_". Example: ?- format("~w", [X]). %@ _A true. Found thanks to a discussion initiated by @haijinSk: https://github.com/mthom/scryer-prolog/discussions/2863 --- src/lib/format.pl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/format.pl b/src/lib/format.pl index 62a9e93e7..6f5bac923 100644 --- a/src/lib/format.pl +++ b/src/lib/format.pl @@ -90,15 +90,15 @@ format_args_cells(Fs, Args, Cells) :- must_be(chars, Fs), must_be(list, Args), - unique_variable_names(Args, VNs), + unique_variable_names(fabricated, Args, VNs), phrase(cells(Fs,Args,0,[],VNs), Cells). -unique_variable_names(Term, VNs) :- +unique_variable_names(Type, Term, VNs) :- term_variables(Term, Vs), - foldl(var_name, Vs, VNs, 0, _). + foldl(var_name(Type), Vs, VNs, 0, _). -var_name(V, Name=V, Num0, Num) :- - charsio:fabricate_var_name(numbervars, Name, Num0), +var_name(Type, V, Name=V, Num0, Num) :- + charsio:fabricate_var_name(Type, Name, Num0), Num is Num0 + 1. user:goal_expansion(format_(Fs,Args,Cs0,Cs), @@ -574,7 +574,7 @@ flush_output(Stream). portray_clause_(Term) --> - { unique_variable_names(Term, VNs) }, + { unique_variable_names(numbervars, Term, VNs) }, portray_(Term, VNs), ".\n". literal(Lit, VNs) --> From 14c8fc3e345a664d02f61833a761f23fdf58f65f Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 5 Apr 2025 10:01:22 +0200 Subject: [PATCH 54/67] move ~w to least prominent position, point to more suitable specifiers In particular, "~q" is a safe better choice. --- src/lib/format.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/format.pl b/src/lib/format.pl index 6f5bac923..0c6fa2db9 100644 --- a/src/lib/format.pl +++ b/src/lib/format.pl @@ -42,7 +42,6 @@ % FormatString are used literally, except for the following tokens % with special meaning: % -% | `~w` | use the next available argument from Arguments here | % | `~q` | use the next argument here, formatted as by `writeq/1` | % | `~a` | use the next argument here, which must be an atom | % | `~s` | use the next argument here, which must be a string | @@ -72,6 +71,7 @@ % | `~Nn` | N newlines | % | `~i` | ignore the next argument | % | `~~` | the literal ~ | +% | `~w` | format like `write/1` would; consider using `~q`, `~d`, etc. | % % Instead of `~N`, you can write `~*` to use the next argument from % Arguments as the numeric argument. From d2dfb7ccda23782d0692f6aff76157ba876a436d Mon Sep 17 00:00:00 2001 From: constraintAutomaton Date: Tue, 8 Apr 2025 19:12:27 +0200 Subject: [PATCH 55/67] Update README with working WebAssembly usage example. --- README.md | 129 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 0e8438d41..cbd9ff517 100644 --- a/README.md +++ b/README.md @@ -197,57 +197,86 @@ Then a `pkg` directory will be created, containing everything you need for a web ```html - - - Scryer Prolog - Sudoku Solver Example - - - + + + + + + ``` From 1f82dafcb6296089aa4e5fc10ebc3ce809ae7bce Mon Sep 17 00:00:00 2001 From: constraintAutomaton Date: Tue, 8 Apr 2025 19:17:20 +0200 Subject: [PATCH 56/67] Useless console.log in the example deleted. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index cbd9ff517..ead2b06c4 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,6 @@ Then a `pkg` directory will be created, containing everything you need for a web const formatted = grid.map(row => `[${row.join(", ")}]`).join("\n"); formattedSolutions.push(formatted); - console.log(formatted); } // Output results From 8c348a7160ae11fd86bf94ead16b9a26552b1713 Mon Sep 17 00:00:00 2001 From: constraintAutomaton Date: Tue, 8 Apr 2025 19:29:34 +0200 Subject: [PATCH 57/67] better identation of the knowledge base. --- README.md | 60 +++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index ead2b06c4..2cd8be486 100644 --- a/README.md +++ b/README.md @@ -214,36 +214,36 @@ Then a `pkg` directory will be created, containing everything you need for a web // Knowledge base: Sudoku rules and problem definition const kb = ` - :- use_module(library(format)). - :- use_module(library(clpz)). - :- use_module(library(lists)). - - sudoku(Rows) :- - length(Rows, 9), maplist(same_length(Rows), Rows), - append(Rows, Vs), Vs ins 1..9, - maplist(all_distinct, Rows), - transpose(Rows, Columns), - maplist(all_distinct, Columns), - Rows = [A,B,C,D,E,F,G,H,I], - blocks(A, B, C), - blocks(D, E, F), - blocks(G, H, I). - - blocks([], [], []). - blocks([A,B,C|T1], [D,E,F|T2], [G,H,I|T3]) :- - all_distinct([A,B,C,D,E,F,G,H,I]), - blocks(T1, T2, T3). - - problem(1, [[_,_,_,_,_,_,_,_,_], - [_,_,_,_,_,3,_,8,5], - [_,_,1,_,2,_,_,_,_], - [_,_,_,5,_,7,_,_,_], - [_,_,4,_,_,_,1,_,_], - [_,9,_,_,_,_,_,_,_], - [5,_,_,_,_,_,_,7,3], - [_,_,2,_,1,_,_,_,_], - [_,_,_,_,4,_,_,_,9]]). -`; + :- use_module(library(format)). + :- use_module(library(clpz)). + :- use_module(library(lists)). + + sudoku(Rows) :- + length(Rows, 9), maplist(same_length(Rows), Rows), + append(Rows, Vs), Vs ins 1..9, + maplist(all_distinct, Rows), + transpose(Rows, Columns), + maplist(all_distinct, Columns), + Rows = [A,B,C,D,E,F,G,H,I], + blocks(A, B, C), + blocks(D, E, F), + blocks(G, H, I). + + blocks([], [], []). + blocks([A,B,C|T1], [D,E,F|T2], [G,H,I|T3]) :- + all_distinct([A,B,C,D,E,F,G,H,I]), + blocks(T1, T2, T3). + + problem(1, [[_,_,_,_,_,_,_,_,_], + [_,_,_,_,_,3,_,8,5], + [_,_,1,_,2,_,_,_,_], + [_,_,_,5,_,7,_,_,_], + [_,_,4,_,_,_,1,_,_], + [_,9,_,_,_,_,_,_,_], + [5,_,_,_,_,_,_,7,3], + [_,_,2,_,1,_,_,_,_], + [_,_,_,_,4,_,_,_,9]]). + `; machine.consultModuleString("user", kb); From 024c498beeb7d49dd65babbcb498abee03346c19 Mon Sep 17 00:00:00 2001 From: constraintAutomaton Date: Tue, 8 Apr 2025 20:13:02 +0200 Subject: [PATCH 58/67] Fix DOM manipulation to avoid deprecated functions in Sudoku solver example. --- README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2cd8be486..68a4cec88 100644 --- a/README.md +++ b/README.md @@ -266,17 +266,24 @@ Then a `pkg` directory will be created, containing everything you need for a web } // Output results - document.write(`

Sudoku solver returns:

`); + const solutionDiv = document.querySelector("#soduku-solution"); for (const solution of formattedSolutions) { - document.write(`
${solution}
`); + const newPre = document.createElement("pre"); + newPre.textContent = solution; + solutionDiv.appendChild(newPre); } - - + +

Sudoku solver returns:

+
+ +
+ + ``` Then you can serve it with your favorite http server like `python -m http.server` or `npx serve`, and access the page with your browser. From ba9fed3eaec5dc1dd1923dff58c8fc6c624414d0 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 9 Apr 2025 22:23:12 +0200 Subject: [PATCH 59/67] generalize read_from_chars/2 and read_term_from_chars/3 to allow instantiated Term This addresses part of #2871, raised by @haijinSk. Many thanks! --- src/lib/charsio.pl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 2204d80da..536aeac69 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -195,7 +195,7 @@ ; type_error(in_character, C, get_single_char/1) ). -%% read_from_chars(+Chars, -Term). +%% read_from_chars(+Chars, ?Term). % % Given a string made of chars which contains a representation of % a Prolog term, Term is the Prolog term represented. Example: @@ -206,10 +206,10 @@ % ``` read_from_chars(Chars, Term) :- must_be(chars, Chars), - must_be(var, Term), - '$read_from_chars'(Chars, Term). + '$read_from_chars'(Chars, Term0), + Term = Term0. -%% read_term_from_chars(+Chars, -Term, +Options). +%% read_term_from_chars(+Chars, ?Term, +Options). % % Like `read_from_chars`, except the reader is configured according to % `Options` which are those of `read_term`. @@ -220,9 +220,9 @@ % ``` read_term_from_chars(Chars, Term, Options) :- must_be(chars, Chars), - must_be(var, Term), builtins:parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term_from_chars/3), - '$read_term_from_chars'(Chars, Term, Singletons, Variables, VariableNames). + '$read_term_from_chars'(Chars, Term0, Singletons, Variables, VariableNames), + Term = Term0. %% write_term_to_chars(+Term, +Options, -Chars). % From 4e207fc34adfa2e8c85711fcd0142f4b193ff46c Mon Sep 17 00:00:00 2001 From: constraintAutomaton Date: Thu, 10 Apr 2025 19:02:04 +0200 Subject: [PATCH 60/67] Made WasmQueryState an iterable. --- src/wasm.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/wasm.rs b/src/wasm.rs index 10430677e..22234e167 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -78,6 +78,7 @@ impl WasmMachine { ), } .into(); + self_iterable(&query_state); Ok(query_state) } @@ -174,6 +175,21 @@ impl WasmQueryState { } } +/// Sets a [JsValue] as the `Symbol.iterator` property of the [JsValue]. +/// +/// If the [JsValue] conforms to the [JavaScript iterator interface](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_generators), +/// this function will make the [JsValue] iterable. +#[wasm_bindgen(inline_js = " + export function self_iterable(obj) { + obj[Symbol.iterator] = function () { + return this; + }; + } +")] +extern "C" { + fn self_iterable(obj: &JsValue); +} + impl From for JsValue { fn from(leaf_answer: LeafAnswer) -> JsValue { match leaf_answer { From 1b22c89211bd87537a110f9e7542b579e9ed10c5 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Thu, 10 Apr 2025 22:39:35 -0700 Subject: [PATCH 61/67] mark cyclic lists with ellipses at the head (#2635) --- src/heap_print.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 3c22f0aec..f6f0c8f3b 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -874,12 +874,16 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { None => { if self.max_depth == 0 || *max_depth == 0 { // otherwise, contract it to an ellipsis. - push_space_if_amb!(self, "...", { - append_str!(self, "..."); - }); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); } else { debug_assert!(cell.is_ref()); + let h = cell.get_value() as usize; + self.iter.push_stack(IterStackLoc::iterable_loc( + h, + HeapOrStackTag::Heap, + )); + // as usual, the WAM's // optimization of the Lis tag // (conflating the location of @@ -889,15 +893,17 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { // lest we find ourselves in // an infinite loop. if cell.get_tag() == HeapCellValueTag::Lis { - *max_depth -= 1; + if self.iter.heap[cell.get_value() as usize] + .get_forwarding_bit() + { + self.state_stack + .push(TokenOrRedirect::Atom(atom!("..."))); + return None; + } else { + *max_depth -= 1; + } } - let h = cell.get_value() as usize; - self.iter.push_stack(IterStackLoc::iterable_loc( - h, - HeapOrStackTag::Heap, - )); - if let Some(cell) = self.iter.next() { orig_cell = cell; continue; From 3090cb0084cde9b6be125c0011fda77e0e2ed344 Mon Sep 17 00:00:00 2001 From: constraintAutomaton Date: Sat, 12 Apr 2025 07:18:00 +0200 Subject: [PATCH 62/67] Example update to use iterable. --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 68a4cec88..c14512213 100644 --- a/README.md +++ b/README.md @@ -254,8 +254,8 @@ Then a `pkg` directory will be created, containing everything you need for a web const formattedSolutions = []; // Format the answers - for (let solution = answers.next(); !solution.done; solution = answers.next()) { - const rows = solution.value.bindings["Rows"].list; + for (const solution of answers) { + const rows = solution.bindings["Rows"].list; const grid = rows.map(row => row.list.map(cell => cell.integer) @@ -283,7 +283,6 @@ Then a `pkg` directory will be created, containing everything you need for a web - ``` Then you can serve it with your favorite http server like `python -m http.server` or `npx serve`, and access the page with your browser. From b98076b39e0baaefe70ef3932c67ea831b1d6cfc Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 12 Apr 2025 09:05:28 +0200 Subject: [PATCH 63/67] enumerate applications, add AD4M by @coasys --- README.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0e8438d41..d72abb30f 100644 --- a/README.md +++ b/README.md @@ -830,16 +830,19 @@ ideally suited for use in corporations and government agencies that are subject to strict regulations pertaining to interoperability, standards compliance and warranty. -Successful existing applications of Scryer Prolog include the -[DocLog](https://github.com/aarroyoc/doclog) system which -generates Scryer's own documentation and homepage, [reasoning -about business grants](https://arxiv.org/abs/2406.15293) -in the Austrian public administration, and parts of the -[precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog) -package for the analysis of dose-escalation trials in the -safety-critical and highly regulated domain of oncology -trial design, described in [*An Executable Specification of -Oncology Dose-Escalation Protocols with Prolog*](https://arxiv.org/abs/2402.08334). +Successful existing applications of Scryer Prolog include: + +- [DocLog](https://github.com/aarroyoc/doclog) which generates + Scryer's own documentation and homepage +- [Grants4Companies](https://arxiv.org/abs/2406.15293): reasoning + about business grants in the Austrian public administration +- parts of the [precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog) + package for the analysis of dose-escalation trials in the + safety-critical and highly regulated domain of oncology + trial design, described in [*An Executable Specification of + Oncology Dose-Escalation Protocols with Prolog*](https://arxiv.org/abs/2402.08334) +- semantic reasoning and queries in [AD4M](https://github.com/coasys/ad4m), + an agent-centric distributed application meta-ontology. Scryer Prolog is also very well suited for teaching and learning Prolog, and for testing syntactic conformance and hence portability of From 4da0f9009702c049d324e7078cb0f0d0723262ce Mon Sep 17 00:00:00 2001 From: Colin King Date: Fri, 18 Apr 2025 21:40:29 -0500 Subject: [PATCH 64/67] Update `libffi` and `libffi-sys` dependencies to fix aarch64 macOS build Pulls in latest `libffi` and `libffi-sys` dependencies to fix a compilation error on aarch64 macOS builds. --- Cargo.lock | 8 ++++---- Cargo.toml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 519de31bb..4f7b286e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1647,9 +1647,9 @@ checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libffi" -version = "3.2.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" +checksum = "4a9434b6fc77375fb624698d5f8c49d7e80b10d59eb1219afda27d1f824d4074" dependencies = [ "libc", "libffi-sys", @@ -1657,9 +1657,9 @@ dependencies = [ [[package]] name = "libffi-sys" -version = "2.3.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36115160c57e8529781b4183c2bb51fdc1f6d6d1ed345591d84be7703befb3c" +checksum = "ead36a2496acfc8edd6cc32352110e9478ac5b9b5f5b9856ebd3d28019addb84" dependencies = [ "cc", ] diff --git a/Cargo.toml b/Cargo.toml index d2401f8b2..baf6b9608 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,9 +84,9 @@ serde = "1.0.204" crossterm = { version = "0.28.1", optional = true } ctrlc = { version = "3.4.4", optional = true } hostname = { version = "0.4.0", optional = true } -libffi = { version = "3.2.0", optional = true } +libffi = { version = "4.0.0", optional = true } native-tls = { version = "0.2.12", optional = true } -# the version requirement of reqwest is kept low for compatibility with old deno versions +# the version requirement of reqwest is kept low for compatibility with old deno versions # that pin reqwest to 0.11.20 reqwest = { version = "0.11.0", optional = true } rustyline = { version = "14.0.0", optional = true } From 638a765937288e9319877a386f9169eff3d0f74f Mon Sep 17 00:00:00 2001 From: bakaq Date: Tue, 22 Apr 2025 17:10:29 -0300 Subject: [PATCH 65/67] Bump MSRV and update flake.lock --- .github/workflows/ci.yml | 2 +- Cargo.toml | 3 ++- flake.lock | 18 +++++++++--------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab63d3af4..36006cb6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: # FIXME(issue #2138): run wasm tests, failing to run since https://github.com/mthom/scryer-prolog/pull/2137 removed wasm-pack - { os: ubuntu-22.04, rust-version: nightly, target: 'wasm32-unknown-unknown', publish: true, args: '--no-default-features' , test-args: '--no-run --no-default-features', use_swap: true } # Cargo.toml rust-version - - { os: ubuntu-22.04, rust-version: "1.77", target: 'x86_64-unknown-linux-gnu'} + - { os: ubuntu-22.04, rust-version: "1.85", target: 'x86_64-unknown-linux-gnu'} # rust versions - { os: ubuntu-22.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'} - { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu', miri: true, components: "miri"} diff --git a/Cargo.toml b/Cargo.toml index baf6b9608..536e57931 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,8 @@ license = "BSD-3-Clause" keywords = ["prolog", "prolog-interpreter", "prolog-system"] categories = ["command-line-utilities"] build = "build/main.rs" -rust-version = "1.77" +# Remember to check CI +rust-version = "1.85" [lib] crate-type = ["cdylib", "rlib"] diff --git a/flake.lock b/flake.lock index c1bd6923d..e7d0f27a1 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1723541349, - "narHash": "sha256-LrmeqqHdPgAJsVKIJja8jGgRG/CA2y6SGT2TjX5Do68=", + "lastModified": 1744868846, + "narHash": "sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4877ea239f4d02410c3516101faf35a81af0c30e", + "rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1723515680, - "narHash": "sha256-nHdKymsHCVIh0Wdm4MvSgxcTTg34FJIYHRQkQYaSuvk=", + "lastModified": 1745289264, + "narHash": "sha256-7nt+UJ7qaIUe2J7BdnEEph9n2eKEwxUwKS/QIr091uA=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "4ee3d9e9569f70d7bb40f28804d6fe950c81eab3", + "rev": "3b7171858c20d5293360042936058fb0c4cb93a9", "type": "github" }, "original": { From bd4b30564935096c02ca7894b9f8fc1e8adcd7a4 Mon Sep 17 00:00:00 2001 From: bakaq Date: Tue, 22 Apr 2025 17:32:36 -0300 Subject: [PATCH 66/67] Remove Ubuntu 20.04 runner --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36006cb6b..70f8d1a24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,6 @@ jobs: # operating systems - { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc', publish: true } - { os: macos-latest, rust-version: stable, target: 'x86_64-apple-darwin', publish: true } - - { os: ubuntu-20.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true } # architectures - { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true } - { os: ubuntu-22.04, rust-version: stable, target: 'i686-unknown-linux-gnu', publish: true } @@ -95,13 +94,13 @@ jobs: logtalk-test: # if: false # uncomment to disable job - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: [build-test] steps: # Download prebuilt ubuntu binary from build-test job, setup logtalk - uses: actions/download-artifact@v4 with: - name: scryer-prolog_ubuntu-20.04_x86_64-unknown-linux-gnu + name: scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu - run: | chmod +x scryer-prolog echo "$PWD" >> "$GITHUB_PATH" From f496abbbef33e974c596501ef835f9bb07585b22 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 22 Apr 2025 23:28:19 -0700 Subject: [PATCH 67/67] use loader:strip_subst_module/4 in second module check of retract/1 (#2891, #2893) --- src/lib/builtins.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 745cbc487..4b2d4b1c1 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -1210,9 +1210,9 @@ retract(Clause0) :- loader:strip_module(Clause0, Module, Clause), ( Clause \= (_ :- _) -> - loader:strip_module(Clause, Module, Head), + loader:strip_subst_module(Clause, Module, InnerModule, Head), Body = true, - retract_module_clause(Head, Body, Module) + retract_module_clause(Head, Body, InnerModule) ; Clause = (Head :- Body) -> retract_module_clause(Head, Body, Module) ).