diff --git a/bootstraptest/test_method.rb b/bootstraptest/test_method.rb index 997675200e4d73..78aab734854fee 100644 --- a/bootstraptest/test_method.rb +++ b/bootstraptest/test_method.rb @@ -1427,3 +1427,10 @@ def test(*, kw: false) test RUBY + +assert_equal '[1, 2, 3]', %q{ + def target(*args) = args + def x = [1] + def forwarder(...) = target(*x, 2, ...) + forwarder(3).inspect +}, '[Bug #21832] post-splat args before forwarding' diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index e58d88025bf5e9..e8ba019ae14ae8 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -91,6 +91,8 @@ RB_THREAD_LOCAL_SPECIFIER VALUE marking_parent_object; #include +static inline VALUE rb_mmtk_call_object_closure(VALUE obj, bool pin); + static void rb_mmtk_init_gc_worker_thread(MMTk_VMWorkerThread gc_thread_tls) { @@ -361,7 +363,7 @@ make_final_job(struct objspace *objspace, VALUE obj, VALUE table) } static int -rb_mmtk_update_finalizer_table_i(st_data_t key, st_data_t value, st_data_t data) +rb_mmtk_update_finalizer_table_i(st_data_t key, st_data_t value, st_data_t data, int error) { RUBY_ASSERT(RB_FL_TEST(key, RUBY_FL_FINALIZE)); RUBY_ASSERT(mmtk_is_reachable((MMTk_ObjectReference)value)); @@ -369,7 +371,14 @@ rb_mmtk_update_finalizer_table_i(st_data_t key, st_data_t value, st_data_t data) struct objspace *objspace = (struct objspace *)data; - if (!mmtk_is_reachable((MMTk_ObjectReference)key)) { + if (mmtk_is_reachable((MMTk_ObjectReference)key)) { + VALUE new_key_location = rb_mmtk_call_object_closure((VALUE)key, false); + + if (new_key_location != key) { + return ST_REPLACE; + } + } + else { make_final_job(objspace, (VALUE)key, (VALUE)value); rb_postponed_job_trigger(objspace->finalizer_postponed_job); @@ -380,13 +389,25 @@ rb_mmtk_update_finalizer_table_i(st_data_t key, st_data_t value, st_data_t data) return ST_CONTINUE; } +static int +rb_mmtk_update_finalizer_table_replace_i(st_data_t *key, st_data_t *value, st_data_t data, int existing) +{ + *key = rb_mmtk_call_object_closure((VALUE)*key, false); + + return ST_CONTINUE; +} + static void rb_mmtk_update_finalizer_table(void) { struct objspace *objspace = rb_gc_get_objspace(); - // TODO: replace with st_foreach_with_replace when GC is moving - st_foreach(objspace->finalizer_table, rb_mmtk_update_finalizer_table_i, (st_data_t)objspace); + st_foreach_with_replace( + objspace->finalizer_table, + rb_mmtk_update_finalizer_table_i, + rb_mmtk_update_finalizer_table_replace_i, + (st_data_t)objspace + ); } static int diff --git a/misc/.vscode/settings.json b/misc/.vscode/settings.json index c11838155efaa9..a2e4e1ec6945af 100644 --- a/misc/.vscode/settings.json +++ b/misc/.vscode/settings.json @@ -3,4 +3,8 @@ "disasm", ], "rust-analyzer.cfg.setTest": false, + // rust-analyzer bundled in the VSCode extension may only support Rust newer than 1.85.0. + // To avoid warnings, install rust-analyzer with `rustup component add rust-analyzer` and + // use `~/.cargo/bin/rust-analyzer` with the following config. + "rust-analyzer.server.path": "rust-analyzer", } diff --git a/prism_compile.c b/prism_compile.c index 36a0b6ce50a77b..788968113090cd 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -1833,6 +1833,10 @@ pm_setup_args_core(const pm_arguments_node_t *arguments_node, const pm_node_t *b // foo(*a, b, c: :d) // foo(*a, b, **c) // + // If the next node is a forwarding argument: + // + // foo(*a, b, ...) + // // If the next node is NULL (we have hit the end): // // foo(*a, b) @@ -1855,6 +1859,10 @@ pm_setup_args_core(const pm_arguments_node_t *arguments_node, const pm_node_t *b PUSH_INSN(ret, location, concatarray); break; } + case PM_FORWARDING_ARGUMENTS_NODE: { + PUSH_INSN1(ret, location, pushtoarray, INT2FIX(post_splat_counter)); + break; + } default: break; } diff --git a/zjit/src/backend/lir.rs b/zjit/src/backend/lir.rs index f4ed3d7cb78ce6..d8d82a09cabb00 100644 --- a/zjit/src/backend/lir.rs +++ b/zjit/src/backend/lir.rs @@ -1901,18 +1901,19 @@ impl Assembler } } -const BOLD_BEGIN: &str = "\x1b[1m"; -const BOLD_END: &str = "\x1b[22m"; - /// Return a result of fmt::Display for Assembler without escape sequence pub fn lir_string(asm: &Assembler) -> String { - format!("{asm}").replace(BOLD_BEGIN, "").replace(BOLD_END, "") + use crate::ttycolors::TTY_TERMINAL_COLOR; + format!("{asm}").replace(TTY_TERMINAL_COLOR.bold_begin, "").replace(TTY_TERMINAL_COLOR.bold_end, "") } impl fmt::Display for Assembler { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Count the number of duplicated label names to disambiguate them if needed let mut label_counts: HashMap<&String, usize> = HashMap::new(); + let colors = crate::ttycolors::get_colors(); + let bold_begin = colors.bold_begin; + let bold_end = colors.bold_end; for label_name in self.label_names.iter() { let counter = label_counts.entry(label_name).or_insert(0); *counter += 1; @@ -1932,7 +1933,7 @@ impl fmt::Display for Assembler { for insn in self.insns.iter() { match insn { Insn::Comment(comment) => { - writeln!(f, " {BOLD_BEGIN}# {comment}{BOLD_END}")?; + writeln!(f, " {bold_begin}# {comment}{bold_end}")?; } Insn::Label(target) => { let &Target::Label(Label(label_idx)) = target else { @@ -2506,6 +2507,9 @@ impl AssemblerPanicHook { /// Dump Assembler, highlighting the insn_idx line fn dump_asm(asm: &Assembler, insn_idx: usize) { + let colors = crate::ttycolors::get_colors(); + let bold_begin = colors.bold_begin; + let bold_end = colors.bold_end; let lir_string = lir_string(asm); let lines: Vec<&str> = lir_string.split('\n').collect(); @@ -2520,7 +2524,7 @@ impl AssemblerPanicHook { eprintln!("Failed to compile LIR at insn_idx={insn_idx}:"); for (idx, line) in lines.iter().enumerate().filter(|(idx, _)| (min_idx..=max_idx).contains(idx)) { if idx == insn_idx && line.starts_with(" ") { - eprintln!("{BOLD_BEGIN}=>{}{BOLD_END}", &line[" ".len()..]); + eprintln!("{bold_begin}=>{}{bold_end}", &line[" ".len()..]); } else { eprintln!("{line}"); } diff --git a/zjit/src/disasm.rs b/zjit/src/disasm.rs index 70a56d780d4edc..d4f65915946e0e 100644 --- a/zjit/src/disasm.rs +++ b/zjit/src/disasm.rs @@ -1,8 +1,5 @@ use crate::asm::CodeBlock; -pub const BOLD_BEGIN: &str = "\x1b[1m"; -pub const BOLD_END: &str = "\x1b[22m"; - pub fn disasm_addr_range(cb: &CodeBlock, start_addr: usize, end_addr: usize) -> String { use std::fmt::Write; @@ -36,12 +33,16 @@ pub fn disasm_addr_range(cb: &CodeBlock, start_addr: usize, end_addr: usize) -> let start_addr = 0; let insns = cs.disasm_all(code_slice, start_addr as u64).unwrap(); + let colors = crate::ttycolors::get_colors(); + let bold_begin = colors.bold_begin; + let bold_end = colors.bold_end; + // For each instruction in this block for insn in insns.as_ref() { // Comments for this block if let Some(comment_list) = cb.comments_at(insn.address() as usize) { for comment in comment_list { - writeln!(&mut out, " {BOLD_BEGIN}# {comment}{BOLD_END}").unwrap(); + writeln!(&mut out, " {bold_begin}# {comment}{bold_end}").unwrap(); } } writeln!(&mut out, " {}", format!("{insn}").trim()).unwrap(); diff --git a/zjit/src/lib.rs b/zjit/src/lib.rs index 88e7af02b75013..e7420c16dde419 100644 --- a/zjit/src/lib.rs +++ b/zjit/src/lib.rs @@ -30,3 +30,4 @@ mod bitset; mod gc; mod payload; mod json; +mod ttycolors; diff --git a/zjit/src/ttycolors.rs b/zjit/src/ttycolors.rs new file mode 100644 index 00000000000000..f325772431048b --- /dev/null +++ b/zjit/src/ttycolors.rs @@ -0,0 +1,31 @@ +use std::io::IsTerminal; + +pub fn stdout_supports_colors() -> bool { + std::io::stdout().is_terminal() +} + +#[cfg_attr(not(feature = "disasm"), allow(dead_code))] +#[derive(Copy, Clone, Debug)] +pub struct TerminalColor { + pub bold_begin: &'static str, + pub bold_end: &'static str, +} + +pub static TTY_TERMINAL_COLOR: TerminalColor = TerminalColor { + bold_begin: "\x1b[1m", + bold_end: "\x1b[22m", +}; + +pub static NON_TTY_TERMINAL_COLOR: TerminalColor = TerminalColor { + bold_begin: "", + bold_end: "", +}; + +/// Terminal escape codes for colors, font weight, etc. Only enabled if stdout is a TTY. +pub fn get_colors() -> &'static TerminalColor { + if stdout_supports_colors() { + &TTY_TERMINAL_COLOR + } else { + &NON_TTY_TERMINAL_COLOR + } +}