Implement a few debugger commands
This commit is contained in:
parent
107e34aca1
commit
fc28d89097
65
Cargo.lock
generated
65
Cargo.lock
generated
|
@ -8,6 +8,11 @@ dependencies = [
|
|||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "argon2rs"
|
||||
version = "0.2.5"
|
||||
|
@ -95,6 +100,16 @@ name = "cfg-if"
|
|||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cgmath"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.0"
|
||||
|
@ -118,6 +133,15 @@ dependencies = [
|
|||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.3"
|
||||
|
@ -178,6 +202,11 @@ name = "fuchsia-cprng"
|
|||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.4.1"
|
||||
|
@ -277,6 +306,18 @@ dependencies = [
|
|||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.1"
|
||||
|
@ -335,6 +376,11 @@ dependencies = [
|
|||
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustboyadvance"
|
||||
version = "0.1.0"
|
||||
|
@ -342,6 +388,7 @@ dependencies = [
|
|||
"bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -544,6 +591,17 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winconsole"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.3.5"
|
||||
|
@ -551,6 +609,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[metadata]
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94"
|
||||
"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392"
|
||||
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
|
||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
|
@ -563,8 +622,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d"
|
||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||
"checksum cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64a4b57c8f4e3a2e9ac07e0f6abc9c24b6fc9e1b54c3478cfb598f3d0023e51c"
|
||||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cdb90b60f2927f8d76139c72dbde7e10c3a2bc47c8594c9c7a66529f2687c03"
|
||||
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
||||
"checksum dirs 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1c4ef5a8b902d393339e2a2c7fe573af92ce7e0ee5a3ff827b4c9ad7e07e4fa1"
|
||||
"checksum dirs-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "937756392ec77d1f2dd9dc3ac9d69867d109a2121479d72c364e42f4cab21e2d"
|
||||
|
@ -572,6 +633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
||||
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
||||
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||
"checksum lexical-core 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d224b31f370c8dfc0dea1932d9e6bd451e65aef6f5f2318846664c04b42a796"
|
||||
"checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319"
|
||||
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
||||
|
@ -585,6 +647,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
|
||||
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
|
||||
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||
|
@ -592,6 +655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828"
|
||||
"checksum rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4f089652ca87f5a82a62935ec6172a534066c7b97be003cc8f702ee9a7a59c92"
|
||||
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum rustyline 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67e12e40e0240de07f0dab4f4dd01bdb15d74dc977026d4ba91666c41c679ade"
|
||||
|
@ -620,4 +684,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef84b96d10db72dd980056666d7f1e7663ce93d82fa33b63e71c966f4cf5032"
|
||||
"checksum yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992"
|
||||
|
|
|
@ -11,4 +11,5 @@ num-traits = "^0.1"
|
|||
bit = "^0.1"
|
||||
clap = {version = "2.33", features = ["color", "yaml"]}
|
||||
rustyline = "5.0.0"
|
||||
nom = "5.0.0"
|
||||
nom = "5.0.0"
|
||||
colored = "1.8"
|
115
src/debugger.rs
115
src/debugger.rs
|
@ -3,9 +3,13 @@ use std::str::FromStr;
|
|||
use rustyline::error::ReadlineError;
|
||||
use rustyline::Editor;
|
||||
|
||||
use colored::*;
|
||||
|
||||
use nom;
|
||||
use nom::bytes;
|
||||
use nom::IResult;
|
||||
use nom::bytes::complete::{tag, take_till1, take_till, take_while_m_n, take_while1};
|
||||
use nom::combinator::map_res;
|
||||
|
||||
use super::arm7tdmi::arm;
|
||||
use super::arm7tdmi::cpu;
|
||||
|
@ -15,16 +19,19 @@ use super::sysbus::SysBus;
|
|||
pub struct Debugger {
|
||||
cpu: cpu::Core,
|
||||
sysbus: SysBus,
|
||||
breakpoints: Vec<u32>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DebuggerError {
|
||||
ParsingError,
|
||||
ParsingError(String),
|
||||
InvalidCommand(String)
|
||||
}
|
||||
|
||||
impl From<nom::Err<(&str, nom::error::ErrorKind)>> for DebuggerError {
|
||||
fn from(e: nom::Err<(&str, nom::error::ErrorKind)>) -> DebuggerError {
|
||||
DebuggerError::ParsingError
|
||||
DebuggerError::ParsingError("parsing of command failed".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,23 +41,63 @@ type DebuggerResult<T> = Result<T, DebuggerError>;
|
|||
enum DebuggerCommand {
|
||||
SingleStep,
|
||||
Continue,
|
||||
Disass { addr: u32, num_opcodes: u32 },
|
||||
Stop,
|
||||
Disass { addr: u32, num_opcodes: usize },
|
||||
AddBreakpoint(u32),
|
||||
ListBreakpoints,
|
||||
Quit,
|
||||
Nop,
|
||||
}
|
||||
use DebuggerCommand::*;
|
||||
|
||||
fn parse_debugger_command(input: &str) -> IResult<&str, DebuggerCommand> {
|
||||
let (input, command_name) = bytes::complete::take_while1(|c: char| c.is_alphanumeric())(input)?;
|
||||
println!("parsed command: {}", command_name);
|
||||
|
||||
unimplemented!()
|
||||
fn from_hex(input: &str) -> Result<u32, std::num::ParseIntError> {
|
||||
u32::from_str_radix(input, 16)
|
||||
}
|
||||
|
||||
impl FromStr for DebuggerCommand {
|
||||
type Err = DebuggerError;
|
||||
fn from_dec(input: &str) -> Result<u32, std::num::ParseIntError> {
|
||||
u32::from_str_radix(input, 10)
|
||||
}
|
||||
|
||||
fn from_str(text: &str) -> Result<Self, Self::Err> {
|
||||
|
||||
fn whitespace(input: &str) -> IResult<&str, ()> {
|
||||
let (input, _) = take_while1(char::is_whitespace)(input)?;
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
fn parse_hex_num(input: &str) -> IResult<&str, u32> {
|
||||
let (input, _) = tag("0x")(input)?;
|
||||
map_res(take_while_m_n(1, 8, |c: char| c.is_digit(16)), from_hex)(input)
|
||||
}
|
||||
|
||||
fn parse_num(input: &str) -> IResult<&str, u32> {
|
||||
map_res(take_while1(|c: char| c.is_digit(10)), from_dec)(input)
|
||||
}
|
||||
|
||||
fn parse_word(input: &str) -> IResult<&str, &str> {
|
||||
take_till(char::is_whitespace)(input)
|
||||
}
|
||||
|
||||
fn parse_debugger_command(input: &str) -> DebuggerResult<DebuggerCommand> {
|
||||
// TODO this code is shit!
|
||||
let (input, command_name) = parse_word(input)?;
|
||||
match command_name {
|
||||
"s" | "step" => Ok(SingleStep),
|
||||
"c" | "continue" => Ok(Continue),
|
||||
"d" | "disass" => {
|
||||
let (input, _) = whitespace(input).map_err(|_| DebuggerError::ParsingError("argument missing".to_string()))?;
|
||||
let (input, addr) = parse_hex_num(input)?;
|
||||
let (input, _) = whitespace(input).map_err(|_| DebuggerError::ParsingError("argument missing".to_string()))?;
|
||||
let (_, num_opcodes) = parse_num(input)?;
|
||||
let num_opcodes = num_opcodes as usize;
|
||||
Ok(Disass{ addr, num_opcodes })
|
||||
}
|
||||
"b" | "break" => {
|
||||
let (input, _) = whitespace(input).map_err(|_| DebuggerError::ParsingError("argument missing".to_string()))?;
|
||||
let (_, addr) = parse_hex_num(input)?;
|
||||
Ok(AddBreakpoint(addr))
|
||||
}
|
||||
"bl" => Ok(ListBreakpoints),
|
||||
"q" | "quit" => Ok(Quit),
|
||||
"" => Ok(Nop),
|
||||
_ => Err(DebuggerError::InvalidCommand(command_name.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,16 +106,52 @@ impl Debugger {
|
|||
Debugger {
|
||||
cpu: cpu,
|
||||
sysbus: sysbus,
|
||||
breakpoints: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn repl(&self) -> DebuggerResult<()> {
|
||||
pub fn repl(&mut self) -> DebuggerResult<()> {
|
||||
let mut rl = Editor::<()>::new();
|
||||
loop {
|
||||
let readline = rl.readline("(rustboyadvance-dbg) >> ");
|
||||
let readline = rl.readline(&format!("({}) >> ", "rustboyadvance-dbg".cyan()));
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
let command = parse_debugger_command(&line)?;
|
||||
let command = parse_debugger_command(&line);
|
||||
match command {
|
||||
Ok(Nop) => (),
|
||||
Ok(SingleStep) => {
|
||||
self.cpu.step(&mut self.sysbus).unwrap()
|
||||
},
|
||||
Ok(Quit) => {
|
||||
print!("Quitting!");
|
||||
break
|
||||
},
|
||||
Ok(AddBreakpoint(addr)) => {
|
||||
if !self.breakpoints.contains(&addr) {
|
||||
let new_index = self.breakpoints.len();
|
||||
self.breakpoints.push(addr);
|
||||
println!("added breakpoint [{}] 0x{:08x}", new_index, addr);
|
||||
} else {
|
||||
println!("breakpoint already exists!")
|
||||
}
|
||||
}
|
||||
Ok(ListBreakpoints) => {
|
||||
println!("breakpoint list:");
|
||||
for (i, b) in self.breakpoints.iter().enumerate() {
|
||||
println!("[{}] 0x{:08x}", i, b)
|
||||
}
|
||||
}
|
||||
Err(DebuggerError::InvalidCommand(command)) => {
|
||||
println!("invalid command: {}", command)
|
||||
}
|
||||
Err(DebuggerError::ParsingError(msg)) => {
|
||||
println!("Parsing error: {:?}", msg)
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
Ok(command) => println!("got command: {:?}", command),
|
||||
}
|
||||
}
|
||||
Err(ReadlineError::Interrupted) => {
|
||||
println!("CTRL-C");
|
||||
|
|
|
@ -19,9 +19,10 @@ use clap::{App, ArgMatches};
|
|||
|
||||
extern crate rustyline;
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
extern crate colored; // not needed in Rust 2018
|
||||
|
||||
pub mod sysbus;
|
||||
use sysbus::SysBus;
|
||||
|
||||
|
@ -105,9 +106,9 @@ fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
|
|||
println!("Loading BIOS: {}", gba_bios_path);
|
||||
let bios_bin = read_bin_file(gba_bios_path)?;
|
||||
|
||||
let sysbus = SysBus::new(bios_bin);
|
||||
let core = cpu::Core::new();
|
||||
let debugger = Debugger::new(core, sysbus);
|
||||
let mut sysbus = SysBus::new(bios_bin);
|
||||
let mut core = cpu::Core::new();
|
||||
let mut debugger = Debugger::new(core, sysbus);
|
||||
|
||||
println!("starting debugger...");
|
||||
debugger.repl()?;
|
||||
|
|
Reference in a new issue